File size: 2,548 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
 * The inline/reconnect MCP config validation for the v2 client's
 * session-level MCP methods (`sdk-rpc-client-v2.ts`), which ride the
 * session's own connection manager and have no engine service behind them.
 * The validators keep v1's exact error text for the session RPC paths, and
 * validation keeps using the shared `McpServerConfigSchema`.
 *
 * Persisting session-level adds to the user-level `mcp.json` no longer
 * happens here: `addSessionMcpServer`'s `persist: true` path writes through
 * the engine's App-scope `IMcpConfigStore` — the single writer of that file —
 * and the unified management plane (CRUD facade, connection probe,
 * inspection, OAuth orchestration) delegates to the engine's
 * `IMcpManagementService`.
 */
import {
  McpServerConfigSchema,
  type McpServerConfig,
} from '@moonshot-ai/agent-core-v2/mcpCore/config-schema';

import { ErrorCodes, KimiError } from '#/errors';
import type { McpServerConfig as GlobalMcpServerConfig } from '#/mcp';

/** Byte-identical port of v1's `mcpConfigWithoutName`. */
export function mcpConfigWithoutName(server: GlobalMcpServerConfig): McpServerConfig {
  const { name: _name, ...config } = server;
  return config;
}

/**
 * Byte-identical port of v1's inline-server validation (the `server` branch
 * of `resolveMcpTestTarget`, shared with `addSessionMcpServer`): the schema
 * strips unknown keys such as a stray `name`, so the name is re-attached.
 */
export function parseInlineMcpServer(server: GlobalMcpServerConfig): GlobalMcpServerConfig {
  const parsed = McpServerConfigSchema.safeParse(server);
  if (!parsed.success) {
    throw new KimiError(
      ErrorCodes.CONFIG_INVALID,
      `Invalid MCP server "${server.name}": ${parsed.error.message}`,
    );
  }
  return { name: server.name, ...parsed.data };
}

/**
 * Byte-identical port of v1's reconnect-config validation
 * (`SessionAPIImpl.reconnectMcpServer`): same schema strip, nameless result.
 */
export function parseReconnectMcpServerConfig(
  name: string,
  config: GlobalMcpServerConfig,
): McpServerConfig {
  const parsed = McpServerConfigSchema.safeParse(config);
  if (!parsed.success) {
    throw new KimiError(
      ErrorCodes.CONFIG_INVALID,
      `Invalid MCP server config for "${name}": ${parsed.error.message}`,
    );
  }
  return parsed.data;
}

export function normalizeServerName(name: string): string {
  const normalized = name.trim();
  if (normalized.length > 0) return normalized;
  throw new KimiError(ErrorCodes.REQUEST_INVALID, 'MCP server name cannot be empty');
}