@obov/mext 0.0.0-stage → 0.1.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 (155) hide show
  1. package/dist/build-cli.d.ts +39 -0
  2. package/dist/build-cli.js +160 -0
  3. package/dist/build-cli.js.map +1 -0
  4. package/dist/build.d.ts +65 -0
  5. package/dist/build.js +252 -0
  6. package/dist/build.js.map +1 -0
  7. package/dist/cli/args.d.ts +107 -0
  8. package/dist/cli/args.js +621 -0
  9. package/dist/cli/args.js.map +1 -0
  10. package/dist/cli/bin.d.ts +2 -0
  11. package/dist/cli/bin.js +13 -0
  12. package/dist/cli/bin.js.map +1 -0
  13. package/dist/cli/build.d.ts +11 -0
  14. package/dist/cli/build.js +87 -0
  15. package/dist/cli/build.js.map +1 -0
  16. package/dist/cli/dev-lock.d.ts +23 -0
  17. package/dist/cli/dev-lock.js +68 -0
  18. package/dist/cli/dev-lock.js.map +1 -0
  19. package/dist/cli/dev-server.d.ts +20 -0
  20. package/dist/cli/dev-server.js +74 -0
  21. package/dist/cli/dev-server.js.map +1 -0
  22. package/dist/cli/dev.d.ts +21 -0
  23. package/dist/cli/dev.js +564 -0
  24. package/dist/cli/dev.js.map +1 -0
  25. package/dist/cli/help.d.ts +31 -0
  26. package/dist/cli/help.js +238 -0
  27. package/dist/cli/help.js.map +1 -0
  28. package/dist/cli/io.d.ts +26 -0
  29. package/dist/cli/io.js +74 -0
  30. package/dist/cli/io.js.map +1 -0
  31. package/dist/cli/login.d.ts +32 -0
  32. package/dist/cli/login.js +284 -0
  33. package/dist/cli/login.js.map +1 -0
  34. package/dist/cli/logout.d.ts +9 -0
  35. package/dist/cli/logout.js +105 -0
  36. package/dist/cli/logout.js.map +1 -0
  37. package/dist/cli/main.d.ts +19 -0
  38. package/dist/cli/main.js +77 -0
  39. package/dist/cli/main.js.map +1 -0
  40. package/dist/cli/mcp.d.ts +16 -0
  41. package/dist/cli/mcp.js +92 -0
  42. package/dist/cli/mcp.js.map +1 -0
  43. package/dist/cli/output.d.ts +34 -0
  44. package/dist/cli/output.js +172 -0
  45. package/dist/cli/output.js.map +1 -0
  46. package/dist/cli/remote.d.ts +17 -0
  47. package/dist/cli/remote.js +35 -0
  48. package/dist/cli/remote.js.map +1 -0
  49. package/dist/cli/run.d.ts +14 -0
  50. package/dist/cli/run.js +218 -0
  51. package/dist/cli/run.js.map +1 -0
  52. package/dist/cli/start.d.ts +8 -0
  53. package/dist/cli/start.js +37 -0
  54. package/dist/cli/start.js.map +1 -0
  55. package/dist/client/cache.d.ts +104 -0
  56. package/dist/client/cache.js +208 -0
  57. package/dist/client/cache.js.map +1 -0
  58. package/dist/client/main.d.ts +25 -0
  59. package/dist/client/main.js +100 -0
  60. package/dist/client/main.js.map +1 -0
  61. package/dist/client/manifest.d.ts +19 -0
  62. package/dist/client/manifest.js +68 -0
  63. package/dist/client/manifest.js.map +1 -0
  64. package/dist/client/runner.d.ts +49 -0
  65. package/dist/client/runner.js +562 -0
  66. package/dist/client/runner.js.map +1 -0
  67. package/dist/command.d.ts +75 -0
  68. package/dist/command.js +44 -0
  69. package/dist/command.js.map +1 -0
  70. package/dist/config.d.ts +53 -0
  71. package/dist/config.js +122 -0
  72. package/dist/config.js.map +1 -0
  73. package/dist/errors.d.ts +74 -0
  74. package/dist/errors.js +38 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/ids.d.ts +6 -0
  77. package/dist/ids.js +12 -0
  78. package/dist/ids.js.map +1 -0
  79. package/dist/index.d.ts +45 -0
  80. package/dist/index.js +27 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/instrumentation.d.ts +97 -0
  83. package/dist/instrumentation.js +165 -0
  84. package/dist/instrumentation.js.map +1 -0
  85. package/dist/invoke.d.ts +64 -0
  86. package/dist/invoke.js +152 -0
  87. package/dist/invoke.js.map +1 -0
  88. package/dist/lookup.d.ts +9 -0
  89. package/dist/lookup.js +27 -0
  90. package/dist/lookup.js.map +1 -0
  91. package/dist/manifest.d.ts +68 -0
  92. package/dist/manifest.js +77 -0
  93. package/dist/manifest.js.map +1 -0
  94. package/dist/mcp/constants.d.ts +9 -0
  95. package/dist/mcp/constants.js +10 -0
  96. package/dist/mcp/constants.js.map +1 -0
  97. package/dist/mcp/dispatch.d.ts +66 -0
  98. package/dist/mcp/dispatch.js +249 -0
  99. package/dist/mcp/dispatch.js.map +1 -0
  100. package/dist/mcp/help.d.ts +57 -0
  101. package/dist/mcp/help.js +136 -0
  102. package/dist/mcp/help.js.map +1 -0
  103. package/dist/mcp/http.d.ts +92 -0
  104. package/dist/mcp/http.js +366 -0
  105. package/dist/mcp/http.js.map +1 -0
  106. package/dist/mcp/index.d.ts +15 -0
  107. package/dist/mcp/index.js +9 -0
  108. package/dist/mcp/index.js.map +1 -0
  109. package/dist/mcp/install.d.ts +17 -0
  110. package/dist/mcp/install.js +36 -0
  111. package/dist/mcp/install.js.map +1 -0
  112. package/dist/mcp/mrtr.d.ts +82 -0
  113. package/dist/mcp/mrtr.js +130 -0
  114. package/dist/mcp/mrtr.js.map +1 -0
  115. package/dist/mcp/server.d.ts +58 -0
  116. package/dist/mcp/server.js +204 -0
  117. package/dist/mcp/server.js.map +1 -0
  118. package/dist/mcp/stdio.d.ts +57 -0
  119. package/dist/mcp/stdio.js +79 -0
  120. package/dist/mcp/stdio.js.map +1 -0
  121. package/dist/principal.d.ts +16 -0
  122. package/dist/principal.js +24 -0
  123. package/dist/principal.js.map +1 -0
  124. package/dist/registry.d.ts +90 -0
  125. package/dist/registry.js +169 -0
  126. package/dist/registry.js.map +1 -0
  127. package/dist/remote/browser.d.ts +15 -0
  128. package/dist/remote/browser.js +47 -0
  129. package/dist/remote/browser.js.map +1 -0
  130. package/dist/remote/client.d.ts +113 -0
  131. package/dist/remote/client.js +312 -0
  132. package/dist/remote/client.js.map +1 -0
  133. package/dist/remote/credentials.d.ts +48 -0
  134. package/dist/remote/credentials.js +196 -0
  135. package/dist/remote/credentials.js.map +1 -0
  136. package/dist/remote/index.d.ts +8 -0
  137. package/dist/remote/index.js +5 -0
  138. package/dist/remote/index.js.map +1 -0
  139. package/dist/remote/oauth.d.ts +186 -0
  140. package/dist/remote/oauth.js +507 -0
  141. package/dist/remote/oauth.js.map +1 -0
  142. package/dist/scan.d.ts +34 -0
  143. package/dist/scan.js +155 -0
  144. package/dist/scan.js.map +1 -0
  145. package/dist/start.d.ts +63 -0
  146. package/dist/start.js +267 -0
  147. package/dist/start.js.map +1 -0
  148. package/dist/suggest.d.ts +7 -0
  149. package/dist/suggest.js +45 -0
  150. package/dist/suggest.js.map +1 -0
  151. package/dist/version.d.ts +4 -0
  152. package/dist/version.js +13 -0
  153. package/dist/version.js.map +1 -0
  154. package/package.json +49 -4
  155. package/README.md +0 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoke.js","sourceRoot":"","sources":["../src/invoke.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from \"zod\"\nimport type { Ask, AskRequest, CommandContext, InputSchema, MextApp, ProgressUpdate } from \"./command.ts\"\nimport { inputDeclined, MextError, type ArgumentIssue } from \"./errors.ts\"\nimport { observeCommand, reportCommandError, type Instrumentation, type Transport } from \"./instrumentation.ts\"\nimport { findCommand } from \"./lookup.ts\"\nimport { hasScope, type Principal } from \"./principal.ts\"\nimport { loadCommand, type JsonSchema, type RegisteredCommand, type Registry } from \"./registry.ts\"\n\n/** 어댑터가 주입하는 ctx.ask 구현. command = 정식 ID */\nexport type AskHandler = (request: AskHandlerRequest) => Promise<unknown>\n\nexport type AskHandlerRequest =\n | { command: string; kind: \"confirm\"; message: string }\n | { command: string; kind: \"input\"; message: string; schema: InputSchema; requestedSchema: JsonSchema }\n\nexport interface InvokeOptions {\n principal: Principal\n /** 기본 = abort 되지 않는 signal */\n signal?: AbortSignal\n /** 기본 = 무시 */\n progress?: (update: ProgressUpdate) => void\n /** 기본 = ctx.ask 호출 시 INPUT_UNAVAILABLE */\n ask?: AskHandler\n app?: MextApp\n /** readOnly 명령어만 허용 (split 의 query 도구). 그 외 = PERMISSION_DENIED */\n readOnly?: boolean\n /** loadInstrumentation 결과. onCommandError · mext.command span. 기본 = 없음 */\n instrumentation?: Instrumentation\n /** 실행 어댑터 (onCommandError · span 속성). 기본 \"local\" */\n transport?: Transport\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate). span 의 부모 */\n traceContext?: Record<string, unknown>\n}\n\nexport interface InvokeResult {\n /** 정식 ID (별칭으로 호출해도) */\n command: string\n value: unknown\n}\n\n/**\n * 어댑터 공통 실행기: ID 해석 (별칭 포함) → 권한 → (lazy 면 모듈 import) → Zod safeParse 1회 → 확인 (requiresConfirmation) → execute.\n * 실패는 MextError (UNKNOWN_COMMAND · PERMISSION_DENIED · INVALID_ARGUMENT · INPUT_UNAVAILABLE · INPUT_DECLINED).\n * 핸들러 예외는 그대로 전파 (instrumentation 있으면 onCommandError 보고 후).\n * instrumentation.otel 있으면 findCommand 뒤 전체를 `mext.command <id>` span 으로 감싼다.\n */\nexport async function invoke(\n registry: Registry,\n id: string,\n rawInput: unknown,\n options: InvokeOptions,\n): Promise<InvokeResult> {\n const entry = findCommand(registry, id)\n return observeCommand(entry.id, options, () => options.signal, async () => {\n authorize(entry, options.principal, { readOnly: options.readOnly })\n // lazy Registry (mext start) = 여기서 명령어 모듈 import. 권한 · ID 오류는 import 없이\n const command = await loadCommand(entry)\n const input = await parseInput(entry, rawInput)\n const ctx = createContext(entry, options)\n ctx.signal.throwIfAborted()\n if (entry.command.requiresConfirmation) {\n // 확인은 ctx.ask 경유 (CLI = 프롬프트 · --yes, MCP = MRTR). ask 미주입 = INPUT_UNAVAILABLE\n const confirmed = await ctx.ask({ kind: \"confirm\", message: confirmationMessage(entry, input) })\n if (!confirmed) throw inputDeclined(entry.id, \"confirm\")\n ctx.signal.throwIfAborted()\n }\n try {\n const value = await command.execute(input, ctx)\n return { command: entry.id, value }\n } catch (error) {\n await reportCommandError(error, entry.id, options, ctx.signal)\n throw error\n }\n })\n}\n\n/** ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage). 구현 = lookup.ts (help · 고객 CLI 공용) */\nexport { findCommand }\n\n/**\n * 안전성 메타데이터를 주체에 대해 강제.\n * readOnly 전용 호출 = readOnly 명령어만 · anonymous = readOnly 명령어만 · command.scopes 전부 보유 (\"*\" = 전체).\n */\nexport function authorize(entry: RegisteredCommand, principal: Principal, options: { readOnly?: boolean } = {}): void {\n const c = entry.command\n const deny = (reason: \"anonymous\" | \"read_only\" | \"scope\", message: string): never =>\n throwError(\n new MextError(\"PERMISSION_DENIED\", message, {\n command: entry.id,\n reason,\n required_scopes: [...c.scopes],\n held_scopes: [...principal.scopes],\n }),\n )\n if (options.readOnly && !c.readOnly) deny(\"read_only\", `${entry.id} is not read-only and cannot run here.`)\n if (principal.kind === \"anonymous\" && !c.readOnly) {\n deny(\"anonymous\", `${entry.id} is not read-only and requires an authenticated principal.`)\n }\n const missing = c.scopes.filter((s) => !hasScope(principal, s))\n if (missing.length > 0) deny(\"scope\", `${entry.id} requires scopes: ${missing.join(\", \")}.`)\n}\n\n/** Zod safeParse 1회. 실패 = INVALID_ARGUMENT (missing_fields · issues · expected_schema · examples) */\nexport async function parseInput(entry: RegisteredCommand, rawInput: unknown): Promise<z.output<InputSchema>> {\n const raw = rawInput === undefined ? {} : rawInput\n const result = await (await loadCommand(entry)).input.safeParseAsync(raw)\n if (result.success) return result.data\n throw invalidArgument(entry.id, raw, result.error.issues, entry.inputSchema, [...entry.command.examples])\n}\n\n/** ctx 생성. ask · progress · signal 은 어댑터 주입 */\nexport function createContext(entry: RegisteredCommand, options: InvokeOptions): CommandContext {\n const handler = options.ask\n const ask = async (request: AskRequest): Promise<unknown> => {\n if (!handler) {\n throw new MextError(\"INPUT_UNAVAILABLE\", `${entry.id} requested input, but this environment cannot ask.`, {\n command: entry.id,\n })\n }\n if (request.kind === \"confirm\") {\n return (await handler({ command: entry.id, kind: \"confirm\", message: request.message })) === true\n }\n const requestedSchema = z.toJSONSchema(request.schema, { target: \"draft-2020-12\", io: \"input\" }) as JsonSchema\n const response = await handler({\n command: entry.id,\n kind: \"input\",\n message: request.message,\n schema: request.schema,\n requestedSchema,\n })\n const parsed = await request.schema.safeParseAsync(response)\n if (parsed.success) return parsed.data\n throw invalidArgument(entry.id, response, parsed.error.issues, requestedSchema, [])\n }\n return {\n principal: options.principal,\n signal: options.signal ?? new AbortController().signal,\n progress: options.progress ?? (() => {}),\n ask: ask as Ask,\n app: options.app ?? ({} as MextApp),\n }\n}\n\n/** requiresConfirmation 확인 문구. 검증된 입력을 포함 (무엇을 승인하는지) */\nexport function confirmationMessage(entry: RegisteredCommand, input: unknown): string {\n let args: string\n try {\n args = JSON.stringify(input) ?? \"{}\"\n } catch {\n args = \"{}\"\n }\n return `Run ${entry.id} with ${args}?${entry.command.destructive ? \" This command is destructive.\" : \"\"}`\n}\n\nfunction invalidArgument(\n command: string,\n raw: unknown,\n zodIssues: readonly z.core.$ZodIssue[],\n expectedSchema: JsonSchema,\n examples: unknown[],\n): MextError<\"INVALID_ARGUMENT\"> {\n const issues: ArgumentIssue[] = zodIssues.map((i) => ({\n path: i.path.map(String).join(\".\"),\n code: i.code,\n message: i.message,\n }))\n const missing = new Set<string>()\n for (const i of zodIssues) {\n if (i.code === \"invalid_type\" && i.path.length > 0 && isAbsent(raw, i.path)) missing.add(i.path.map(String).join(\".\"))\n }\n const missing_fields = [...missing]\n const summary =\n missing_fields.length > 0\n ? `missing required: ${missing_fields.join(\", \")}`\n : issues.map((i) => (i.path ? `${i.path}: ${i.message}` : i.message)).join(\"; \")\n return new MextError(\"INVALID_ARGUMENT\", `Invalid arguments for ${command}: ${summary}`, {\n command,\n missing_fields,\n issues,\n expected_schema: expectedSchema,\n examples,\n })\n}\n\n/** 원본 입력에 경로가 없는지 (값 undefined 포함) */\nfunction isAbsent(raw: unknown, path: readonly PropertyKey[]): boolean {\n let cur: unknown = raw\n for (const key of path) {\n if (cur === null || typeof cur !== \"object\") return true\n cur = (cur as Record<PropertyKey, unknown>)[key]\n if (cur === undefined) return true\n }\n return false\n}\n\nfunction throwError(error: MextError): never {\n throw error\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { MextError } from "./errors.ts";
2
+ import type { RegisteredCommand, Registry } from "./registry.ts";
3
+ /**
4
+ * ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage).
5
+ * Zod · 실행 코드에 의존하지 않는다 (help 렌더러 · 고객 CLI 번들이 import)
6
+ */
7
+ export declare function findCommand(registry: Registry, id: string): RegisteredCommand;
8
+ /** UNKNOWN_COMMAND. candidates = [비교 이름, 정식 ID], usage = 존재하는 가장 가까운 상위 네임스페이스의 help */
9
+ export declare function unknownCommand(id: string, candidates: Iterable<readonly [string, string]>, hasNamespace: (id: string) => boolean): MextError<"UNKNOWN_COMMAND">;
package/dist/lookup.js ADDED
@@ -0,0 +1,27 @@
1
+ import { MextError } from "./errors.js";
2
+ import { parentNamespace } from "./ids.js";
3
+ import { closest } from "./suggest.js";
4
+ /**
5
+ * ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage).
6
+ * Zod · 실행 코드에 의존하지 않는다 (help 렌더러 · 고객 CLI 번들이 import)
7
+ */
8
+ export function findCommand(registry, id) {
9
+ const entry = registry.commands.get(id) ?? registry.commands.get(registry.aliases.get(id) ?? "");
10
+ if (entry)
11
+ return entry;
12
+ throw unknownCommand(id, [
13
+ ...[...registry.commands.keys()].map((c) => [c, c]),
14
+ ...[...registry.aliases].map(([alias, c]) => [alias, c]),
15
+ ], (ns) => registry.namespaces.has(ns));
16
+ }
17
+ /** UNKNOWN_COMMAND. candidates = [비교 이름, 정식 ID], usage = 존재하는 가장 가까운 상위 네임스페이스의 help */
18
+ export function unknownCommand(id, candidates, hasNamespace) {
19
+ const suggested = closest(id, candidates);
20
+ let ns = id;
21
+ while (ns !== "" && !hasNamespace(ns))
22
+ ns = parentNamespace(ns);
23
+ const usage = ns === "" ? "help" : `help ${ns}`;
24
+ const hint = suggested.length > 0 ? ` Did you mean: ${suggested.join(", ")}?` : "";
25
+ return new MextError("UNKNOWN_COMMAND", `Unknown command "${id}".${hint}`, { command: id, suggested, usage });
26
+ }
27
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAkB,EAAE,EAAU;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IAChG,IAAI,KAAK;QAAE,OAAO,KAAK,CAAA;IACvB,MAAM,cAAc,CAClB,EAAE,EACF;QACE,GAAG,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrE,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;KAC3E,EACD,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CACpC,CAAA;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,cAAc,CAC5B,EAAU,EACV,UAA+C,EAC/C,YAAqC;IAErC,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;IACzC,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAAE,EAAE,GAAG,eAAe,CAAC,EAAE,CAAC,CAAA;IAC/D,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAClF,OAAO,IAAI,SAAS,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,KAAK,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAC/G,CAAC","sourcesContent":["import { MextError } from \"./errors.ts\"\nimport { parentNamespace } from \"./ids.ts\"\nimport type { RegisteredCommand, Registry } from \"./registry.ts\"\nimport { closest } from \"./suggest.ts\"\n\n/**\n * ID · 별칭 → 명령어. 없으면 UNKNOWN_COMMAND (suggested · usage).\n * Zod · 실행 코드에 의존하지 않는다 (help 렌더러 · 고객 CLI 번들이 import)\n */\nexport function findCommand(registry: Registry, id: string): RegisteredCommand {\n const entry = registry.commands.get(id) ?? registry.commands.get(registry.aliases.get(id) ?? \"\")\n if (entry) return entry\n throw unknownCommand(\n id,\n [\n ...[...registry.commands.keys()].map((c): [string, string] => [c, c]),\n ...[...registry.aliases].map(([alias, c]): [string, string] => [alias, c]),\n ],\n (ns) => registry.namespaces.has(ns),\n )\n}\n\n/** UNKNOWN_COMMAND. candidates = [비교 이름, 정식 ID], usage = 존재하는 가장 가까운 상위 네임스페이스의 help */\nexport function unknownCommand(\n id: string,\n candidates: Iterable<readonly [string, string]>,\n hasNamespace: (id: string) => boolean,\n): MextError<\"UNKNOWN_COMMAND\"> {\n const suggested = closest(id, candidates)\n let ns = id\n while (ns !== \"\" && !hasNamespace(ns)) ns = parentNamespace(ns)\n const usage = ns === \"\" ? \"help\" : `help ${ns}`\n const hint = suggested.length > 0 ? ` Did you mean: ${suggested.join(\", \")}?` : \"\"\n return new MextError(\"UNKNOWN_COMMAND\", `Unknown command \"${id}\".${hint}`, { command: id, suggested, usage })\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import type { JsonSchema, RegisteredCommand, Registry } from "./registry.ts";
2
+ export declare const MANIFEST_SCHEMA_VERSION = 1;
3
+ export interface ManifestCommand {
4
+ id: string;
5
+ description: string;
6
+ /** 별칭 전체 ID */
7
+ aliases: string[];
8
+ /** commands/ 기준 상대 경로 (posix) */
9
+ file?: string;
10
+ readOnly: boolean;
11
+ destructive: boolean;
12
+ requiresConfirmation: boolean;
13
+ scopes: string[];
14
+ positional: string[];
15
+ examples: unknown[];
16
+ /** JSON Schema 2020-12 */
17
+ inputSchema: JsonSchema;
18
+ outputSchema?: JsonSchema;
19
+ }
20
+ export interface ManifestNamespace {
21
+ id: string;
22
+ /** _index.md 내용 */
23
+ description?: string;
24
+ }
25
+ export interface Manifest {
26
+ schemaVersion: typeof MANIFEST_SCHEMA_VERSION;
27
+ /** commands/_index.md 내용 */
28
+ description?: string;
29
+ /** 루트 제외, ID 순 */
30
+ namespaces: ManifestNamespace[];
31
+ /** ID 순 */
32
+ commands: ManifestCommand[];
33
+ }
34
+ /** 명령어 하나의 메타데이터 (manifest · help 공통) */
35
+ export declare function describeCommand(entry: RegisteredCommand): ManifestCommand;
36
+ export declare function buildManifest(registry: Registry): Manifest;
37
+ /** 빌드 시 manifest 생성: commands/ 스캔 → Manifest (JSON 직렬화 가능) */
38
+ export declare function generateManifest(dir: string): Promise<Manifest>;
39
+ /** mext build 의 .mext/manifest.json 명령어: Manifest 명령어 + 번들 모듈 경로 */
40
+ export interface BuildManifestCommand extends ManifestCommand {
41
+ /** .mext/ 기준 상대 경로 (posix). commands/users/get.ts → "commands/users/get.js". default export = defineCommand 결과 */
42
+ module: string;
43
+ }
44
+ /**
45
+ * .mext/manifest.json (mext build 산출, mext start · handler.js 가 읽는다).
46
+ * namespaces · description = Manifest 와 같음
47
+ */
48
+ export interface BuildManifest extends Omit<Manifest, "commands"> {
49
+ /** 빌드한 @obov/mext 버전. 실행 시 설치된 버전과 다르면 시작 오류 */
50
+ mextVersion: string;
51
+ /** 빌드 식별자 (D3: mextVersion · manifestHash · 번들 코드의 sha256 앞 16자, 같은 입력 = 같은 값) */
52
+ buildId: string;
53
+ /** manifestHash(commands) */
54
+ manifestHash: string;
55
+ /** mext.config.ts 번들 (.mext/ 기준). 설정 파일이 없으면 생략 */
56
+ config?: string;
57
+ /** instrumentation.ts 번들 (.mext/ 기준). 없으면 생략 */
58
+ instrumentation?: string;
59
+ /** ID 순 */
60
+ commands: BuildManifestCommand[];
61
+ }
62
+ /**
63
+ * 명령어 계약의 지문: ID · 별칭 · positional · 입력 스키마 · 안전성 메타데이터 (readOnly · destructive ·
64
+ * requiresConfirmation · scopes) 의 정규화 JSON (키 정렬, ID 순) sha256 hex.
65
+ * 설명 · examples · outputSchema · 파일 경로는 제외 (클라이언트의 help 캐시 · 플래그 파싱에 영향 없음).
66
+ * 빌드 manifest · 서버 (_meta["dev.mext/manifestHash"]) · 클라이언트가 같은 함수로 계산한다
67
+ */
68
+ export declare function manifestHash(commands: readonly ManifestCommand[]): string;
@@ -0,0 +1,77 @@
1
+ import { createHash } from "node:crypto";
2
+ import { loadRegistry } from "./scan.js";
3
+ export const MANIFEST_SCHEMA_VERSION = 1;
4
+ /** 명령어 하나의 메타데이터 (manifest · help 공통) */
5
+ export function describeCommand(entry) {
6
+ const c = entry.command;
7
+ const out = {
8
+ id: entry.id,
9
+ description: c.description,
10
+ aliases: [...entry.aliases],
11
+ readOnly: c.readOnly,
12
+ destructive: c.destructive,
13
+ requiresConfirmation: c.requiresConfirmation,
14
+ scopes: [...c.scopes],
15
+ positional: [...c.positional],
16
+ examples: [...c.examples],
17
+ inputSchema: entry.inputSchema,
18
+ };
19
+ if (entry.file !== undefined)
20
+ out.file = entry.file;
21
+ if (entry.outputSchema !== undefined)
22
+ out.outputSchema = entry.outputSchema;
23
+ return out;
24
+ }
25
+ export function buildManifest(registry) {
26
+ const manifest = {
27
+ schemaVersion: MANIFEST_SCHEMA_VERSION,
28
+ namespaces: [],
29
+ commands: [...registry.commands.values()].map(describeCommand),
30
+ };
31
+ for (const ns of registry.namespaces.values()) {
32
+ if (ns.id === "") {
33
+ if (ns.description !== undefined)
34
+ manifest.description = ns.description;
35
+ continue;
36
+ }
37
+ manifest.namespaces.push(ns.description === undefined ? { id: ns.id } : { id: ns.id, description: ns.description });
38
+ }
39
+ return manifest;
40
+ }
41
+ /** 빌드 시 manifest 생성: commands/ 스캔 → Manifest (JSON 직렬화 가능) */
42
+ export async function generateManifest(dir) {
43
+ return buildManifest(await loadRegistry(dir));
44
+ }
45
+ /**
46
+ * 명령어 계약의 지문: ID · 별칭 · positional · 입력 스키마 · 안전성 메타데이터 (readOnly · destructive ·
47
+ * requiresConfirmation · scopes) 의 정규화 JSON (키 정렬, ID 순) sha256 hex.
48
+ * 설명 · examples · outputSchema · 파일 경로는 제외 (클라이언트의 help 캐시 · 플래그 파싱에 영향 없음).
49
+ * 빌드 manifest · 서버 (_meta["dev.mext/manifestHash"]) · 클라이언트가 같은 함수로 계산한다
50
+ */
51
+ export function manifestHash(commands) {
52
+ const normalized = [...commands]
53
+ .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
54
+ .map((c) => ({
55
+ id: c.id,
56
+ aliases: [...c.aliases].sort(),
57
+ positional: c.positional,
58
+ inputSchema: c.inputSchema,
59
+ readOnly: c.readOnly,
60
+ destructive: c.destructive,
61
+ requiresConfirmation: c.requiresConfirmation,
62
+ scopes: [...c.scopes].sort(),
63
+ }));
64
+ return createHash("sha256").update(canonicalJson(normalized)).digest("hex");
65
+ }
66
+ /** 키 정렬 JSON (배열 순서 유지, undefined 속성 생략) */
67
+ function canonicalJson(value) {
68
+ if (value === null || typeof value !== "object")
69
+ return JSON.stringify(value) ?? "null";
70
+ if (Array.isArray(value))
71
+ return `[${value.map((v) => (v === undefined ? "null" : canonicalJson(v))).join(",")}]`;
72
+ const entries = Object.entries(value)
73
+ .filter(([, v]) => v !== undefined)
74
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
75
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
76
+ }
77
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\"\nimport type { JsonSchema, RegisteredCommand, Registry } from \"./registry.ts\"\nimport { loadRegistry } from \"./scan.ts\"\n\nexport const MANIFEST_SCHEMA_VERSION = 1\n\nexport interface ManifestCommand {\n id: string\n description: string\n /** 별칭 전체 ID */\n aliases: string[]\n /** commands/ 기준 상대 경로 (posix) */\n file?: string\n readOnly: boolean\n destructive: boolean\n requiresConfirmation: boolean\n scopes: string[]\n positional: string[]\n examples: unknown[]\n /** JSON Schema 2020-12 */\n inputSchema: JsonSchema\n outputSchema?: JsonSchema\n}\n\nexport interface ManifestNamespace {\n id: string\n /** _index.md 내용 */\n description?: string\n}\n\nexport interface Manifest {\n schemaVersion: typeof MANIFEST_SCHEMA_VERSION\n /** commands/_index.md 내용 */\n description?: string\n /** 루트 제외, ID 순 */\n namespaces: ManifestNamespace[]\n /** ID 순 */\n commands: ManifestCommand[]\n}\n\n/** 명령어 하나의 메타데이터 (manifest · help 공통) */\nexport function describeCommand(entry: RegisteredCommand): ManifestCommand {\n const c = entry.command\n const out: ManifestCommand = {\n id: entry.id,\n description: c.description,\n aliases: [...entry.aliases],\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n scopes: [...c.scopes],\n positional: [...c.positional],\n examples: [...c.examples],\n inputSchema: entry.inputSchema,\n }\n if (entry.file !== undefined) out.file = entry.file\n if (entry.outputSchema !== undefined) out.outputSchema = entry.outputSchema\n return out\n}\n\nexport function buildManifest(registry: Registry): Manifest {\n const manifest: Manifest = {\n schemaVersion: MANIFEST_SCHEMA_VERSION,\n namespaces: [],\n commands: [...registry.commands.values()].map(describeCommand),\n }\n for (const ns of registry.namespaces.values()) {\n if (ns.id === \"\") {\n if (ns.description !== undefined) manifest.description = ns.description\n continue\n }\n manifest.namespaces.push(ns.description === undefined ? { id: ns.id } : { id: ns.id, description: ns.description })\n }\n return manifest\n}\n\n/** 빌드 시 manifest 생성: commands/ 스캔 → Manifest (JSON 직렬화 가능) */\nexport async function generateManifest(dir: string): Promise<Manifest> {\n return buildManifest(await loadRegistry(dir))\n}\n\n/** mext build 의 .mext/manifest.json 명령어: Manifest 명령어 + 번들 모듈 경로 */\nexport interface BuildManifestCommand extends ManifestCommand {\n /** .mext/ 기준 상대 경로 (posix). commands/users/get.ts → \"commands/users/get.js\". default export = defineCommand 결과 */\n module: string\n}\n\n/**\n * .mext/manifest.json (mext build 산출, mext start · handler.js 가 읽는다).\n * namespaces · description = Manifest 와 같음\n */\nexport interface BuildManifest extends Omit<Manifest, \"commands\"> {\n /** 빌드한 @obov/mext 버전. 실행 시 설치된 버전과 다르면 시작 오류 */\n mextVersion: string\n /** 빌드 식별자 (D3: mextVersion · manifestHash · 번들 코드의 sha256 앞 16자, 같은 입력 = 같은 값) */\n buildId: string\n /** manifestHash(commands) */\n manifestHash: string\n /** mext.config.ts 번들 (.mext/ 기준). 설정 파일이 없으면 생략 */\n config?: string\n /** instrumentation.ts 번들 (.mext/ 기준). 없으면 생략 */\n instrumentation?: string\n /** ID 순 */\n commands: BuildManifestCommand[]\n}\n\n/**\n * 명령어 계약의 지문: ID · 별칭 · positional · 입력 스키마 · 안전성 메타데이터 (readOnly · destructive ·\n * requiresConfirmation · scopes) 의 정규화 JSON (키 정렬, ID 순) sha256 hex.\n * 설명 · examples · outputSchema · 파일 경로는 제외 (클라이언트의 help 캐시 · 플래그 파싱에 영향 없음).\n * 빌드 manifest · 서버 (_meta[\"dev.mext/manifestHash\"]) · 클라이언트가 같은 함수로 계산한다\n */\nexport function manifestHash(commands: readonly ManifestCommand[]): string {\n const normalized = [...commands]\n .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))\n .map((c) => ({\n id: c.id,\n aliases: [...c.aliases].sort(),\n positional: c.positional,\n inputSchema: c.inputSchema,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n scopes: [...c.scopes].sort(),\n }))\n return createHash(\"sha256\").update(canonicalJson(normalized)).digest(\"hex\")\n}\n\n/** 키 정렬 JSON (배열 순서 유지, undefined 속성 생략) */\nfunction canonicalJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\"\n if (Array.isArray(value)) return `[${value.map((v) => (v === undefined ? \"null\" : canonicalJson(v))).join(\",\")}]`\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(\",\")}}`\n}\n"]}
@@ -0,0 +1,9 @@
1
+ /** MCP 엔드포인트 경로 (POST 만) */
2
+ export declare const MCP_PATH = "/mcp";
3
+ export declare const DEFAULT_HOST = "127.0.0.1";
4
+ export declare const DEFAULT_PORT = 3100;
5
+ /**
6
+ * server/discover · tools/call 결과 _meta 의 현재 manifestHash 키 (스키마 drift 감지, §5).
7
+ * 고객 CLI 번들도 import (의존 없는 모듈)
8
+ */
9
+ export declare const MANIFEST_HASH_META_KEY = "dev.mext/manifestHash";
@@ -0,0 +1,10 @@
1
+ /** MCP 엔드포인트 경로 (POST 만) */
2
+ export const MCP_PATH = "/mcp";
3
+ export const DEFAULT_HOST = "127.0.0.1";
4
+ export const DEFAULT_PORT = 3100;
5
+ /**
6
+ * server/discover · tools/call 결과 _meta 의 현재 manifestHash 키 (스키마 drift 감지, §5).
7
+ * 고객 CLI 번들도 import (의존 없는 모듈)
8
+ */
9
+ export const MANIFEST_HASH_META_KEY = "dev.mext/manifestHash";
10
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/mcp/constants.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAA;AAC9B,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAA;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;AAEhC;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAA","sourcesContent":["/** MCP 엔드포인트 경로 (POST 만) */\nexport const MCP_PATH = \"/mcp\"\nexport const DEFAULT_HOST = \"127.0.0.1\"\nexport const DEFAULT_PORT = 3100\n\n/**\n * server/discover · tools/call 결과 _meta 의 현재 manifestHash 키 (스키마 drift 감지, §5).\n * 고객 CLI 번들도 import (의존 없는 모듈)\n */\nexport const MANIFEST_HASH_META_KEY = \"dev.mext/manifestHash\"\n"]}
@@ -0,0 +1,66 @@
1
+ import { type InvokeOptions } from "../invoke.ts";
2
+ import type { JsonSchema, Registry } from "../registry.ts";
3
+ import { type InputRequiredResult, type InputRound, type RequestStateSigner } from "./mrtr.ts";
4
+ /**
5
+ * MCP 어댑터의 transport 무관 부분: dispatcher 도구 정의 · tools/call 실행 · help · 오류 → 결과 변환.
6
+ * SDK · HTTP 에 의존하지 않는다 (HTTP · stdio 가 공유).
7
+ */
8
+ /** execute = 도구 1개 (기본), split = query (readOnly · help) + mutate (그 외) */
9
+ export type ToolExposure = "execute" | "split";
10
+ export type DispatcherToolName = "execute" | "query" | "mutate";
11
+ /** MCP ToolAnnotations 중 mext 가 쓰는 항목 */
12
+ export interface DispatcherAnnotations {
13
+ title?: string;
14
+ readOnlyHint: boolean;
15
+ destructiveHint?: boolean;
16
+ idempotentHint?: boolean;
17
+ openWorldHint?: boolean;
18
+ }
19
+ export interface DispatcherTool {
20
+ name: DispatcherToolName;
21
+ description: string;
22
+ /** JSON Schema 2020-12. command 에 x-mcp-header: "Command" */
23
+ inputSchema: JsonSchema;
24
+ annotations: DispatcherAnnotations;
25
+ /** readOnly 명령어만 실행 (split 의 query). 그 외 = PERMISSION_DENIED */
26
+ readOnly: boolean;
27
+ }
28
+ export interface TextContent {
29
+ type: "text";
30
+ text: string;
31
+ }
32
+ /** MCP CallToolResult 와 같은 형태 (SDK 타입 비의존) */
33
+ export interface ToolCallResult {
34
+ content: TextContent[];
35
+ /** 성공 = 핸들러 반환값 (undefined 는 null), 오류 = { error: MextError.toJSON() } */
36
+ structuredContent: unknown;
37
+ isError?: true;
38
+ }
39
+ /** 핸들러 예외 (MextError 외) 의 결과 code. CLI --json 과 같음 */
40
+ export declare const EXECUTION_ERROR = "EXECUTION_ERROR";
41
+ /** 노출 방식별 dispatcher 도구 (정렬 순서 고정: execute · query, mutate) */
42
+ export declare function dispatcherTools(registry: Registry, exposure?: ToolExposure): DispatcherTool[];
43
+ export declare function findDispatcherTool(tools: readonly DispatcherTool[], name: string): DispatcherTool | undefined;
44
+ export interface DispatchOptions extends Omit<InvokeOptions, "readOnly"> {
45
+ /** help 의 tool 표시용. dispatcherTools 결과 */
46
+ tools?: readonly DispatcherTool[];
47
+ /**
48
+ * MRTR (MCP 2026-07-28). 지정하면 ctx.ask = InputRequiredResult 반환 · 재시도의 inputResponses 로 resolve
49
+ * (ask 옵션 무시). requiresConfirmation 확인도 이 경로
50
+ */
51
+ input?: InputRound & {
52
+ signer: RequestStateSigner;
53
+ };
54
+ }
55
+ /** tools/call 결과: 완료 (CallToolResult 형태) 또는 입력 요청 (InputRequiredResult) */
56
+ export type DispatchResult = ToolCallResult | InputRequiredResult;
57
+ /**
58
+ * dispatcher 도구 호출: { command, args } → help 또는 invoke.
59
+ * MextError · 핸들러 예외는 isError 결과로 (throw 하지 않음). 인증 (UNAUTHENTICATED → 401) 은 transport 몫.
60
+ * options.input 이 있으면 ctx.ask 가 InputRequiredResult 를 돌려준다 (mrtr.ts).
61
+ */
62
+ export declare function callDispatcher(registry: Registry, tool: DispatcherTool, rawArguments: unknown, options: DispatchOptions): Promise<DispatchResult>;
63
+ /** 오류 → isError 결과. MextError = toJSON, 그 외 = EXECUTION_ERROR */
64
+ export declare function errorResult(error: unknown): ToolCallResult;
65
+ /** dispatcher 도구 inputSchema. command 를 Mcp-Param-Command 헤더로 복사 (SEP-2243) */
66
+ export declare function dispatcherInputSchema(): JsonSchema;
@@ -0,0 +1,249 @@
1
+ import { isMextError, MextError } from "../errors.js";
2
+ import { invoke } from "../invoke.js";
3
+ import { formatHelp, help, HELP_COMMAND } from "./help.js";
4
+ import { createAskSession, hasPending, inputRequiredResult, } from "./mrtr.js";
5
+ /** 핸들러 예외 (MextError 외) 의 결과 code. CLI --json 과 같음 */
6
+ export const EXECUTION_ERROR = "EXECUTION_ERROR";
7
+ /** 도구 설명에 나열할 명령어 수 상한. 넘으면 help 안내만 */
8
+ const DESCRIPTION_COMMAND_LIMIT = 50;
9
+ /** 노출 방식별 dispatcher 도구 (정렬 순서 고정: execute · query, mutate) */
10
+ export function dispatcherTools(registry, exposure = "execute") {
11
+ const all = [...registry.commands.values()];
12
+ if (exposure === "split") {
13
+ const reads = all.filter((e) => e.command.readOnly);
14
+ const writes = all.filter((e) => !e.command.readOnly);
15
+ return [
16
+ {
17
+ name: "query",
18
+ description: describeTool("query", "Run a read-only command.", reads, 'Commands that are not read-only are rejected here; call them with the "mutate" tool.'),
19
+ inputSchema: dispatcherInputSchema(),
20
+ annotations: { title: "Query", readOnlyHint: true, destructiveHint: false, idempotentHint: true },
21
+ readOnly: true,
22
+ },
23
+ {
24
+ name: "mutate",
25
+ description: describeTool("mutate", "Run a command that changes state.", writes),
26
+ inputSchema: dispatcherInputSchema(),
27
+ annotations: { title: "Mutate", readOnlyHint: false, destructiveHint: true },
28
+ readOnly: false,
29
+ },
30
+ ];
31
+ }
32
+ const readOnly = all.every((e) => e.command.readOnly);
33
+ return [
34
+ {
35
+ name: "execute",
36
+ description: describeTool("execute", "Run a command.", all),
37
+ inputSchema: dispatcherInputSchema(),
38
+ // 단일 dispatcher 는 최악값 (researches/others.md 9)
39
+ annotations: readOnly
40
+ ? { title: "Execute", readOnlyHint: true, destructiveHint: false }
41
+ : { title: "Execute", readOnlyHint: false, destructiveHint: all.some((e) => e.command.destructive) },
42
+ readOnly: false,
43
+ },
44
+ ];
45
+ }
46
+ export function findDispatcherTool(tools, name) {
47
+ return tools.find((t) => t.name === name);
48
+ }
49
+ /**
50
+ * dispatcher 도구 호출: { command, args } → help 또는 invoke.
51
+ * MextError · 핸들러 예외는 isError 결과로 (throw 하지 않음). 인증 (UNAUTHENTICATED → 401) 은 transport 몫.
52
+ * options.input 이 있으면 ctx.ask 가 InputRequiredResult 를 돌려준다 (mrtr.ts).
53
+ */
54
+ export async function callDispatcher(registry, tool, rawArguments, options) {
55
+ try {
56
+ const { command, args } = parseDispatcherArguments(tool, rawArguments);
57
+ if (command === HELP_COMMAND) {
58
+ const target = args?.target;
59
+ if (target !== undefined && typeof target !== "string") {
60
+ throw dispatcherArgumentError(tool, "help", "args.target must be a string", [], {
61
+ path: "args.target",
62
+ code: "invalid_type",
63
+ message: "expected string",
64
+ });
65
+ }
66
+ const toolFor = splitToolFor(options.tools);
67
+ const value = help(registry, target, toolFor ? { toolFor } : {});
68
+ return { content: [{ type: "text", text: formatHelp(value, tool.name) }], structuredContent: value };
69
+ }
70
+ const { input, tools: _tools, ...invokeOptions } = options;
71
+ const result = input
72
+ ? await invokeRound(registry, tool, command, args, invokeOptions, input)
73
+ : await invoke(registry, command, args, { ...invokeOptions, readOnly: tool.readOnly });
74
+ if ("resultType" in result)
75
+ return result;
76
+ const value = result.value === undefined ? null : result.value;
77
+ return { content: [{ type: "text", text: stringify(value) }], structuredContent: value };
78
+ }
79
+ catch (error) {
80
+ return errorResult(error);
81
+ }
82
+ }
83
+ /**
84
+ * MRTR 한 라운드: requestState 검증 → ask 세션으로 invoke. 답 없는 ask 가 있으면 (핸들러가 오류를 삼켜도)
85
+ * 결과를 버리고 InputRequiredResult. elicitation 미선언 클라이언트 = ask 미주입 (INPUT_UNAVAILABLE)
86
+ */
87
+ async function invokeRound(registry, tool, command, args, options, input) {
88
+ const { ask: _ask, ...rest } = options;
89
+ if (!input.elicitation)
90
+ return invoke(registry, command, args, { ...rest, readOnly: tool.readOnly });
91
+ const binding = {
92
+ principal: `${options.principal.kind}:${options.principal.id}`,
93
+ tool: tool.name,
94
+ command,
95
+ args: args ?? {},
96
+ };
97
+ let answers = { ...input.inputResponses };
98
+ if (input.requestState !== undefined) {
99
+ const prior = input.signer.verify(input.requestState, binding);
100
+ if (!prior)
101
+ throw invalidRequestState(tool, command);
102
+ answers = { ...answers, ...prior };
103
+ }
104
+ const session = createAskSession(answers);
105
+ const signal = options.signal ? AbortSignal.any([options.signal, session.signal]) : session.signal;
106
+ try {
107
+ const result = await invoke(registry, command, args, { ...rest, signal, ask: session.ask, readOnly: tool.readOnly });
108
+ return hasPending(session) ? inputRequiredResult(session, input.signer, binding) : result;
109
+ }
110
+ catch (error) {
111
+ if (hasPending(session))
112
+ return inputRequiredResult(session, input.signer, binding);
113
+ throw error;
114
+ }
115
+ }
116
+ /** 서명 · 만료 · 바인딩 검증 실패한 requestState. 처음부터 다시 호출하도록 안내 */
117
+ function invalidRequestState(tool, command) {
118
+ return new MextError("INVALID_ARGUMENT", `Invalid arguments for ${command}: requestState is invalid or expired. Call ${tool.name} again without requestState and inputResponses.`, {
119
+ command,
120
+ missing_fields: [],
121
+ issues: [
122
+ {
123
+ path: "requestState",
124
+ code: "invalid_request_state",
125
+ message: "requestState failed verification (tampered, expired, or for a different call or server key)",
126
+ },
127
+ ],
128
+ expected_schema: tool.inputSchema,
129
+ examples: [],
130
+ });
131
+ }
132
+ /** 오류 → isError 결과. MextError = toJSON, 그 외 = EXECUTION_ERROR */
133
+ export function errorResult(error) {
134
+ const body = isMextError(error)
135
+ ? error.toJSON()
136
+ : { code: EXECUTION_ERROR, message: error instanceof Error ? error.message : String(error) };
137
+ return {
138
+ content: [{ type: "text", text: `${body.code}: ${body.message}\n${stringify(body)}` }],
139
+ structuredContent: { error: body },
140
+ isError: true,
141
+ };
142
+ }
143
+ /** dispatcher 도구 inputSchema. command 를 Mcp-Param-Command 헤더로 복사 (SEP-2243) */
144
+ export function dispatcherInputSchema() {
145
+ return {
146
+ type: "object",
147
+ properties: {
148
+ command: {
149
+ type: "string",
150
+ description: 'Command ID (e.g. "users.get"), or "help" to list namespaces and commands.',
151
+ "x-mcp-header": "Command",
152
+ },
153
+ args: {
154
+ type: "object",
155
+ description: 'Command input. For "help": { "target": "<namespace or command ID>" }.',
156
+ additionalProperties: true,
157
+ },
158
+ },
159
+ required: ["command"],
160
+ additionalProperties: false,
161
+ };
162
+ }
163
+ function parseDispatcherArguments(tool, raw) {
164
+ const input = raw === undefined ? {} : raw;
165
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
166
+ throw dispatcherArgumentError(tool, tool.name, "arguments must be an object", [], {
167
+ path: "",
168
+ code: "invalid_type",
169
+ message: "expected object",
170
+ });
171
+ }
172
+ const { command, args, ...rest } = input;
173
+ if (command === undefined) {
174
+ throw dispatcherArgumentError(tool, tool.name, "missing required: command", ["command"], {
175
+ path: "command",
176
+ code: "invalid_type",
177
+ message: "expected string, received undefined",
178
+ });
179
+ }
180
+ if (typeof command !== "string") {
181
+ throw dispatcherArgumentError(tool, tool.name, "command must be a string", [], {
182
+ path: "command",
183
+ code: "invalid_type",
184
+ message: "expected string",
185
+ });
186
+ }
187
+ const unknownKeys = Object.keys(rest);
188
+ if (unknownKeys.length > 0) {
189
+ throw dispatcherArgumentError(tool, tool.name, `unrecognized keys: ${unknownKeys.join(", ")}`, [], {
190
+ path: "",
191
+ code: "unrecognized_keys",
192
+ message: `Unrecognized keys: ${unknownKeys.join(", ")}. Put command input under "args".`,
193
+ });
194
+ }
195
+ if (args !== undefined && (args === null || typeof args !== "object" || Array.isArray(args))) {
196
+ throw dispatcherArgumentError(tool, command, "args must be an object", [], {
197
+ path: "args",
198
+ code: "invalid_type",
199
+ message: "expected object",
200
+ });
201
+ }
202
+ return { command, args };
203
+ }
204
+ function dispatcherArgumentError(tool, command, summary, missing, issue) {
205
+ return new MextError("INVALID_ARGUMENT", `Invalid arguments for ${command}: ${summary}`, {
206
+ command,
207
+ missing_fields: missing,
208
+ issues: [issue],
209
+ expected_schema: tool.inputSchema,
210
+ examples: [{ command: HELP_COMMAND }],
211
+ });
212
+ }
213
+ function splitToolFor(tools) {
214
+ if (!tools || tools.length < 2)
215
+ return undefined;
216
+ const query = tools.find((t) => t.readOnly)?.name;
217
+ const mutate = tools.find((t) => !t.readOnly)?.name;
218
+ if (query === undefined || mutate === undefined)
219
+ return undefined;
220
+ return (e) => (e.command.readOnly ? query : mutate);
221
+ }
222
+ function describeTool(name, lead, commands, note) {
223
+ const lines = [
224
+ lead,
225
+ `Arguments: { "command": "<command ID>", "args": { ...input } }. ` +
226
+ `Call ${name}({ "command": "help" }) to list namespaces and commands, ` +
227
+ `${name}({ "command": "help", "args": { "target": "<namespace or command ID>" } }) for input schemas and examples.`,
228
+ ];
229
+ if (note)
230
+ lines.push(note);
231
+ if (commands.length > 0 && commands.length <= DESCRIPTION_COMMAND_LIMIT) {
232
+ lines.push("", "Commands:");
233
+ for (const e of commands)
234
+ lines.push(`- ${e.id}: ${firstLine(e.command.description)}`);
235
+ }
236
+ return lines.join("\n");
237
+ }
238
+ function firstLine(text) {
239
+ return text.split("\n", 1)[0].trim();
240
+ }
241
+ function stringify(value) {
242
+ try {
243
+ return JSON.stringify(value, null, 2) ?? "null";
244
+ }
245
+ catch {
246
+ return String(value);
247
+ }
248
+ }
249
+ //# sourceMappingURL=dispatch.js.map