@obov/mext 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/build-cli.d.ts +2 -1
  2. package/dist/build-cli.js +24 -14
  3. package/dist/build-cli.js.map +1 -1
  4. package/dist/build.d.ts +23 -3
  5. package/dist/build.js +58 -5
  6. package/dist/build.js.map +1 -1
  7. package/dist/cli/build.js +5 -0
  8. package/dist/cli/build.js.map +1 -1
  9. package/dist/cli/dev-server.js +2 -1
  10. package/dist/cli/dev-server.js.map +1 -1
  11. package/dist/cli/dev.js +2 -1
  12. package/dist/cli/dev.js.map +1 -1
  13. package/dist/cli/output.d.ts +45 -1
  14. package/dist/cli/output.js +60 -3
  15. package/dist/cli/output.js.map +1 -1
  16. package/dist/cli/run.d.ts +26 -0
  17. package/dist/cli/run.js +41 -20
  18. package/dist/cli/run.js.map +1 -1
  19. package/dist/client/main.d.ts +8 -2
  20. package/dist/client/main.js +4 -3
  21. package/dist/client/main.js.map +1 -1
  22. package/dist/client/runner.d.ts +3 -0
  23. package/dist/client/runner.js +13 -12
  24. package/dist/client/runner.js.map +1 -1
  25. package/dist/config.d.ts +67 -2
  26. package/dist/config.js +89 -4
  27. package/dist/config.js.map +1 -1
  28. package/dist/errors.d.ts +19 -1
  29. package/dist/errors.js +47 -0
  30. package/dist/errors.js.map +1 -1
  31. package/dist/index.d.ts +3 -2
  32. package/dist/index.js +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/instrumentation.d.ts +2 -2
  35. package/dist/instrumentation.js +5 -5
  36. package/dist/instrumentation.js.map +1 -1
  37. package/dist/local-bin.d.ts +20 -0
  38. package/dist/local-bin.js +102 -0
  39. package/dist/local-bin.js.map +1 -0
  40. package/dist/mcp/dispatch.d.ts +1 -1
  41. package/dist/mcp/dispatch.js +3 -3
  42. package/dist/mcp/dispatch.js.map +1 -1
  43. package/dist/mcp/http.d.ts +4 -0
  44. package/dist/mcp/http.js +2 -0
  45. package/dist/mcp/http.js.map +1 -1
  46. package/dist/mcp/install.d.ts +2 -2
  47. package/dist/mcp/install.js +18 -0
  48. package/dist/mcp/install.js.map +1 -1
  49. package/dist/mcp/server.d.ts +5 -0
  50. package/dist/mcp/server.js +7 -1
  51. package/dist/mcp/server.js.map +1 -1
  52. package/dist/mcp/stdio.d.ts +18 -0
  53. package/dist/mcp/stdio.js +155 -12
  54. package/dist/mcp/stdio.js.map +1 -1
  55. package/dist/start.d.ts +2 -1
  56. package/dist/start.js +13 -1
  57. package/dist/start.js.map +1 -1
  58. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/mcp/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAerE;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,GAAsB,EAAE;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAA;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,YAAY,IAAI,YAAY,GAAG,QAAQ,EAAE,CAAA;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAA;IAChG,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAC1B,MAAM,KAAK,GAAG;QACZ,iBAAiB,IAAI,oCAAoC;QACzD,EAAE;QACF,gCAAgC;QAChC,qCAAqC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE;KAC5D,CAAA;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,wBAAwB,EACxB,qCAAqC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,2CAA2C,CACrG,CAAA;IACH,CAAC;IACD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,6CAA6C,EAAE,oBAAoB,CAAC,OAAO,KAAK,EAAE,CAAC,CAAA;QAClG,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,oCAAoC,EAAE,oBAAoB,CAAC,4BAA4B,CAAC,CAAA;IAC7I,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC7D,IAAI,OAAO,CAAC,IAAI;QAAE,MAAM,CAAC,OAAO,GAAG,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACtE,KAAK,CAAC,IAAI,CACR,EAAE,EACF,kCAAkC,EAClC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;SAC3D,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EACvB,EAAE,EACF,uFAAuF,CACxF,CAAA;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAA;AAC/F,CAAC","sourcesContent":["import { DEFAULT_HOST, DEFAULT_PORT, MCP_PATH } from \"./constants.ts\"\n\nexport interface McpInstallOptions {\n /** MCP 클라이언트에 등록할 서버 이름. 기본 \"mext\" */\n name?: string\n /** Streamable HTTP 엔드포인트. 기본 http://127.0.0.1:3100/mcp */\n url?: string\n /**\n * stdio 실행 명령. 기본 \"npx mext dev --stdio\" (운영 = \"npx mext start --stdio\"). false = stdio 안내 생략 (고객 CLI 는 원격만)\n */\n stdioCommand?: string | false\n /** 토큰이 필요한 서버면 Authorization 헤더 예시 포함 */\n auth?: boolean\n}\n\n/**\n * `mext mcp install` · `myapp mcp install` 의 안내 텍스트.\n * MCP 클라이언트 등록 명령만 출력한다. 클라이언트 설정 파일은 쓰지 않는다.\n */\nexport function mcpInstallText(options: McpInstallOptions = {}): string {\n const name = options.name ?? \"mext\"\n const url = options.url ?? `http://${DEFAULT_HOST}:${DEFAULT_PORT}${MCP_PATH}`\n const stdio = options.stdioCommand === undefined ? \"npx mext dev --stdio\" : options.stdioCommand\n const n = shellQuote(name)\n const lines = [\n `Register the \"${name}\" MCP server with your MCP client.`,\n \"\",\n \"Claude Code (Streamable HTTP):\",\n ` claude mcp add --transport http ${n} ${shellQuote(url)}`,\n ]\n if (options.auth) {\n lines.push(\n \" # with an API token:\",\n ` claude mcp add --transport http ${n} ${shellQuote(url)} --header \"Authorization: Bearer <token>\"`,\n )\n }\n if (stdio !== false) {\n lines.push(\"\", \"Claude Code (stdio, runs commands locally):\", ` claude mcp add ${n} -- ${stdio}`)\n if (options.stdioCommand === undefined) lines.push(\" # production (after mext build):\", ` claude mcp add ${n} -- npx mext start --stdio`)\n }\n const config: Record<string, unknown> = { type: \"http\", url }\n if (options.auth) config.headers = { Authorization: \"Bearer <token>\" }\n lines.push(\n \"\",\n \"Other MCP clients (JSON config):\",\n ...JSON.stringify({ mcpServers: { [name]: config } }, null, 2)\n .split(\"\\n\")\n .map((l) => ` ${l}`),\n \"\",\n \"This command only prints instructions; it does not modify client configuration files.\",\n )\n return lines.join(\"\\n\")\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", `'\\\\''`)}'`\n}\n"]}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/mcp/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAerE;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,GAAsB,EAAE;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAA;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,YAAY,IAAI,YAAY,GAAG,QAAQ,EAAE,CAAA;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAA;IAChG,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,IAAI,KAAK,KAAK,KAAK;YAAE,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAA;QACrG,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3C,OAAO;YACL,iBAAiB,IAAI,oCAAoC;YACzD,EAAE;YACF,6CAA6C;YAC7C,oBAAoB,CAAC,OAAO,KAAK,EAAE;YACnC,EAAE;YACF,kCAAkC;YAClC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;iBACtE,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,EAAE;YACF,uFAAuF;SACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACd,CAAC;IACD,MAAM,KAAK,GAAG;QACZ,iBAAiB,IAAI,oCAAoC;QACzD,EAAE;QACF,gCAAgC;QAChC,qCAAqC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE;KAC5D,CAAA;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,wBAAwB,EACxB,qCAAqC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,2CAA2C,CACrG,CAAA;IACH,CAAC;IACD,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,6CAA6C,EAAE,oBAAoB,CAAC,OAAO,KAAK,EAAE,CAAC,CAAA;QAClG,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,oCAAoC,EAAE,oBAAoB,CAAC,4BAA4B,CAAC,CAAA;IAC7I,CAAC;IACD,MAAM,MAAM,GAA4B,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;IAC7D,IAAI,OAAO,CAAC,IAAI;QAAE,MAAM,CAAC,OAAO,GAAG,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACtE,KAAK,CAAC,IAAI,CACR,EAAE,EACF,kCAAkC,EAClC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;SAC3D,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EACvB,EAAE,EACF,uFAAuF,CACxF,CAAA;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAA;AAC/F,CAAC","sourcesContent":["import { DEFAULT_HOST, DEFAULT_PORT, MCP_PATH } from \"./constants.ts\"\n\nexport interface McpInstallOptions {\n /** MCP 클라이언트에 등록할 서버 이름. 기본 \"mext\" */\n name?: string\n /** Streamable HTTP 엔드포인트. 기본 http://127.0.0.1:3100/mcp. false = HTTP 안내 생략 (로컬 bin 은 stdio 만) */\n url?: string | false\n /**\n * stdio 실행 명령. 기본 \"npx mext dev --stdio\" (운영 = \"npx mext start --stdio\"). false = stdio 안내 생략 (고객 CLI 는 원격만)\n */\n stdioCommand?: string | false\n /** 토큰이 필요한 서버면 Authorization 헤더 예시 포함 */\n auth?: boolean\n}\n\n/**\n * `mext mcp install` · `myapp mcp install` 의 안내 텍스트.\n * MCP 클라이언트 등록 명령만 출력한다. 클라이언트 설정 파일은 쓰지 않는다.\n */\nexport function mcpInstallText(options: McpInstallOptions = {}): string {\n const name = options.name ?? \"mext\"\n const url = options.url ?? `http://${DEFAULT_HOST}:${DEFAULT_PORT}${MCP_PATH}`\n const stdio = options.stdioCommand === undefined ? \"npx mext dev --stdio\" : options.stdioCommand\n const n = shellQuote(name)\n if (url === false) {\n if (stdio === false) throw new TypeError(\"mcpInstallText: url and stdioCommand cannot both be false\")\n const [command, ...args] = stdio.split(\" \")\n return [\n `Register the \"${name}\" MCP server with your MCP client.`,\n \"\",\n \"Claude Code (stdio, runs commands locally):\",\n ` claude mcp add ${n} -- ${stdio}`,\n \"\",\n \"Other MCP clients (JSON config):\",\n ...JSON.stringify({ mcpServers: { [name]: { command, args } } }, null, 2)\n .split(\"\\n\")\n .map((l) => ` ${l}`),\n \"\",\n \"This command only prints instructions; it does not modify client configuration files.\",\n ].join(\"\\n\")\n }\n const lines = [\n `Register the \"${name}\" MCP server with your MCP client.`,\n \"\",\n \"Claude Code (Streamable HTTP):\",\n ` claude mcp add --transport http ${n} ${shellQuote(url)}`,\n ]\n if (options.auth) {\n lines.push(\n \" # with an API token:\",\n ` claude mcp add --transport http ${n} ${shellQuote(url)} --header \"Authorization: Bearer <token>\"`,\n )\n }\n if (stdio !== false) {\n lines.push(\"\", \"Claude Code (stdio, runs commands locally):\", ` claude mcp add ${n} -- ${stdio}`)\n if (options.stdioCommand === undefined) lines.push(\" # production (after mext build):\", ` claude mcp add ${n} -- npx mext start --stdio`)\n }\n const config: Record<string, unknown> = { type: \"http\", url }\n if (options.auth) config.headers = { Authorization: \"Bearer <token>\" }\n lines.push(\n \"\",\n \"Other MCP clients (JSON config):\",\n ...JSON.stringify({ mcpServers: { [name]: config } }, null, 2)\n .split(\"\\n\")\n .map((l) => ` ${l}`),\n \"\",\n \"This command only prints instructions; it does not modify client configuration files.\",\n )\n return lines.join(\"\\n\")\n}\n\nfunction shellQuote(value: string): string {\n return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : `'${value.replaceAll(\"'\", `'\\\\''`)}'`\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { type AuthInfo, type McpRequestContext, type McpServerFactory } from "@modelcontextprotocol/server";
2
2
  import type { MextApp } from "../command.ts";
3
+ import type { McpCapabilities } from "../config.ts";
3
4
  import type { Instrumentation, Transport } from "../instrumentation.ts";
4
5
  import { type Principal } from "../principal.ts";
5
6
  import type { Registry } from "../registry.ts";
@@ -22,6 +23,10 @@ export interface McpCoreOptions {
22
23
  tools?: ToolExposure;
23
24
  /** serverInfo.name. 기본 "mext" */
24
25
  name?: string;
26
+ /** initialize · server/discover 결과 instructions */
27
+ instructions?: string;
28
+ /** 서버 capabilities 에 병합 (experimental). tools capability 는 mext 가 고정 */
29
+ capabilities?: McpCapabilities;
25
30
  /** serverInfo.version. 기본 mext 버전 */
26
31
  version?: string;
27
32
  /**
@@ -22,10 +22,16 @@ export function createMcpServerFactory(options) {
22
22
  const serverInfo = { name: options.name ?? "mext", version: options.version ?? VERSION };
23
23
  const signer = createRequestStateSigner(options.requestStateKey === undefined ? {} : { key: options.requestStateKey });
24
24
  const meta = { [MANIFEST_HASH_META_KEY]: options.manifestHash ?? registryManifestHash(options.registry) };
25
+ const experimental = options.capabilities?.experimental;
26
+ const capabilities = {
27
+ ...(experimental && Object.keys(experimental).length > 0 ? { experimental: { ...experimental } } : {}),
28
+ tools: { listChanged: false },
29
+ };
25
30
  return (requestContext) => {
26
31
  const principal = resolvePrincipal(options.principal, requestContext);
27
32
  const server = new McpServer(serverInfo, {
28
- capabilities: { tools: { listChanged: false } },
33
+ capabilities,
34
+ ...(options.instructions !== undefined && options.instructions !== "" ? { instructions: options.instructions } : {}),
29
35
  cacheHints: { "tools/list": TOOLS_LIST_CACHE, "server/discover": TOOLS_LIST_CACHE },
30
36
  });
31
37
  withDiscoverMeta(server, meta);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CLIENT_CAPABILITIES_META_KEY,\n McpServer,\n type AuthInfo,\n type CallToolResult,\n type InputRequiredResult,\n type McpRequestContext,\n type McpServerFactory,\n type ServerContext,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\"\nimport type { MextApp, ProgressUpdate } from \"../command.ts\"\nimport type { Instrumentation, Transport } from \"../instrumentation.ts\"\nimport { ANONYMOUS, type Principal } from \"../principal.ts\"\nimport type { JsonSchema, Registry } from \"../registry.ts\"\nimport { VERSION } from \"../index.ts\"\nimport { describeCommand, manifestHash } from \"../manifest.ts\"\nimport { MANIFEST_HASH_META_KEY } from \"./constants.ts\"\nimport { callDispatcher, dispatcherTools, type DispatcherTool, type ToolExposure } from \"./dispatch.ts\"\nimport { createRequestStateSigner, type InputRound, type RequestStateSigner } from \"./mrtr.ts\"\n\n/** tools/list · server/discover 캐시 힌트. 도구 정의는 principal 과 무관하게 고정 → public */\nexport const TOOLS_LIST_CACHE = { ttlMs: 300_000, cacheScope: \"public\" } as const\n\n/**\n * progressToken 있는 요청의 주기 progress 간격 (ms). 마지막 알림 후 이 시간 동안 알림이 없으면 보낸다.\n * TS SDK 클라이언트 기본 타임아웃 60초 · 프록시 유휴 타임아웃 (보통 60초) 보다 충분히 짧게\n */\nexport const PROGRESS_INTERVAL_MS = 15_000\n\n\nexport interface McpCoreOptions {\n registry: Registry\n /** ctx.app 생성. 처음 필요할 때 한 번 호출 (실패하면 다음 요청에서 재시도) */\n app?: () => MextApp | Promise<MextApp>\n /** 기본 execute */\n tools?: ToolExposure\n /** serverInfo.name. 기본 \"mext\" */\n name?: string\n /** serverInfo.version. 기본 mext 버전 */\n version?: string\n /**\n * 요청 주체. 기본 = HTTP transport 가 authInfo 로 넘긴 principal, 없으면 ANONYMOUS.\n * stdio = localPrincipal()\n */\n principal?: Principal | ((ctx: McpRequestContext) => Principal)\n /**\n * MRTR requestState 서명 키 (HMAC-SHA256, 32바이트 이상). requestState 는 2번째 이후 ctx.ask 에서만 쓴다.\n * 기본 = 프로세스별 임의 키. 여러 인스턴스 · 재시작을 넘는 재시도에는 공유 키를 지정한다\n */\n requestStateKey?: string | Uint8Array\n /** 주기 progress 간격 (ms). 기본 PROGRESS_INTERVAL_MS, 0 = 끔 */\n progressInterval?: number\n /** loadInstrumentation 결과 (onCommandError · span) */\n instrumentation?: Instrumentation\n /** onCommandError · span 의 transport. HTTP · stdio 어댑터가 지정 */\n transport?: Transport\n /**\n * _meta[\"dev.mext/manifestHash\"] 값. 기본 = registry 명령어로 계산 (mext dev = 스캔 결과).\n * mext start = manifest.json 의 manifestHash (같은 함수로 계산한 값)\n */\n manifestHash?: string\n}\n\n/**\n * Registry → MCP 서버 factory (SDK McpServerFactory). 요청 (HTTP) · 연결 (stdio) 마다 새 McpServer.\n * HTTP 의 createMcpHandler 와 stdio 의 serveStdio 가 같은 factory 를 쓴다.\n */\nexport function createMcpServerFactory(options: McpCoreOptions): McpServerFactory {\n const tools = dispatcherTools(options.registry, options.tools ?? \"execute\")\n const getApp = memoizeApp(options.app)\n const serverInfo = { name: options.name ?? \"mext\", version: options.version ?? VERSION }\n const signer = createRequestStateSigner(options.requestStateKey === undefined ? {} : { key: options.requestStateKey })\n const meta = { [MANIFEST_HASH_META_KEY]: options.manifestHash ?? registryManifestHash(options.registry) }\n return (requestContext) => {\n const principal = resolvePrincipal(options.principal, requestContext)\n const server = new McpServer(serverInfo, {\n capabilities: { tools: { listChanged: false } },\n cacheHints: { \"tools/list\": TOOLS_LIST_CACHE, \"server/discover\": TOOLS_LIST_CACHE },\n })\n withDiscoverMeta(server, meta)\n for (const tool of tools) {\n server.registerTool(\n tool.name,\n {\n title: tool.annotations.title,\n description: tool.description,\n inputSchema: passthroughSchema(tool.inputSchema),\n annotations: tool.annotations,\n },\n async (args, ctx) => withMeta(await runTool(options, tools, tool, args, ctx, principal, getApp, signer), meta),\n )\n }\n return server\n }\n}\n\n/** registry 명령어 메타데이터의 manifestHash (빌드 manifest 와 같은 계산) */\nexport function registryManifestHash(registry: Registry): string {\n return manifestHash([...registry.commands.values()].map(describeCommand))\n}\n\n/** 결과 _meta 에 mext 키 추가 (SDK 의 serverInfo 등 기존 _meta 유지) */\nfunction withMeta<T extends { _meta?: Record<string, unknown> }>(result: T, meta: Record<string, unknown>): T {\n return { ...result, _meta: { ...result._meta, ...meta } }\n}\n\n/**\n * server/discover 결과에 _meta 추가. SDK 의 discover handler 는 McpServer 생성 후 transport 진입점이\n * (재)설치하고 매번 server._ondiscover() 를 부르므로 인스턴스 메서드를 감싼다 (공개 API 에 discover _meta 옵션 없음)\n */\nfunction withDiscoverMeta(server: McpServer, meta: Record<string, unknown>): void {\n const inner = server.server as unknown as { _ondiscover?: () => Record<string, unknown> }\n const original = inner._ondiscover\n if (typeof original !== \"function\") return\n inner._ondiscover = function (this: unknown) {\n return withMeta(original.call(inner) as { _meta?: Record<string, unknown> }, meta)\n }\n}\n\nasync function runTool(\n options: McpCoreOptions,\n tools: readonly DispatcherTool[],\n tool: DispatcherTool,\n args: unknown,\n ctx: ServerContext,\n principal: Principal,\n getApp: () => Promise<MextApp>,\n signer: RequestStateSigner,\n): Promise<CallToolResult | InputRequiredResult> {\n const progress = progressReporter(ctx, options.progressInterval ?? PROGRESS_INTERVAL_MS)\n try {\n const app = await getApp()\n const result = await callDispatcher(options.registry, tool, args, {\n principal,\n signal: ctx.mcpReq.signal,\n ...(progress ? { progress: progress.report } : {}),\n app,\n tools,\n input: { ...inputRound(ctx), signer },\n ...(options.instrumentation ? { instrumentation: options.instrumentation, traceContext: traceCarrier(ctx) } : {}),\n ...(options.transport ? { transport: options.transport } : {}),\n })\n return result as CallToolResult | InputRequiredResult\n } finally {\n progress?.stop()\n }\n}\n\n/** 요청의 MRTR 입력: elicitation (form) 선언 · inputResponses · requestState (SDK 가 params 에서 분리) */\nfunction inputRound(ctx: ServerContext): InputRound {\n const envelope = ctx.mcpReq.envelope as Record<string, unknown> | undefined\n const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY] as { elicitation?: unknown } | undefined\n const state = ctx.mcpReq.requestState()\n return {\n elicitation: declaresFormElicitation(capabilities?.elicitation),\n ...(ctx.mcpReq.inputResponses ? { inputResponses: ctx.mcpReq.inputResponses } : {}),\n ...(typeof state === \"string\" ? { requestState: state } : {}),\n }\n}\n\n/** D6: params._meta 의 W3C trace context (traceparent · tracestate · baggage). 등록된 propagator 가 해석 */\nfunction traceCarrier(ctx: ServerContext): Record<string, string> | undefined {\n const meta = ctx.mcpReq._meta as Record<string, unknown> | undefined\n let carrier: Record<string, string> | undefined\n for (const key of [\"traceparent\", \"tracestate\", \"baggage\"]) {\n const value = meta?.[key]\n if (typeof value === \"string\") (carrier ??= {})[key] = value\n }\n return carrier\n}\n\n/** elicitation.form 선언 또는 mode 없는 elicitation: {} (SDK 와 같은 해석). 2025 레거시 요청 = envelope 없음 → false */\nfunction declaresFormElicitation(value: unknown): boolean {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false\n const modes = value as { form?: unknown; url?: unknown }\n return modes.form !== undefined || modes.url === undefined\n}\n\n/**\n * ctx.progress → notifications/progress. 요청에 progressToken 이 있을 때만 (없으면 undefined = 무시).\n * HTTP 는 progressToken 이 있는 요청을 SSE 로 응답하므로 알림이 스트림으로 간다.\n * 주기 progress: 마지막 알림 후 intervalMs 동안 알림이 없으면 마지막 total · message 를 다시 보낸다 (keepalive).\n * progress 값은 항상 증가 (사양 MUST): 증가하지 않는 값 · keepalive 는 직전 값에 아주 작은 값을 더한다.\n */\nfunction progressReporter(\n ctx: ServerContext,\n intervalMs: number,\n): { report: (update: ProgressUpdate) => void; stop: () => void } | undefined {\n const token = ctx.mcpReq._meta?.progressToken\n if (token === undefined) return undefined\n let last: ProgressUpdate | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n let stopped = false\n const schedule = () => {\n if (timer !== undefined) clearTimeout(timer)\n if (intervalMs > 0 && !stopped) timer = setTimeout(() => report({ ...(last ?? { progress: 0 }) }), intervalMs)\n }\n const report = (update: ProgressUpdate) => {\n if (stopped || ctx.mcpReq.signal.aborted) return\n const progress = last === undefined || update.progress > last.progress ? update.progress : increase(last.progress)\n last = { progress, ...(update.total !== undefined ? { total: update.total } : {}), ...(update.message !== undefined ? { message: update.message } : {}) }\n const params: Record<string, unknown> = { progressToken: token, ...last }\n void ctx.mcpReq.notify({ method: \"notifications/progress\", params }).catch(() => {})\n schedule()\n }\n schedule()\n return {\n report,\n stop() {\n stopped = true\n if (timer !== undefined) clearTimeout(timer)\n },\n }\n}\n\n/** 직전 값보다 큰 가장 가까운 값 (표시에는 드러나지 않는 크기) */\nfunction increase(value: number): number {\n return value + Math.max(Math.abs(value), 1) * 1e-9\n}\n\n/* principal 은 SDK 의 pass-through authInfo 로 transport → factory 에 전달 */\n\nconst PRINCIPAL_KEY = \"mext.principal\"\n\n/** HTTP transport: 검증된 principal → SDK authInfo (토큰 원문은 담지 않는다) */\nexport function principalAuthInfo(principal: Principal): AuthInfo {\n return { token: \"\", clientId: principal.id, scopes: [...principal.scopes], extra: { [PRINCIPAL_KEY]: principal } }\n}\n\nexport function principalFromAuthInfo(authInfo: AuthInfo | undefined): Principal | undefined {\n const value = authInfo?.extra?.[PRINCIPAL_KEY]\n return isPrincipal(value) ? value : undefined\n}\n\nfunction resolvePrincipal(option: McpCoreOptions[\"principal\"], ctx: McpRequestContext): Principal {\n if (typeof option === \"function\") return option(ctx)\n if (option) return option\n return principalFromAuthInfo(ctx.authInfo) ?? ANONYMOUS\n}\n\nfunction isPrincipal(value: unknown): value is Principal {\n const p = value as Partial<Principal> | undefined\n return typeof p?.id === \"string\" && typeof p.kind === \"string\" && Array.isArray(p.scopes)\n}\n\nfunction memoizeApp(factory: McpCoreOptions[\"app\"]): () => Promise<MextApp> {\n if (!factory) return async () => ({}) as MextApp\n let pending: Promise<MextApp> | undefined\n return () => {\n pending ??= Promise.resolve()\n .then(factory)\n .catch((error: unknown) => {\n pending = undefined\n throw error\n })\n return pending\n }\n}\n\n/**\n * 고정 JSON Schema 를 그대로 광고하고 검증은 하지 않는 Standard Schema.\n * dispatcher 인자 검증은 callDispatcher 가 구조화 오류 (INVALID_ARGUMENT) 로 수행한다.\n */\nfunction passthroughSchema(schema: JsonSchema): StandardSchemaWithJSON<Record<string, unknown>> {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"mext\",\n validate: (value: unknown) => ({ value: value as Record<string, unknown> }),\n jsonSchema: { input: () => schema, output: () => schema },\n },\n }\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,SAAS,GAQV,MAAM,8BAA8B,CAAA;AAIrC,OAAO,EAAE,SAAS,EAAkB,MAAM,iBAAiB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAA0C,MAAM,eAAe,CAAA;AACvG,OAAO,EAAE,wBAAwB,EAA4C,MAAM,WAAW,CAAA;AAI9F,8EAA8E;AAC9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAW,CAAA;AAEjF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAA;AAwC1C;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAuB;IAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC,CAAA;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACtC,MAAM,UAAU,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,CAAA;IACxF,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IACtH,MAAM,IAAI,GAAG,EAAE,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC,YAAY,IAAI,oBAAoB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA;IACzG,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,YAAY,CAAA;IACvD,MAAM,YAAY,GAAG;QACnB,GAAG,CAAC,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtG,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;KACR,CAAA;IACvB,OAAO,CAAC,cAAc,EAAE,EAAE;QACxB,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;QACrE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,UAAU,EAAE;YACvC,YAAY;YACZ,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpH,UAAU,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE;SACpF,CAAC,CAAA;QACF,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,IAAI,EACT;gBACE,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK;gBAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,WAAW,EAAE,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC;gBAChD,WAAW,EAAE,IAAI,CAAC,WAAW;aAC9B,EACD,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAC/G,CAAA;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;AACH,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,oBAAoB,CAAC,QAAkB;IACrD,OAAO,YAAY,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAA;AAC3E,CAAC;AAED,4DAA4D;AAC5D,SAAS,QAAQ,CAAgD,MAAS,EAAE,IAA6B;IACvG,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,CAAA;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,MAAiB,EAAE,IAA6B;IACxE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAoE,CAAA;IACzF,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAA;IAClC,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAM;IAC1C,KAAK,CAAC,WAAW,GAAG;QAClB,OAAO,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAwC,EAAE,IAAI,CAAC,CAAA;IACpF,CAAC,CAAA;AACH,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,OAAuB,EACvB,KAAgC,EAChC,IAAoB,EACpB,IAAa,EACb,GAAkB,EAClB,SAAoB,EACpB,MAA8B,EAC9B,MAA0B;IAE1B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,gBAAgB,IAAI,oBAAoB,CAAC,CAAA;IACxF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAA;QAC1B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;YAChE,SAAS;YACT,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM;YACzB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,GAAG;YACH,KAAK;YACL,KAAK,EAAE,EAAE,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE;YACrC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,YAAY,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjH,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAA;QACF,OAAO,MAA8C,CAAA;IACvD,CAAC;YAAS,CAAC;QACT,QAAQ,EAAE,IAAI,EAAE,CAAA;IAClB,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,SAAS,UAAU,CAAC,GAAkB;IACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAA+C,CAAA;IAC3E,MAAM,YAAY,GAAG,QAAQ,EAAE,CAAC,4BAA4B,CAA0C,CAAA;IACtG,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,CAAA;IACvC,OAAO;QACL,WAAW,EAAE,uBAAuB,CAAC,YAAY,EAAE,WAAW,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9D,CAAA;AACH,CAAC;AAED,qGAAqG;AACrG,SAAS,YAAY,CAAC,GAAkB;IACtC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,KAA4C,CAAA;IACpE,IAAI,OAA2C,CAAA;IAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAA;QACzB,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IAC9D,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,sGAAsG;AACtG,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACrF,MAAM,KAAK,GAAG,KAA0C,CAAA;IACxD,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAA;AAC5D,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CACvB,GAAkB,EAClB,UAAkB;IAElB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,IAAI,IAAgC,CAAA;IACpC,IAAI,KAAgD,CAAA;IACpD,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;QAC5C,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,OAAO;YAAE,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAA;IAChH,CAAC,CAAA;IACD,MAAM,MAAM,GAAG,CAAC,MAAsB,EAAE,EAAE;QACxC,IAAI,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO;YAAE,OAAM;QAChD,MAAM,QAAQ,GAAG,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAClH,IAAI,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QACzJ,MAAM,MAAM,GAA4B,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,CAAA;QACzE,KAAK,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACpF,QAAQ,EAAE,CAAA;IACZ,CAAC,CAAA;IACD,QAAQ,EAAE,CAAA;IACV,OAAO;QACL,MAAM;QACN,IAAI;YACF,OAAO,GAAG,IAAI,CAAA;YACd,IAAI,KAAK,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAA;QAC9C,CAAC;KACF,CAAA;AACH,CAAC;AAED,0CAA0C;AAC1C,SAAS,QAAQ,CAAC,KAAa;IAC7B,OAAO,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AACpD,CAAC;AAED,wEAAwE;AAExE,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAEtC,mEAAmE;AACnE,MAAM,UAAU,iBAAiB,CAAC,SAAoB;IACpD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,SAAS,EAAE,EAAE,CAAA;AACpH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAA8B;IAClE,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,EAAE,CAAC,aAAa,CAAC,CAAA;IAC9C,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC/C,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAmC,EAAE,GAAsB;IACnF,IAAI,OAAO,MAAM,KAAK,UAAU;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAA;IACpD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IACzB,OAAO,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAA;AACzD,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,CAAC,GAAG,KAAuC,CAAA;IACjD,OAAO,OAAO,CAAC,EAAE,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAC3F,CAAC;AAED,SAAS,UAAU,CAAC,OAA8B;IAChD,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAY,CAAA;IAChD,IAAI,OAAqC,CAAA;IACzC,OAAO,GAAG,EAAE;QACV,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE;aAC1B,IAAI,CAAC,OAAO,CAAC;aACb,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACxB,OAAO,GAAG,SAAS,CAAA;YACnB,MAAM,KAAK,CAAA;QACb,CAAC,CAAC,CAAA;QACJ,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,MAAkB;IAC3C,OAAO;QACL,WAAW,EAAE;YACX,OAAO,EAAE,CAAC;YACV,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAgC,EAAE,CAAC;YAC3E,UAAU,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;SAC1D;KACF,CAAA;AACH,CAAC","sourcesContent":["import {\n CLIENT_CAPABILITIES_META_KEY,\n McpServer,\n type AuthInfo,\n type CallToolResult,\n type InputRequiredResult,\n type McpRequestContext,\n type McpServerFactory,\n type ServerContext,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\"\nimport type { MextApp, ProgressUpdate } from \"../command.ts\"\nimport type { McpCapabilities } from \"../config.ts\"\nimport type { Instrumentation, Transport } from \"../instrumentation.ts\"\nimport { ANONYMOUS, type Principal } from \"../principal.ts\"\nimport type { JsonSchema, Registry } from \"../registry.ts\"\nimport { VERSION } from \"../index.ts\"\nimport { describeCommand, manifestHash } from \"../manifest.ts\"\nimport { MANIFEST_HASH_META_KEY } from \"./constants.ts\"\nimport { callDispatcher, dispatcherTools, type DispatcherTool, type ToolExposure } from \"./dispatch.ts\"\nimport { createRequestStateSigner, type InputRound, type RequestStateSigner } from \"./mrtr.ts\"\n\ntype ServerCapabilities = NonNullable<NonNullable<ConstructorParameters<typeof McpServer>[1]>[\"capabilities\"]>\n\n/** tools/list · server/discover 캐시 힌트. 도구 정의는 principal 과 무관하게 고정 → public */\nexport const TOOLS_LIST_CACHE = { ttlMs: 300_000, cacheScope: \"public\" } as const\n\n/**\n * progressToken 있는 요청의 주기 progress 간격 (ms). 마지막 알림 후 이 시간 동안 알림이 없으면 보낸다.\n * TS SDK 클라이언트 기본 타임아웃 60초 · 프록시 유휴 타임아웃 (보통 60초) 보다 충분히 짧게\n */\nexport const PROGRESS_INTERVAL_MS = 15_000\n\n\nexport interface McpCoreOptions {\n registry: Registry\n /** ctx.app 생성. 처음 필요할 때 한 번 호출 (실패하면 다음 요청에서 재시도) */\n app?: () => MextApp | Promise<MextApp>\n /** 기본 execute */\n tools?: ToolExposure\n /** serverInfo.name. 기본 \"mext\" */\n name?: string\n /** initialize · server/discover 결과 instructions */\n instructions?: string\n /** 서버 capabilities 에 병합 (experimental). tools capability 는 mext 가 고정 */\n capabilities?: McpCapabilities\n /** serverInfo.version. 기본 mext 버전 */\n version?: string\n /**\n * 요청 주체. 기본 = HTTP transport 가 authInfo 로 넘긴 principal, 없으면 ANONYMOUS.\n * stdio = localPrincipal()\n */\n principal?: Principal | ((ctx: McpRequestContext) => Principal)\n /**\n * MRTR requestState 서명 키 (HMAC-SHA256, 32바이트 이상). requestState 는 2번째 이후 ctx.ask 에서만 쓴다.\n * 기본 = 프로세스별 임의 키. 여러 인스턴스 · 재시작을 넘는 재시도에는 공유 키를 지정한다\n */\n requestStateKey?: string | Uint8Array\n /** 주기 progress 간격 (ms). 기본 PROGRESS_INTERVAL_MS, 0 = 끔 */\n progressInterval?: number\n /** loadInstrumentation 결과 (onCommandError · span) */\n instrumentation?: Instrumentation\n /** onCommandError · span 의 transport. HTTP · stdio 어댑터가 지정 */\n transport?: Transport\n /**\n * _meta[\"dev.mext/manifestHash\"] 값. 기본 = registry 명령어로 계산 (mext dev = 스캔 결과).\n * mext start = manifest.json 의 manifestHash (같은 함수로 계산한 값)\n */\n manifestHash?: string\n}\n\n/**\n * Registry → MCP 서버 factory (SDK McpServerFactory). 요청 (HTTP) · 연결 (stdio) 마다 새 McpServer.\n * HTTP 의 createMcpHandler 와 stdio 의 serveStdio 가 같은 factory 를 쓴다.\n */\nexport function createMcpServerFactory(options: McpCoreOptions): McpServerFactory {\n const tools = dispatcherTools(options.registry, options.tools ?? \"execute\")\n const getApp = memoizeApp(options.app)\n const serverInfo = { name: options.name ?? \"mext\", version: options.version ?? VERSION }\n const signer = createRequestStateSigner(options.requestStateKey === undefined ? {} : { key: options.requestStateKey })\n const meta = { [MANIFEST_HASH_META_KEY]: options.manifestHash ?? registryManifestHash(options.registry) }\n const experimental = options.capabilities?.experimental\n const capabilities = {\n ...(experimental && Object.keys(experimental).length > 0 ? { experimental: { ...experimental } } : {}),\n tools: { listChanged: false },\n } as ServerCapabilities\n return (requestContext) => {\n const principal = resolvePrincipal(options.principal, requestContext)\n const server = new McpServer(serverInfo, {\n capabilities,\n ...(options.instructions !== undefined && options.instructions !== \"\" ? { instructions: options.instructions } : {}),\n cacheHints: { \"tools/list\": TOOLS_LIST_CACHE, \"server/discover\": TOOLS_LIST_CACHE },\n })\n withDiscoverMeta(server, meta)\n for (const tool of tools) {\n server.registerTool(\n tool.name,\n {\n title: tool.annotations.title,\n description: tool.description,\n inputSchema: passthroughSchema(tool.inputSchema),\n annotations: tool.annotations,\n },\n async (args, ctx) => withMeta(await runTool(options, tools, tool, args, ctx, principal, getApp, signer), meta),\n )\n }\n return server\n }\n}\n\n/** registry 명령어 메타데이터의 manifestHash (빌드 manifest 와 같은 계산) */\nexport function registryManifestHash(registry: Registry): string {\n return manifestHash([...registry.commands.values()].map(describeCommand))\n}\n\n/** 결과 _meta 에 mext 키 추가 (SDK 의 serverInfo 등 기존 _meta 유지) */\nfunction withMeta<T extends { _meta?: Record<string, unknown> }>(result: T, meta: Record<string, unknown>): T {\n return { ...result, _meta: { ...result._meta, ...meta } }\n}\n\n/**\n * server/discover 결과에 _meta 추가. SDK 의 discover handler 는 McpServer 생성 후 transport 진입점이\n * (재)설치하고 매번 server._ondiscover() 를 부르므로 인스턴스 메서드를 감싼다 (공개 API 에 discover _meta 옵션 없음)\n */\nfunction withDiscoverMeta(server: McpServer, meta: Record<string, unknown>): void {\n const inner = server.server as unknown as { _ondiscover?: () => Record<string, unknown> }\n const original = inner._ondiscover\n if (typeof original !== \"function\") return\n inner._ondiscover = function (this: unknown) {\n return withMeta(original.call(inner) as { _meta?: Record<string, unknown> }, meta)\n }\n}\n\nasync function runTool(\n options: McpCoreOptions,\n tools: readonly DispatcherTool[],\n tool: DispatcherTool,\n args: unknown,\n ctx: ServerContext,\n principal: Principal,\n getApp: () => Promise<MextApp>,\n signer: RequestStateSigner,\n): Promise<CallToolResult | InputRequiredResult> {\n const progress = progressReporter(ctx, options.progressInterval ?? PROGRESS_INTERVAL_MS)\n try {\n const app = await getApp()\n const result = await callDispatcher(options.registry, tool, args, {\n principal,\n signal: ctx.mcpReq.signal,\n ...(progress ? { progress: progress.report } : {}),\n app,\n tools,\n input: { ...inputRound(ctx), signer },\n ...(options.instrumentation ? { instrumentation: options.instrumentation, traceContext: traceCarrier(ctx) } : {}),\n ...(options.transport ? { transport: options.transport } : {}),\n })\n return result as CallToolResult | InputRequiredResult\n } finally {\n progress?.stop()\n }\n}\n\n/** 요청의 MRTR 입력: elicitation (form) 선언 · inputResponses · requestState (SDK 가 params 에서 분리) */\nfunction inputRound(ctx: ServerContext): InputRound {\n const envelope = ctx.mcpReq.envelope as Record<string, unknown> | undefined\n const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY] as { elicitation?: unknown } | undefined\n const state = ctx.mcpReq.requestState()\n return {\n elicitation: declaresFormElicitation(capabilities?.elicitation),\n ...(ctx.mcpReq.inputResponses ? { inputResponses: ctx.mcpReq.inputResponses } : {}),\n ...(typeof state === \"string\" ? { requestState: state } : {}),\n }\n}\n\n/** D6: params._meta 의 W3C trace context (traceparent · tracestate · baggage). 등록된 propagator 가 해석 */\nfunction traceCarrier(ctx: ServerContext): Record<string, string> | undefined {\n const meta = ctx.mcpReq._meta as Record<string, unknown> | undefined\n let carrier: Record<string, string> | undefined\n for (const key of [\"traceparent\", \"tracestate\", \"baggage\"]) {\n const value = meta?.[key]\n if (typeof value === \"string\") (carrier ??= {})[key] = value\n }\n return carrier\n}\n\n/** elicitation.form 선언 또는 mode 없는 elicitation: {} (SDK 와 같은 해석). 2025 레거시 요청 = envelope 없음 → false */\nfunction declaresFormElicitation(value: unknown): boolean {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false\n const modes = value as { form?: unknown; url?: unknown }\n return modes.form !== undefined || modes.url === undefined\n}\n\n/**\n * ctx.progress → notifications/progress. 요청에 progressToken 이 있을 때만 (없으면 undefined = 무시).\n * HTTP 는 progressToken 이 있는 요청을 SSE 로 응답하므로 알림이 스트림으로 간다.\n * 주기 progress: 마지막 알림 후 intervalMs 동안 알림이 없으면 마지막 total · message 를 다시 보낸다 (keepalive).\n * progress 값은 항상 증가 (사양 MUST): 증가하지 않는 값 · keepalive 는 직전 값에 아주 작은 값을 더한다.\n */\nfunction progressReporter(\n ctx: ServerContext,\n intervalMs: number,\n): { report: (update: ProgressUpdate) => void; stop: () => void } | undefined {\n const token = ctx.mcpReq._meta?.progressToken\n if (token === undefined) return undefined\n let last: ProgressUpdate | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n let stopped = false\n const schedule = () => {\n if (timer !== undefined) clearTimeout(timer)\n if (intervalMs > 0 && !stopped) timer = setTimeout(() => report({ ...(last ?? { progress: 0 }) }), intervalMs)\n }\n const report = (update: ProgressUpdate) => {\n if (stopped || ctx.mcpReq.signal.aborted) return\n const progress = last === undefined || update.progress > last.progress ? update.progress : increase(last.progress)\n last = { progress, ...(update.total !== undefined ? { total: update.total } : {}), ...(update.message !== undefined ? { message: update.message } : {}) }\n const params: Record<string, unknown> = { progressToken: token, ...last }\n void ctx.mcpReq.notify({ method: \"notifications/progress\", params }).catch(() => {})\n schedule()\n }\n schedule()\n return {\n report,\n stop() {\n stopped = true\n if (timer !== undefined) clearTimeout(timer)\n },\n }\n}\n\n/** 직전 값보다 큰 가장 가까운 값 (표시에는 드러나지 않는 크기) */\nfunction increase(value: number): number {\n return value + Math.max(Math.abs(value), 1) * 1e-9\n}\n\n/* principal 은 SDK 의 pass-through authInfo 로 transport → factory 에 전달 */\n\nconst PRINCIPAL_KEY = \"mext.principal\"\n\n/** HTTP transport: 검증된 principal → SDK authInfo (토큰 원문은 담지 않는다) */\nexport function principalAuthInfo(principal: Principal): AuthInfo {\n return { token: \"\", clientId: principal.id, scopes: [...principal.scopes], extra: { [PRINCIPAL_KEY]: principal } }\n}\n\nexport function principalFromAuthInfo(authInfo: AuthInfo | undefined): Principal | undefined {\n const value = authInfo?.extra?.[PRINCIPAL_KEY]\n return isPrincipal(value) ? value : undefined\n}\n\nfunction resolvePrincipal(option: McpCoreOptions[\"principal\"], ctx: McpRequestContext): Principal {\n if (typeof option === \"function\") return option(ctx)\n if (option) return option\n return principalFromAuthInfo(ctx.authInfo) ?? ANONYMOUS\n}\n\nfunction isPrincipal(value: unknown): value is Principal {\n const p = value as Partial<Principal> | undefined\n return typeof p?.id === \"string\" && typeof p.kind === \"string\" && Array.isArray(p.scopes)\n}\n\nfunction memoizeApp(factory: McpCoreOptions[\"app\"]): () => Promise<MextApp> {\n if (!factory) return async () => ({}) as MextApp\n let pending: Promise<MextApp> | undefined\n return () => {\n pending ??= Promise.resolve()\n .then(factory)\n .catch((error: unknown) => {\n pending = undefined\n throw error\n })\n return pending\n }\n}\n\n/**\n * 고정 JSON Schema 를 그대로 광고하고 검증은 하지 않는 Standard Schema.\n * dispatcher 인자 검증은 callDispatcher 가 구조화 오류 (INVALID_ARGUMENT) 로 수행한다.\n */\nfunction passthroughSchema(schema: JsonSchema): StandardSchemaWithJSON<Record<string, unknown>> {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"mext\",\n validate: (value: unknown) => ({ value: value as Record<string, unknown> }),\n jsonSchema: { input: () => schema, output: () => schema },\n },\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { Readable } from "node:stream";
2
2
  import { Writable } from "node:stream";
3
3
  import type { MextApp } from "../command.ts";
4
+ import type { McpCapabilities, McpConnectHandler } from "../config.ts";
4
5
  import { type Principal } from "../principal.ts";
5
6
  import type { Registry } from "../registry.ts";
6
7
  import type { Instrumentation } from "../instrumentation.ts";
@@ -12,6 +13,21 @@ export interface McpStdioOptions {
12
13
  tools?: ToolExposure;
13
14
  /** serverInfo.name. 기본 "mext" */
14
15
  name?: string;
16
+ /** initialize · server/discover 결과 instructions */
17
+ instructions?: string;
18
+ /** 서버 capabilities 에 병합 (experimental) */
19
+ capabilities?: McpCapabilities;
20
+ /**
21
+ * 연결 하나에 1회 (2025 = notifications/initialized 후, 2026-07-28 = 연결 직후).
22
+ * conn.notify = 요청과 무관한 서버 발신 알림. 반환 함수 = 연결 종료 시 cleanup. 예외 = onerror
23
+ */
24
+ onConnect?: McpConnectHandler;
25
+ /**
26
+ * auto = 2026-07-28 · 2025 모두 (SDK serveStdio 가 판별). legacy = 2025 만:
27
+ * server/discover = -32601 → 클라이언트가 initialize 로 재시도 (Claude Code channel 은 2025 연결에서만 전달).
28
+ * 기본 = capabilities.experimental["claude/channel"] 선언 시 legacy, 그 외 auto
29
+ */
30
+ era?: "auto" | "legacy";
15
31
  /** 연결 주체 (로컬 모드). 기본 localPrincipal() */
16
32
  principal?: Principal;
17
33
  /** 기본 process.stdin */
@@ -43,6 +59,8 @@ export interface McpStdioServer {
43
59
  * 헤더 검증 · Origin · 바인딩 · Authorization 은 해당 없음. 주체 = 로컬 (kind local).
44
60
  */
45
61
  export declare function serveMcpStdio(options: McpStdioOptions): McpStdioServer;
62
+ /** 연결 종료 후 onConnect cleanup 대기 상한 (ms). 넘으면 오류 보고 후 종료 진행 */
63
+ export declare const CLEANUP_TIMEOUT_MS = 5000;
46
64
  export interface StdoutRedirect {
47
65
  /** 원래 process.stdout 으로 쓰는 스트림. MCP transport 전용 */
48
66
  wire: Writable;
package/dist/mcp/stdio.js CHANGED
@@ -8,42 +8,185 @@ import { createMcpServerFactory } from "./server.js";
8
8
  * 헤더 검증 · Origin · 바인딩 · Authorization 은 해당 없음. 주체 = 로컬 (kind local).
9
9
  */
10
10
  export function serveMcpStdio(options) {
11
- const factory = createMcpServerFactory({
11
+ const core = createMcpServerFactory({
12
12
  registry: options.registry,
13
13
  principal: options.principal ?? localPrincipal(),
14
14
  ...(options.app ? { app: options.app } : {}),
15
15
  ...(options.tools ? { tools: options.tools } : {}),
16
16
  ...(options.name ? { name: options.name } : {}),
17
+ ...(options.instructions !== undefined ? { instructions: options.instructions } : {}),
18
+ ...(options.capabilities ? { capabilities: options.capabilities } : {}),
17
19
  ...(options.requestStateKey !== undefined ? { requestStateKey: options.requestStateKey } : {}),
18
20
  ...(options.progressInterval !== undefined ? { progressInterval: options.progressInterval } : {}),
19
21
  ...(options.instrumentation ? { instrumentation: options.instrumentation } : {}),
20
22
  ...(options.manifestHash !== undefined ? { manifestHash: options.manifestHash } : {}),
21
23
  transport: "stdio",
22
24
  });
25
+ const onConnect = options.onConnect;
26
+ const report = (error) => options.onerror?.(error instanceof Error ? error : new Error(String(error)));
27
+ /** 인스턴스별 정리 완료 (onConnect settle + cleanup). closed 는 이것들을 기다린다 */
28
+ const settled = new Set();
29
+ const factory = onConnect
30
+ ? async (ctx) => {
31
+ const server = (await core(ctx));
32
+ settled.add(attachConnection(server, ctx.era, onConnect, report));
33
+ return server;
34
+ }
35
+ : core;
23
36
  const transport = new StdioServerTransport(options.stdin, options.stdout);
24
37
  let resolveClosed;
25
38
  const closed = new Promise((resolve) => {
26
39
  resolveClosed = resolve;
27
40
  });
28
- const handle = serveStdio(factory, options.onerror ? { transport, onerror: options.onerror } : { transport });
29
- // serveStdio 가 onclose 를 동기적으로 설정한 뒤 감싼다 (stdin 이벤트는 비동기)
30
- const sdkOnClose = transport.onclose;
31
- transport.onclose = () => {
32
- try {
33
- sdkOnClose?.();
34
- }
35
- finally {
36
- resolveClosed();
37
- }
41
+ let finishing = false;
42
+ const finish = () => {
43
+ if (finishing)
44
+ return;
45
+ finishing = true;
46
+ void waitCleanups(settled, report).then(resolveClosed);
47
+ };
48
+ const wrapClose = () => {
49
+ const sdkOnClose = transport.onclose;
50
+ transport.onclose = () => {
51
+ try {
52
+ sdkOnClose?.();
53
+ }
54
+ finally {
55
+ finish();
56
+ }
57
+ };
38
58
  };
59
+ let handle;
60
+ const era = options.era ?? (options.capabilities?.experimental?.[CLAUDE_CHANNEL] !== undefined ? "legacy" : "auto");
61
+ if (era === "legacy") {
62
+ // serveStdio 는 server/discover 를 받으면 2026-07-28 로 고정한다 → 우회.
63
+ // SDK 기본 supportedProtocolVersions = 2025 버전만 → McpServer 생성자가 server/discover 를 등록하지 않는다
64
+ const ready = Promise.resolve(factory({ era: "legacy" })).then(async (product) => {
65
+ const server = product;
66
+ server.server.onerror = report;
67
+ await server.connect(transport);
68
+ wrapClose();
69
+ return server;
70
+ });
71
+ ready.catch((error) => {
72
+ report(error);
73
+ finish();
74
+ });
75
+ handle = {
76
+ async close() {
77
+ const server = await ready.catch(() => undefined);
78
+ await server?.close();
79
+ },
80
+ };
81
+ }
82
+ else {
83
+ handle = serveStdio(factory, options.onerror ? { transport, onerror: options.onerror } : { transport });
84
+ // serveStdio 가 onclose 를 동기적으로 설정한 뒤 감싼다 (stdin 이벤트는 비동기)
85
+ wrapClose();
86
+ }
39
87
  return {
40
88
  closed,
41
89
  async close() {
42
90
  await handle.close();
43
- resolveClosed();
91
+ finish();
92
+ await closed;
44
93
  },
45
94
  };
46
95
  }
96
+ /** Claude Code channel capability. 선언하면 stdio 기본 era = legacy */
97
+ const CLAUDE_CHANNEL = "claude/channel";
98
+ /** 연결 종료 후 onConnect cleanup 대기 상한 (ms). 넘으면 오류 보고 후 종료 진행 */
99
+ export const CLEANUP_TIMEOUT_MS = 5_000;
100
+ async function waitCleanups(settled, report) {
101
+ if (settled.size === 0)
102
+ return;
103
+ let timer;
104
+ const timeout = new Promise((resolve) => {
105
+ timer = setTimeout(() => resolve("timeout"), CLEANUP_TIMEOUT_MS);
106
+ timer.unref?.();
107
+ });
108
+ const result = await Promise.race([Promise.all(settled), timeout]);
109
+ clearTimeout(timer);
110
+ if (result === "timeout")
111
+ report(new Error(`mcp.onConnect cleanup did not finish within ${CLEANUP_TIMEOUT_MS}ms`));
112
+ }
113
+ /**
114
+ * McpServer 인스턴스 하나 = stdio 연결 하나. onConnect 시점: 2025 = notifications/initialized
115
+ * (initialize 응답 전 알림은 클라이언트가 버릴 수 있음), 2026-07-28 = connect 직후 (handshake 없음).
116
+ * SDK 가 era 판별용 probe 인스턴스를 버리면 그 인스턴스의 onclose 로 cleanup 된다.
117
+ * 반환 = 인스턴스 종료 후 (onConnect settle · cleanup 완료) resolve. 시작 전 종료 = 바로 resolve
118
+ */
119
+ function attachConnection(server, era, onConnect, report) {
120
+ const inner = server.server;
121
+ let connected;
122
+ let resolveDone;
123
+ const done = new Promise((resolve) => {
124
+ resolveDone = resolve;
125
+ });
126
+ let closed = false;
127
+ const handlers = new Map();
128
+ // SDK 가 등록하지 않은 method 만 fallback 으로 온다 (사양 알림은 SDK handler · era 필터가 먼저)
129
+ inner.fallbackNotificationHandler = async (notification) => {
130
+ const handler = handlers.get(notification.method);
131
+ if (!handler)
132
+ return;
133
+ const params = (notification.params ?? {});
134
+ try {
135
+ await handler(params);
136
+ }
137
+ catch (error) {
138
+ report(error);
139
+ }
140
+ };
141
+ const conn = {
142
+ era,
143
+ notify: (method, params) => inner.notification({ method, ...(params !== undefined ? { params } : {}) }),
144
+ onNotification(method, handler) {
145
+ handlers.set(method, handler);
146
+ return () => {
147
+ if (handlers.get(method) === handler)
148
+ handlers.delete(method);
149
+ };
150
+ },
151
+ };
152
+ const start = () => {
153
+ if (connected || closed)
154
+ return;
155
+ connected = Promise.resolve().then(() => onConnect(conn));
156
+ connected.catch(report);
157
+ };
158
+ const previousClose = inner.onclose;
159
+ inner.onclose = () => {
160
+ previousClose?.();
161
+ if (closed)
162
+ return;
163
+ closed = true;
164
+ if (!connected)
165
+ return resolveDone();
166
+ connected
167
+ .then(async (cleanup) => {
168
+ if (typeof cleanup === "function")
169
+ await cleanup();
170
+ }, () => { })
171
+ .catch(report)
172
+ .finally(resolveDone);
173
+ };
174
+ if (era === "legacy") {
175
+ const previousInit = inner.oninitialized;
176
+ inner.oninitialized = () => {
177
+ previousInit?.();
178
+ start();
179
+ };
180
+ }
181
+ else {
182
+ const connect = server.connect.bind(server);
183
+ server.connect = async (transport) => {
184
+ await connect(transport);
185
+ start();
186
+ };
187
+ }
188
+ return done;
189
+ }
47
190
  /**
48
191
  * D4: stdio 모드에서 transport 외의 stdout 쓰기 (console.log · process.stdout.write) 를 stderr 로 돌린다.
49
192
  * 반환된 wire 만 실제 stdout 에 쓴다. 명령어 모듈 import 전에 호출해야 top-level 출력도 막는다.
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/mcp/stdio.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAErF,OAAO,EAAE,cAAc,EAAkB,MAAM,iBAAiB,CAAA;AAIhE,OAAO,EAAE,sBAAsB,EAAuB,MAAM,aAAa,CAAA;AAmCzE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,MAAM,OAAO,GAAG,sBAAsB,CAAC;QACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,cAAc,EAAE;QAChD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,SAAS,EAAE,OAAO;KACnB,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACzE,IAAI,aAA0B,CAAA;IAC9B,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC3C,aAAa,GAAG,OAAO,CAAA;IACzB,CAAC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;IAC7G,0DAA0D;IAC1D,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAA;IACpC,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE;QACvB,IAAI,CAAC;YACH,UAAU,EAAE,EAAE,CAAA;QAChB,CAAC;gBAAS,CAAC;YACT,aAAa,EAAE,CAAA;QACjB,CAAC;IACH,CAAC,CAAA;IACD,OAAO;QACL,MAAM;QACN,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,aAAa,EAAE,CAAA;QACjB,CAAC;KACF,CAAA;AACH,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAA;IAC7B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAe,EAAW,EAAE,CAC9C,OAAO,CAAC,MAAM,CAAC,KAAsC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACpF,MAAM,CAAC,KAAK,GAAG,QAA+B,CAAA;IAC9C,kFAAkF;IAClF,MAAM,MAAM,GAAG,GAAS,EAAE,GAAE,CAAC,CAAA;IAC7B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC1B,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC;QACxB,KAAK,CAAC,KAAsB,EAAE,SAAS,EAAE,QAAQ;YAC/C,MAAM,KAAK,GAAG,QAAqF,CAAA;YACnG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAA;QAC/D,CAAC;KACF,CAAC,CAAA;IACF,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,OAAO;QACL,IAAI;QACJ,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,MAAM,CAAC,KAAK,KAAM,QAAgC;gBAAE,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAA;YAC/E,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC7B,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { Readable } from \"node:stream\"\nimport { Writable } from \"node:stream\"\nimport { serveStdio, StdioServerTransport } from \"@modelcontextprotocol/server/stdio\"\nimport type { MextApp } from \"../command.ts\"\nimport { localPrincipal, type Principal } from \"../principal.ts\"\nimport type { Registry } from \"../registry.ts\"\nimport type { Instrumentation } from \"../instrumentation.ts\"\nimport type { ToolExposure } from \"./dispatch.ts\"\nimport { createMcpServerFactory, type McpCoreOptions } from \"./server.ts\"\n\nexport interface McpStdioOptions {\n registry: Registry\n app?: () => MextApp | Promise<MextApp>\n tools?: ToolExposure\n /** serverInfo.name. 기본 \"mext\" */\n name?: string\n /** 연결 주체 (로컬 모드). 기본 localPrincipal() */\n principal?: Principal\n /** 기본 process.stdin */\n stdin?: Readable\n /**\n * MCP 메시지 출력. 기본 process.stdout.\n * D4: process.stdout 을 쓰는 경우 redirectStdout() 의 wire 를 넘겨 핸들러 출력이 메시지와 섞이지 않게 한다\n */\n stdout?: Writable\n /** 응답에 영향 없는 오류 보고 (SDK 내부 오류 · 깨진 메시지) */\n onerror?: (error: Error) => void\n /** MRTR requestState 서명 키 (createMcpServerFactory 와 같음) */\n requestStateKey?: McpCoreOptions[\"requestStateKey\"]\n /** 주기 progress 간격 (ms). 0 = 끔 */\n progressInterval?: number\n /** loadInstrumentation 결과 (onCommandError · span, transport = \"stdio\") */\n instrumentation?: Instrumentation\n /** _meta[\"dev.mext/manifestHash\"]. McpCoreOptions.manifestHash (기본 = registry 로 계산) */\n manifestHash?: string\n}\n\nexport interface McpStdioServer {\n /** stdin EOF · stdout 오류 · close() 로 연결이 끝나면 resolve */\n closed: Promise<void>\n close(): Promise<void>\n}\n\n/**\n * MCP stdio (2026-07-28 · 2025 레거시 모두 SDK serveStdio). 줄 단위 JSON-RPC, 연결 하나 = McpServer 하나.\n * notifications/cancelled → ctx.signal abort (SDK). stdin EOF = 연결 종료, 진행 중 요청은 abort 되고 응답하지 않는다.\n * 헤더 검증 · Origin · 바인딩 · Authorization 은 해당 없음. 주체 = 로컬 (kind local).\n */\nexport function serveMcpStdio(options: McpStdioOptions): McpStdioServer {\n const factory = createMcpServerFactory({\n registry: options.registry,\n principal: options.principal ?? localPrincipal(),\n ...(options.app ? { app: options.app } : {}),\n ...(options.tools ? { tools: options.tools } : {}),\n ...(options.name ? { name: options.name } : {}),\n ...(options.requestStateKey !== undefined ? { requestStateKey: options.requestStateKey } : {}),\n ...(options.progressInterval !== undefined ? { progressInterval: options.progressInterval } : {}),\n ...(options.instrumentation ? { instrumentation: options.instrumentation } : {}),\n ...(options.manifestHash !== undefined ? { manifestHash: options.manifestHash } : {}),\n transport: \"stdio\",\n })\n const transport = new StdioServerTransport(options.stdin, options.stdout)\n let resolveClosed!: () => void\n const closed = new Promise<void>((resolve) => {\n resolveClosed = resolve\n })\n const handle = serveStdio(factory, options.onerror ? { transport, onerror: options.onerror } : { transport })\n // serveStdio 가 onclose 를 동기적으로 설정한 뒤 감싼다 (stdin 이벤트는 비동기)\n const sdkOnClose = transport.onclose\n transport.onclose = () => {\n try {\n sdkOnClose?.()\n } finally {\n resolveClosed()\n }\n }\n return {\n closed,\n async close() {\n await handle.close()\n resolveClosed()\n },\n }\n}\n\nexport interface StdoutRedirect {\n /** 원래 process.stdout 으로 쓰는 스트림. MCP transport 전용 */\n wire: Writable\n /** process.stdout.write 원복 */\n restore(): void\n}\n\n/**\n * D4: stdio 모드에서 transport 외의 stdout 쓰기 (console.log · process.stdout.write) 를 stderr 로 돌린다.\n * 반환된 wire 만 실제 stdout 에 쓴다. 명령어 모듈 import 전에 호출해야 top-level 출력도 막는다.\n * 자식 프로세스 · fd 1 직접 쓰기 (fs.writeSync(1)) 는 막지 못한다.\n */\nexport function redirectStdout(): StdoutRedirect {\n const stdout = process.stdout\n const original = stdout.write\n const toStderr = (...args: unknown[]): boolean =>\n (process.stderr.write as (...a: unknown[]) => boolean).apply(process.stderr, args)\n stdout.write = toStderr as typeof stdout.write\n // EPIPE 등은 wire 의 write 콜백으로 transport 에 전달된다. process.stdout 의 미처리 error 로 죽지 않게\n const ignore = (): void => {}\n stdout.on(\"error\", ignore)\n const wire = new Writable({\n write(chunk: Buffer | string, _encoding, callback) {\n const write = original as (chunk: Buffer | string, done: (error?: Error | null) => void) => boolean\n write.call(stdout, chunk, (error) => callback(error ?? null))\n },\n })\n let restored = false\n return {\n wire,\n restore() {\n if (restored) return\n restored = true\n if (stdout.write === (toStderr as typeof stdout.write)) stdout.write = original\n stdout.off(\"error\", ignore)\n },\n }\n}\n"]}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/mcp/stdio.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAGrF,OAAO,EAAE,cAAc,EAAkB,MAAM,iBAAiB,CAAA;AAIhE,OAAO,EAAE,sBAAsB,EAAuB,MAAM,aAAa,CAAA;AAkDzE;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,MAAM,IAAI,GAAG,sBAAsB,CAAC;QAClC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,cAAc,EAAE;QAChD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,SAAS,EAAE,OAAO;KACnB,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;IACnC,MAAM,MAAM,GAAG,CAAC,KAAc,EAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACrH,mEAAmE;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAA;IACxC,MAAM,OAAO,GAAqB,SAAS;QACzC,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACZ,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAc,CAAA;YAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAA;YACjE,OAAO,MAAM,CAAA;QACf,CAAC;QACH,CAAC,CAAC,IAAI,CAAA;IACR,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACzE,IAAI,aAA0B,CAAA;IAC9B,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC3C,aAAa,GAAG,OAAO,CAAA;IACzB,CAAC,CAAC,CAAA;IACF,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,IAAI,SAAS;YAAE,OAAM;QACrB,SAAS,GAAG,IAAI,CAAA;QAChB,KAAK,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IACxD,CAAC,CAAA;IACD,MAAM,SAAS,GAAG,GAAS,EAAE;QAC3B,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAA;QACpC,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE;YACvB,IAAI,CAAC;gBACH,UAAU,EAAE,EAAE,CAAA;YAChB,CAAC;oBAAS,CAAC;gBACT,MAAM,EAAE,CAAA;YACV,CAAC;QACH,CAAC,CAAA;IACH,CAAC,CAAA;IACD,IAAI,MAAkC,CAAA;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,YAAY,EAAE,CAAC,cAAc,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;IACnH,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,6DAA6D;QAC7D,0FAA0F;QAC1F,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAqC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAClH,MAAM,MAAM,GAAG,OAAoB,CAAA;YACnC,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;YAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;YAC/B,SAAS,EAAE,CAAA;YACX,OAAO,MAAM,CAAA;QACf,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YAC7B,MAAM,CAAC,KAAK,CAAC,CAAA;YACb,MAAM,EAAE,CAAA;QACV,CAAC,CAAC,CAAA;QACF,MAAM,GAAG;YACP,KAAK,CAAC,KAAK;gBACT,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBACjD,MAAM,MAAM,EAAE,KAAK,EAAE,CAAA;YACvB,CAAC;SACF,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;QACvG,0DAA0D;QAC1D,SAAS,EAAE,CAAA;IACb,CAAC;IACD,OAAO;QACL,MAAM;QACN,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,MAAM,EAAE,CAAA;YACR,MAAM,MAAM,CAAA;QACd,CAAC;KACF,CAAA;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,cAAc,GAAG,gBAAgB,CAAA;AAEvC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEvC,KAAK,UAAU,YAAY,CAAC,OAAmC,EAAE,MAAgC;IAC/F,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;QAAE,OAAM;IAC9B,IAAI,KAAgD,CAAA;IACpD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,CAAA;QAChE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;IAClE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnB,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,KAAK,CAAC,+CAA+C,kBAAkB,IAAI,CAAC,CAAC,CAAA;AACpH,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CACvB,MAAiB,EACjB,GAAyB,EACzB,SAA4B,EAC5B,MAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAA;IAC3B,IAAI,SAAuC,CAAA;IAC3C,IAAI,WAAwB,CAAA;IAC5B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACzC,WAAW,GAAG,OAAO,CAAA;IACvB,CAAC,CAAC,CAAA;IACF,IAAI,MAAM,GAAG,KAAK,CAAA;IAClB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqE,CAAA;IAC7F,0EAA0E;IAC1E,KAAK,CAAC,2BAA2B,GAAG,KAAK,EAAE,YAAY,EAAE,EAAE;QACzD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACjD,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,EAAE,CAA4B,CAAA;QACrE,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,CAAA;QACf,CAAC;IACH,CAAC,CAAA;IACD,MAAM,IAAI,GAAkB;QAC1B,GAAG;QACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAW,CAAC;QAChH,cAAc,CAAC,MAAM,EAAE,OAAO;YAC5B,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YAC7B,OAAO,GAAG,EAAE;gBACV,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,OAAO;oBAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC/D,CAAC,CAAA;QACH,CAAC;KACF,CAAA;IACD,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,SAAS,IAAI,MAAM;YAAE,OAAM;QAC/B,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;QACzD,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACzB,CAAC,CAAA;IACD,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAA;IACnC,KAAK,CAAC,OAAO,GAAG,GAAG,EAAE;QACnB,aAAa,EAAE,EAAE,CAAA;QACjB,IAAI,MAAM;YAAE,OAAM;QAClB,MAAM,GAAG,IAAI,CAAA;QACb,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,EAAE,CAAA;QACpC,SAAS;aACN,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACtB,IAAI,OAAO,OAAO,KAAK,UAAU;gBAAE,MAAM,OAAO,EAAE,CAAA;QACpD,CAAC,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC;aACX,KAAK,CAAC,MAAM,CAAC;aACb,OAAO,CAAC,WAAW,CAAC,CAAA;IACzB,CAAC,CAAA;IACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,CAAA;QACxC,KAAK,CAAC,aAAa,GAAG,GAAG,EAAE;YACzB,YAAY,EAAE,EAAE,CAAA;YAChB,KAAK,EAAE,CAAA;QACT,CAAC,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,CAAC,OAAO,GAAG,KAAK,EAAE,SAAS,EAAE,EAAE;YACnC,MAAM,OAAO,CAAC,SAAS,CAAC,CAAA;YACxB,KAAK,EAAE,CAAA;QACT,CAAC,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAA;IAC7B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAe,EAAW,EAAE,CAC9C,OAAO,CAAC,MAAM,CAAC,KAAsC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACpF,MAAM,CAAC,KAAK,GAAG,QAA+B,CAAA;IAC9C,kFAAkF;IAClF,MAAM,MAAM,GAAG,GAAS,EAAE,GAAE,CAAC,CAAA;IAC7B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC1B,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC;QACxB,KAAK,CAAC,KAAsB,EAAE,SAAS,EAAE,QAAQ;YAC/C,MAAM,KAAK,GAAG,QAAqF,CAAA;YACnG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAA;QAC/D,CAAC;KACF,CAAC,CAAA;IACF,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,OAAO;QACL,IAAI;QACJ,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,MAAM,CAAC,KAAK,KAAM,QAAgC;gBAAE,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAA;YAC/E,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC7B,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { Readable } from \"node:stream\"\nimport { Writable } from \"node:stream\"\nimport type { McpServer, McpServerFactory } from \"@modelcontextprotocol/server\"\nimport { serveStdio, StdioServerTransport } from \"@modelcontextprotocol/server/stdio\"\nimport type { MextApp } from \"../command.ts\"\nimport type { McpCapabilities, McpConnectHandler, McpConnection } from \"../config.ts\"\nimport { localPrincipal, type Principal } from \"../principal.ts\"\nimport type { Registry } from \"../registry.ts\"\nimport type { Instrumentation } from \"../instrumentation.ts\"\nimport type { ToolExposure } from \"./dispatch.ts\"\nimport { createMcpServerFactory, type McpCoreOptions } from \"./server.ts\"\n\nexport interface McpStdioOptions {\n registry: Registry\n app?: () => MextApp | Promise<MextApp>\n tools?: ToolExposure\n /** serverInfo.name. 기본 \"mext\" */\n name?: string\n /** initialize · server/discover 결과 instructions */\n instructions?: string\n /** 서버 capabilities 에 병합 (experimental) */\n capabilities?: McpCapabilities\n /**\n * 연결 하나에 1회 (2025 = notifications/initialized 후, 2026-07-28 = 연결 직후).\n * conn.notify = 요청과 무관한 서버 발신 알림. 반환 함수 = 연결 종료 시 cleanup. 예외 = onerror\n */\n onConnect?: McpConnectHandler\n /**\n * auto = 2026-07-28 · 2025 모두 (SDK serveStdio 가 판별). legacy = 2025 만:\n * server/discover = -32601 → 클라이언트가 initialize 로 재시도 (Claude Code channel 은 2025 연결에서만 전달).\n * 기본 = capabilities.experimental[\"claude/channel\"] 선언 시 legacy, 그 외 auto\n */\n era?: \"auto\" | \"legacy\"\n /** 연결 주체 (로컬 모드). 기본 localPrincipal() */\n principal?: Principal\n /** 기본 process.stdin */\n stdin?: Readable\n /**\n * MCP 메시지 출력. 기본 process.stdout.\n * D4: process.stdout 을 쓰는 경우 redirectStdout() 의 wire 를 넘겨 핸들러 출력이 메시지와 섞이지 않게 한다\n */\n stdout?: Writable\n /** 응답에 영향 없는 오류 보고 (SDK 내부 오류 · 깨진 메시지) */\n onerror?: (error: Error) => void\n /** MRTR requestState 서명 키 (createMcpServerFactory 와 같음) */\n requestStateKey?: McpCoreOptions[\"requestStateKey\"]\n /** 주기 progress 간격 (ms). 0 = 끔 */\n progressInterval?: number\n /** loadInstrumentation 결과 (onCommandError · span, transport = \"stdio\") */\n instrumentation?: Instrumentation\n /** _meta[\"dev.mext/manifestHash\"]. McpCoreOptions.manifestHash (기본 = registry 로 계산) */\n manifestHash?: string\n}\n\nexport interface McpStdioServer {\n /** stdin EOF · stdout 오류 · close() 로 연결이 끝나면 resolve */\n closed: Promise<void>\n close(): Promise<void>\n}\n\n/**\n * MCP stdio (2026-07-28 · 2025 레거시 모두 SDK serveStdio). 줄 단위 JSON-RPC, 연결 하나 = McpServer 하나.\n * notifications/cancelled → ctx.signal abort (SDK). stdin EOF = 연결 종료, 진행 중 요청은 abort 되고 응답하지 않는다.\n * 헤더 검증 · Origin · 바인딩 · Authorization 은 해당 없음. 주체 = 로컬 (kind local).\n */\nexport function serveMcpStdio(options: McpStdioOptions): McpStdioServer {\n const core = createMcpServerFactory({\n registry: options.registry,\n principal: options.principal ?? localPrincipal(),\n ...(options.app ? { app: options.app } : {}),\n ...(options.tools ? { tools: options.tools } : {}),\n ...(options.name ? { name: options.name } : {}),\n ...(options.instructions !== undefined ? { instructions: options.instructions } : {}),\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\n ...(options.requestStateKey !== undefined ? { requestStateKey: options.requestStateKey } : {}),\n ...(options.progressInterval !== undefined ? { progressInterval: options.progressInterval } : {}),\n ...(options.instrumentation ? { instrumentation: options.instrumentation } : {}),\n ...(options.manifestHash !== undefined ? { manifestHash: options.manifestHash } : {}),\n transport: \"stdio\",\n })\n const onConnect = options.onConnect\n const report = (error: unknown): void => options.onerror?.(error instanceof Error ? error : new Error(String(error)))\n /** 인스턴스별 정리 완료 (onConnect settle + cleanup). closed 는 이것들을 기다린다 */\n const settled = new Set<Promise<void>>()\n const factory: McpServerFactory = onConnect\n ? async (ctx) => {\n const server = (await core(ctx)) as McpServer\n settled.add(attachConnection(server, ctx.era, onConnect, report))\n return server\n }\n : core\n const transport = new StdioServerTransport(options.stdin, options.stdout)\n let resolveClosed!: () => void\n const closed = new Promise<void>((resolve) => {\n resolveClosed = resolve\n })\n let finishing = false\n const finish = (): void => {\n if (finishing) return\n finishing = true\n void waitCleanups(settled, report).then(resolveClosed)\n }\n const wrapClose = (): void => {\n const sdkOnClose = transport.onclose\n transport.onclose = () => {\n try {\n sdkOnClose?.()\n } finally {\n finish()\n }\n }\n }\n let handle: { close(): Promise<void> }\n const era = options.era ?? (options.capabilities?.experimental?.[CLAUDE_CHANNEL] !== undefined ? \"legacy\" : \"auto\")\n if (era === \"legacy\") {\n // serveStdio 는 server/discover 를 받으면 2026-07-28 로 고정한다 → 우회.\n // SDK 기본 supportedProtocolVersions = 2025 버전만 → McpServer 생성자가 server/discover 를 등록하지 않는다\n const ready = Promise.resolve(factory({ era: \"legacy\" } as Parameters<McpServerFactory>[0])).then(async (product) => {\n const server = product as McpServer\n server.server.onerror = report\n await server.connect(transport)\n wrapClose()\n return server\n })\n ready.catch((error: unknown) => {\n report(error)\n finish()\n })\n handle = {\n async close() {\n const server = await ready.catch(() => undefined)\n await server?.close()\n },\n }\n } else {\n handle = serveStdio(factory, options.onerror ? { transport, onerror: options.onerror } : { transport })\n // serveStdio 가 onclose 를 동기적으로 설정한 뒤 감싼다 (stdin 이벤트는 비동기)\n wrapClose()\n }\n return {\n closed,\n async close() {\n await handle.close()\n finish()\n await closed\n },\n }\n}\n\n/** Claude Code channel capability. 선언하면 stdio 기본 era = legacy */\nconst CLAUDE_CHANNEL = \"claude/channel\"\n\n/** 연결 종료 후 onConnect cleanup 대기 상한 (ms). 넘으면 오류 보고 후 종료 진행 */\nexport const CLEANUP_TIMEOUT_MS = 5_000\n\nasync function waitCleanups(settled: ReadonlySet<Promise<void>>, report: (error: unknown) => void): Promise<void> {\n if (settled.size === 0) return\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), CLEANUP_TIMEOUT_MS)\n timer.unref?.()\n })\n const result = await Promise.race([Promise.all(settled), timeout])\n clearTimeout(timer)\n if (result === \"timeout\") report(new Error(`mcp.onConnect cleanup did not finish within ${CLEANUP_TIMEOUT_MS}ms`))\n}\n\n/**\n * McpServer 인스턴스 하나 = stdio 연결 하나. onConnect 시점: 2025 = notifications/initialized\n * (initialize 응답 전 알림은 클라이언트가 버릴 수 있음), 2026-07-28 = connect 직후 (handshake 없음).\n * SDK 가 era 판별용 probe 인스턴스를 버리면 그 인스턴스의 onclose 로 cleanup 된다.\n * 반환 = 인스턴스 종료 후 (onConnect settle · cleanup 완료) resolve. 시작 전 종료 = 바로 resolve\n */\nfunction attachConnection(\n server: McpServer,\n era: McpConnection[\"era\"],\n onConnect: McpConnectHandler,\n report: (error: unknown) => void,\n): Promise<void> {\n const inner = server.server\n let connected: Promise<unknown> | undefined\n let resolveDone!: () => void\n const done = new Promise<void>((resolve) => {\n resolveDone = resolve\n })\n let closed = false\n const handlers = new Map<string, (params: Record<string, unknown>) => void | Promise<void>>()\n // SDK 가 등록하지 않은 method 만 fallback 으로 온다 (사양 알림은 SDK handler · era 필터가 먼저)\n inner.fallbackNotificationHandler = async (notification) => {\n const handler = handlers.get(notification.method)\n if (!handler) return\n const params = (notification.params ?? {}) as Record<string, unknown>\n try {\n await handler(params)\n } catch (error) {\n report(error)\n }\n }\n const conn: McpConnection = {\n era,\n notify: (method, params) => inner.notification({ method, ...(params !== undefined ? { params } : {}) } as never),\n onNotification(method, handler) {\n handlers.set(method, handler)\n return () => {\n if (handlers.get(method) === handler) handlers.delete(method)\n }\n },\n }\n const start = (): void => {\n if (connected || closed) return\n connected = Promise.resolve().then(() => onConnect(conn))\n connected.catch(report)\n }\n const previousClose = inner.onclose\n inner.onclose = () => {\n previousClose?.()\n if (closed) return\n closed = true\n if (!connected) return resolveDone()\n connected\n .then(async (cleanup) => {\n if (typeof cleanup === \"function\") await cleanup()\n }, () => {})\n .catch(report)\n .finally(resolveDone)\n }\n if (era === \"legacy\") {\n const previousInit = inner.oninitialized\n inner.oninitialized = () => {\n previousInit?.()\n start()\n }\n } else {\n const connect = server.connect.bind(server)\n server.connect = async (transport) => {\n await connect(transport)\n start()\n }\n }\n return done\n}\n\nexport interface StdoutRedirect {\n /** 원래 process.stdout 으로 쓰는 스트림. MCP transport 전용 */\n wire: Writable\n /** process.stdout.write 원복 */\n restore(): void\n}\n\n/**\n * D4: stdio 모드에서 transport 외의 stdout 쓰기 (console.log · process.stdout.write) 를 stderr 로 돌린다.\n * 반환된 wire 만 실제 stdout 에 쓴다. 명령어 모듈 import 전에 호출해야 top-level 출력도 막는다.\n * 자식 프로세스 · fd 1 직접 쓰기 (fs.writeSync(1)) 는 막지 못한다.\n */\nexport function redirectStdout(): StdoutRedirect {\n const stdout = process.stdout\n const original = stdout.write\n const toStderr = (...args: unknown[]): boolean =>\n (process.stderr.write as (...a: unknown[]) => boolean).apply(process.stderr, args)\n stdout.write = toStderr as typeof stdout.write\n // EPIPE 등은 wire 의 write 콜백으로 transport 에 전달된다. process.stdout 의 미처리 error 로 죽지 않게\n const ignore = (): void => {}\n stdout.on(\"error\", ignore)\n const wire = new Writable({\n write(chunk: Buffer | string, _encoding, callback) {\n const write = original as (chunk: Buffer | string, done: (error?: Error | null) => void) => boolean\n write.call(stdout, chunk, (error) => callback(error ?? null))\n },\n })\n let restored = false\n return {\n wire,\n restore() {\n if (restored) return\n restored = true\n if (stdout.write === (toStderr as typeof stdout.write)) stdout.write = original\n stdout.off(\"error\", ignore)\n },\n }\n}\n"]}
package/dist/start.d.ts CHANGED
@@ -11,6 +11,8 @@ import { type Registry } from "./registry.ts";
11
11
  * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.
12
12
  * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)
13
13
  */
14
+ export { mainLocalBin, runLocalBin } from "./local-bin.ts";
15
+ export type { LocalBinOptions } from "./local-bin.ts";
14
16
  export interface StartOptions {
15
17
  /** server.js · mext start 뒤 인자. --stdio · --port <n> · --host <h> */
16
18
  argv?: readonly string[];
@@ -60,4 +62,3 @@ interface StartArgs {
60
62
  }
61
63
  /** 반환 문자열 = 사용법 오류 */
62
64
  export declare function parseStartArgs(argv: readonly string[]): StartArgs | string;
63
- export {};
package/dist/start.js CHANGED
@@ -10,7 +10,7 @@ import { readFileSync } from "node:fs";
10
10
  import { resolve, sep } from "node:path";
11
11
  import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import { COMMAND, isCommand } from "./command.js";
13
- import { resolveLocalPrincipal } from "./config.js";
13
+ import { mcpDeclarations, mcpStdioDeclarations, resolveLocalPrincipal } from "./config.js";
14
14
  import { loadInstrumentation } from "./instrumentation.js";
15
15
  import { MANIFEST_SCHEMA_VERSION } from "./manifest.js";
16
16
  import { DEFAULT_HOST, DEFAULT_PORT } from "./mcp/constants.js";
@@ -18,6 +18,16 @@ import { createMcpFetchHandler, startMcpHttpServer } from "./mcp/http.js";
18
18
  import { redirectStdout, serveMcpStdio } from "./mcp/stdio.js";
19
19
  import { createLazyRegistry } from "./registry.js";
20
20
  import { mextVersion } from "./version.js";
21
+ /**
22
+ * @obov/mext/start: .mext/ (mext build 산출) 실행 런타임. 소스 스캔 · TypeScript 없음.
23
+ * 생성 파일 계약 (mext build 가 쓰는 server.js · handler.js, 바꾸면 기존 빌드가 깨진다):
24
+ * server.js process.exitCode = await startFromBuild(new URL(".", import.meta.url), { argv: process.argv.slice(2), env: process.env })
25
+ * handler.js export default createFetchHandler(new URL(".", import.meta.url))
26
+ *
27
+ * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.
28
+ * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)
29
+ */
30
+ export { mainLocalBin, runLocalBin } from "./local-bin.js";
21
31
  const USAGE = "Usage: mext start [--stdio] [--port <n>] [--host <h>] (node .mext/server.js, PORT · HOST environment variables)";
22
32
  /** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */
23
33
  export async function startFromBuild(dir, options = {}) {
@@ -42,6 +52,7 @@ export async function startFromBuild(dir, options = {}) {
42
52
  principal: await resolveLocalPrincipal(build.config),
43
53
  ...(build.config.app ? { app: build.config.app } : {}),
44
54
  ...(build.config.tools ? { tools: build.config.tools } : {}),
55
+ ...mcpStdioDeclarations(build.config),
45
56
  stdout: redirect.wire,
46
57
  onerror,
47
58
  });
@@ -199,6 +210,7 @@ function httpOptions(build, onerror) {
199
210
  ...(config.auth ? { auth: config.auth } : {}),
200
211
  ...(config.tools ? { tools: config.tools } : {}),
201
212
  ...(config.oauth ? { oauth: config.oauth } : {}),
213
+ ...mcpDeclarations(config),
202
214
  server: config.server?.allowedOrigins ? { allowedOrigins: config.server.allowedOrigins } : {},
203
215
  onerror,
204
216
  };