@obov/mext 0.2.0 → 0.3.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { isAbsolute, join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { isMextError } from \"./errors.ts\"\nimport type { Principal } from \"./principal.ts\"\n\n/**\n * instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).\n * register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.\n * span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).\n */\n\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n\n/** 핸들러를 실행한 어댑터. 원격 CLI 는 핸들러를 실행하지 않으므로 없다 */\nexport type Transport = \"http\" | \"stdio\" | \"local\"\n\nexport interface CommandErrorInfo {\n /** 정식 명령어 ID */\n command: string\n transport: Transport\n /** scopes · claims 는 넘기지 않는다 */\n principal: { id: string; kind: Principal[\"kind\"] }\n}\n\n/** instrumentation.ts 의 export */\nexport interface InstrumentationModule {\n register?: () => unknown\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n}\n\n/** 어댑터가 InvokeOptions.instrumentation 으로 넘기는 로드 결과 */\nexport interface Instrumentation {\n /** 핸들러 예외 (MextError 외, 취소 · MRTR 대기 제외). 자체 예외는 stderr 로 보고하고 응답은 바꾸지 않는다 */\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n /** @opentelemetry/api. undefined = span 없음 */\n otel?: OtelApi\n /** onCommandError 의 예외 보고. 기본 stderr */\n onerror?: (error: unknown) => void\n}\n\nexport interface LoadInstrumentationOptions {\n /** @opentelemetry/api 로더 (테스트 주입). 기본 = 동적 import, 결과는 프로세스당 캐시 */\n importOtel?: () => Promise<unknown>\n onerror?: (error: unknown) => void\n}\n\n/** mext 가 쓰는 @opentelemetry/api 의 부분 (타입 의존 없이) */\nexport interface OtelApi {\n trace: { getTracer(name: string, version?: string): OtelTracer }\n context: { active(): unknown }\n propagation: { extract(context: unknown, carrier: unknown): unknown }\n SpanStatusCode: { ERROR: number }\n}\n\ninterface OtelTracer {\n startActiveSpan<T>(name: string, options: { attributes: Record<string, string> }, context: unknown, fn: (span: OtelSpan) => T): T\n}\n\ninterface OtelSpan {\n setAttribute(key: string, value: string): unknown\n setStatus(status: { code: number; message?: string }): unknown\n recordException(exception: unknown): unknown\n end(): void\n}\n\n/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */\nexport function findInstrumentation(cwd: string = process.cwd()): string | undefined {\n const file = join(resolve(cwd), INSTRUMENTATION_FILE)\n return existsSync(file) ? file : undefined\n}\n\n/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */\nexport function loadProjectInstrumentation(\n cwd: string = process.cwd(),\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n return loadInstrumentation(findInstrumentation(cwd), options)\n}\n\nconst loaded = new Map<string, Promise<InstrumentationModule>>()\nconst loadedModules = new WeakMap<object, Promise<InstrumentationModule>>()\n\n/**\n * instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.\n * 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).\n * source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.\n */\nexport async function loadInstrumentation(\n source?: string | URL | InstrumentationModule,\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n const [mod, otel] = await Promise.all([\n source === undefined ? Promise.resolve<InstrumentationModule>({}) : registerOnce(source),\n options.importOtel ? resolveOtel(options.importOtel) : defaultOtel(),\n ])\n return {\n ...(mod.onCommandError ? { onCommandError: mod.onCommandError } : {}),\n ...(otel ? { otel } : {}),\n ...(options.onerror ? { onerror: options.onerror } : {}),\n }\n}\n\nfunction registerOnce(source: string | URL | InstrumentationModule): Promise<InstrumentationModule> {\n if (typeof source === \"string\" || source instanceof URL) {\n const isPath = typeof source === \"string\" && (isAbsolute(source) || !/^[a-z][a-z0-9+.-]+:/i.test(source))\n const href = isPath ? pathToFileURL(resolve(source as string)).href : String(source)\n return memo(loaded, href, () => import(href).then((m: unknown) => register(m, href)))\n }\n return memo(loadedModules, source, () => register(source, \"instrumentation\"))\n}\n\nfunction memo<K>(\n cache: { get(k: K): Promise<InstrumentationModule> | undefined; set(k: K, v: Promise<InstrumentationModule>): unknown; delete(k: K): unknown },\n key: K,\n load: () => Promise<InstrumentationModule>,\n): Promise<InstrumentationModule> {\n let pending = cache.get(key)\n if (!pending) {\n pending = load().catch((error: unknown) => {\n cache.delete(key)\n throw error\n })\n cache.set(key, pending)\n }\n return pending\n}\n\nasync function register(value: unknown, label: string): Promise<InstrumentationModule> {\n const mod = (value ?? {}) as Record<string, unknown>\n for (const key of [\"register\", \"onCommandError\"]) {\n if (mod[key] !== undefined && typeof mod[key] !== \"function\") {\n throw new TypeError(`${label}: export ${key} must be a function`)\n }\n }\n const hooks = mod as InstrumentationModule\n await hooks.register?.()\n return hooks\n}\n\n/* @opentelemetry/api (선택 peer). 번들러가 해석 · 포함하지 않도록 specifier 는 변수 (R5) */\n\nconst OTEL_API = \"@opentelemetry/api\"\nlet otelDefault: Promise<OtelApi | undefined> | undefined\n\nfunction defaultOtel(): Promise<OtelApi | undefined> {\n otelDefault ??= resolveOtel(() => import(OTEL_API))\n return otelDefault\n}\n\nasync function resolveOtel(importer: () => Promise<unknown>): Promise<OtelApi | undefined> {\n try {\n const mod = (await importer()) as Partial<OtelApi> & { default?: Partial<OtelApi> }\n const api = mod.trace ? mod : mod.default\n return api?.trace && api.context && api.propagation && api.SpanStatusCode ? (api as OtelApi) : undefined\n } catch {\n return undefined\n }\n}\n\n/* invoke 에서 사용 */\n\nconst TRACER_NAME = \"@obov/mext\"\n\nexport interface ObserveOptions {\n instrumentation?: Instrumentation\n transport?: Transport\n principal: Principal\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate) */\n traceContext?: Record<string, unknown>\n}\n\n/**\n * 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).\n * 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.\n * 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError 메시지는 입력 일부를 담을 수 있어 제외).\n * 취소 (signal abort · MRTR 대기) = status 없음.\n */\nexport function observeCommand<T>(\n command: string,\n options: ObserveOptions,\n signal: () => AbortSignal | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n const otel = options.instrumentation?.otel\n if (!otel) return fn()\n const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active()\n const attributes = {\n \"mext.command.id\": command,\n \"mext.transport\": options.transport ?? \"local\",\n \"mext.principal.kind\": options.principal.kind,\n }\n const tracer = otel.trace.getTracer(TRACER_NAME)\n return tracer.startActiveSpan(`mext.command ${command}`, { attributes }, parent, async (span) => {\n try {\n return await fn()\n } catch (error) {\n if (!signal()?.aborted) {\n if (isMextError(error)) {\n span.setAttribute(\"mext.error.code\", error.code)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error.code })\n } else {\n span.setAttribute(\"mext.error.code\", \"EXECUTION_ERROR\")\n span.recordException(error)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) })\n }\n }\n throw error\n } finally {\n span.end()\n }\n })\n}\n\n/**\n * 핸들러 예외 → onCommandError (await). MextError · 취소 · MRTR 대기는 보고하지 않는다.\n * onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.\n */\nexport async function reportCommandError(\n error: unknown,\n command: string,\n options: ObserveOptions,\n signal: AbortSignal | undefined,\n): Promise<void> {\n const hook = options.instrumentation?.onCommandError\n if (!hook || isMextError(error) || signal?.aborted) return\n const info: CommandErrorInfo = {\n command,\n transport: options.transport ?? \"local\",\n principal: { id: options.principal.id, kind: options.principal.kind },\n }\n try {\n await hook(error, info)\n } catch (hookError) {\n const onerror = options.instrumentation?.onerror ?? defaultOnError\n try {\n onerror(hookError)\n } catch {\n // 보고는 응답을 바꾸지 않는다\n }\n }\n}\n\nfunction defaultOnError(error: unknown): void {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error)\n process.stderr.write(`mext: onCommandError threw: ${message}\\n`)\n}\n"]}
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../src/instrumentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\"\nimport { isAbsolute, join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { isDeclaredError } from \"./errors.ts\"\nimport type { Principal } from \"./principal.ts\"\n\n/**\n * instrumentation.ts (프로젝트 루트, 선택) · OpenTelemetry span (§11).\n * register() = 프로세스당 1회, 첫 명령어 실행 전. onCommandError = 핸들러 예외 보고.\n * span = @opentelemetry/api (선택 peer) 가 있을 때만. 없으면 no-op (실패한 import 1회 캐시).\n */\n\nexport const INSTRUMENTATION_FILE = \"instrumentation.ts\"\n\n/** 핸들러를 실행한 어댑터. 원격 CLI 는 핸들러를 실행하지 않으므로 없다 */\nexport type Transport = \"http\" | \"stdio\" | \"local\"\n\nexport interface CommandErrorInfo {\n /** 정식 명령어 ID */\n command: string\n transport: Transport\n /** scopes · claims 는 넘기지 않는다 */\n principal: { id: string; kind: Principal[\"kind\"] }\n}\n\n/** instrumentation.ts 의 export */\nexport interface InstrumentationModule {\n register?: () => unknown\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n}\n\n/** 어댑터가 InvokeOptions.instrumentation 으로 넘기는 로드 결과 */\nexport interface Instrumentation {\n /** 핸들러 예외 (MextError 외, 취소 · MRTR 대기 제외). 자체 예외는 stderr 로 보고하고 응답은 바꾸지 않는다 */\n onCommandError?: (error: unknown, info: CommandErrorInfo) => unknown\n /** @opentelemetry/api. undefined = span 없음 */\n otel?: OtelApi\n /** onCommandError 의 예외 보고. 기본 stderr */\n onerror?: (error: unknown) => void\n}\n\nexport interface LoadInstrumentationOptions {\n /** @opentelemetry/api 로더 (테스트 주입). 기본 = 동적 import, 결과는 프로세스당 캐시 */\n importOtel?: () => Promise<unknown>\n onerror?: (error: unknown) => void\n}\n\n/** mext 가 쓰는 @opentelemetry/api 의 부분 (타입 의존 없이) */\nexport interface OtelApi {\n trace: { getTracer(name: string, version?: string): OtelTracer }\n context: { active(): unknown }\n propagation: { extract(context: unknown, carrier: unknown): unknown }\n SpanStatusCode: { ERROR: number }\n}\n\ninterface OtelTracer {\n startActiveSpan<T>(name: string, options: { attributes: Record<string, string> }, context: unknown, fn: (span: OtelSpan) => T): T\n}\n\ninterface OtelSpan {\n setAttribute(key: string, value: string): unknown\n setStatus(status: { code: number; message?: string }): unknown\n recordException(exception: unknown): unknown\n end(): void\n}\n\n/** cwd 의 instrumentation.ts 절대 경로. 없으면 undefined */\nexport function findInstrumentation(cwd: string = process.cwd()): string | undefined {\n const file = join(resolve(cwd), INSTRUMENTATION_FILE)\n return existsSync(file) ? file : undefined\n}\n\n/** findInstrumentation(cwd) → loadInstrumentation. mext dev · mext run (로컬) · stdio 용 */\nexport function loadProjectInstrumentation(\n cwd: string = process.cwd(),\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n return loadInstrumentation(findInstrumentation(cwd), options)\n}\n\nconst loaded = new Map<string, Promise<InstrumentationModule>>()\nconst loadedModules = new WeakMap<object, Promise<InstrumentationModule>>()\n\n/**\n * instrumentation 모듈 (파일 경로 · URL · import 된 모듈) 을 프로세스당 1회 import 하고 register() 를 await.\n * 같은 모듈의 두 번째 호출 = register 재호출 없이 같은 hooks. register 실패 = reject (다음 호출에서 재시도).\n * source 없음 = hooks 없이 span 만. 빌드 산출물 (.mext/server/instrumentation.js) 도 같은 함수.\n */\nexport async function loadInstrumentation(\n source?: string | URL | InstrumentationModule,\n options: LoadInstrumentationOptions = {},\n): Promise<Instrumentation> {\n const [mod, otel] = await Promise.all([\n source === undefined ? Promise.resolve<InstrumentationModule>({}) : registerOnce(source),\n options.importOtel ? resolveOtel(options.importOtel) : defaultOtel(),\n ])\n return {\n ...(mod.onCommandError ? { onCommandError: mod.onCommandError } : {}),\n ...(otel ? { otel } : {}),\n ...(options.onerror ? { onerror: options.onerror } : {}),\n }\n}\n\nfunction registerOnce(source: string | URL | InstrumentationModule): Promise<InstrumentationModule> {\n if (typeof source === \"string\" || source instanceof URL) {\n const isPath = typeof source === \"string\" && (isAbsolute(source) || !/^[a-z][a-z0-9+.-]+:/i.test(source))\n const href = isPath ? pathToFileURL(resolve(source as string)).href : String(source)\n return memo(loaded, href, () => import(href).then((m: unknown) => register(m, href)))\n }\n return memo(loadedModules, source, () => register(source, \"instrumentation\"))\n}\n\nfunction memo<K>(\n cache: { get(k: K): Promise<InstrumentationModule> | undefined; set(k: K, v: Promise<InstrumentationModule>): unknown; delete(k: K): unknown },\n key: K,\n load: () => Promise<InstrumentationModule>,\n): Promise<InstrumentationModule> {\n let pending = cache.get(key)\n if (!pending) {\n pending = load().catch((error: unknown) => {\n cache.delete(key)\n throw error\n })\n cache.set(key, pending)\n }\n return pending\n}\n\nasync function register(value: unknown, label: string): Promise<InstrumentationModule> {\n const mod = (value ?? {}) as Record<string, unknown>\n for (const key of [\"register\", \"onCommandError\"]) {\n if (mod[key] !== undefined && typeof mod[key] !== \"function\") {\n throw new TypeError(`${label}: export ${key} must be a function`)\n }\n }\n const hooks = mod as InstrumentationModule\n await hooks.register?.()\n return hooks\n}\n\n/* @opentelemetry/api (선택 peer). 번들러가 해석 · 포함하지 않도록 specifier 는 변수 (R5) */\n\nconst OTEL_API = \"@opentelemetry/api\"\nlet otelDefault: Promise<OtelApi | undefined> | undefined\n\nfunction defaultOtel(): Promise<OtelApi | undefined> {\n otelDefault ??= resolveOtel(() => import(OTEL_API))\n return otelDefault\n}\n\nasync function resolveOtel(importer: () => Promise<unknown>): Promise<OtelApi | undefined> {\n try {\n const mod = (await importer()) as Partial<OtelApi> & { default?: Partial<OtelApi> }\n const api = mod.trace ? mod : mod.default\n return api?.trace && api.context && api.propagation && api.SpanStatusCode ? (api as OtelApi) : undefined\n } catch {\n return undefined\n }\n}\n\n/* invoke 에서 사용 */\n\nconst TRACER_NAME = \"@obov/mext\"\n\nexport interface ObserveOptions {\n instrumentation?: Instrumentation\n transport?: Transport\n principal: Principal\n /** W3C trace context carrier (MCP params._meta 의 traceparent · tracestate) */\n traceContext?: Record<string, unknown>\n}\n\n/**\n * 명령어 실행 1회를 `mext.command <id>` span 으로 감싼다 (otel 있을 때만, 없으면 fn 그대로).\n * 속성 = mext.command.id · mext.transport · mext.principal.kind. 인자 · 결과 · 토큰은 기록하지 않는다.\n * 실패: status ERROR + mext.error.code. 핸들러 예외만 recordException (MextError · CommandError 메시지는 입력 일부를 담을 수 있어 제외).\n * 취소 (signal abort · MRTR 대기) = status 없음.\n */\nexport function observeCommand<T>(\n command: string,\n options: ObserveOptions,\n signal: () => AbortSignal | undefined,\n fn: () => Promise<T>,\n): Promise<T> {\n const otel = options.instrumentation?.otel\n if (!otel) return fn()\n const parent = options.traceContext ? otel.propagation.extract(otel.context.active(), options.traceContext) : otel.context.active()\n const attributes = {\n \"mext.command.id\": command,\n \"mext.transport\": options.transport ?? \"local\",\n \"mext.principal.kind\": options.principal.kind,\n }\n const tracer = otel.trace.getTracer(TRACER_NAME)\n return tracer.startActiveSpan(`mext.command ${command}`, { attributes }, parent, async (span) => {\n try {\n return await fn()\n } catch (error) {\n if (!signal()?.aborted) {\n if (isDeclaredError(error)) {\n span.setAttribute(\"mext.error.code\", error.code)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error.code })\n } else {\n span.setAttribute(\"mext.error.code\", \"EXECUTION_ERROR\")\n span.recordException(error)\n span.setStatus({ code: otel.SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error) })\n }\n }\n throw error\n } finally {\n span.end()\n }\n })\n}\n\n/**\n * 핸들러 예외 → onCommandError (await). MextError · CommandError · 취소 · MRTR 대기는 보고하지 않는다.\n * onCommandError 자체의 예외 = onerror (기본 stderr), 호출자에게 전파하지 않는다.\n */\nexport async function reportCommandError(\n error: unknown,\n command: string,\n options: ObserveOptions,\n signal: AbortSignal | undefined,\n): Promise<void> {\n const hook = options.instrumentation?.onCommandError\n if (!hook || isDeclaredError(error) || signal?.aborted) return\n const info: CommandErrorInfo = {\n command,\n transport: options.transport ?? \"local\",\n principal: { id: options.principal.id, kind: options.principal.kind },\n }\n try {\n await hook(error, info)\n } catch (hookError) {\n const onerror = options.instrumentation?.onerror ?? defaultOnError\n try {\n onerror(hookError)\n } catch {\n // 보고는 응답을 바꾸지 않는다\n }\n }\n}\n\nfunction defaultOnError(error: unknown): void {\n const message = error instanceof Error ? (error.stack ?? error.message) : String(error)\n process.stderr.write(`mext: onCommandError threw: ${message}\\n`)\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import { type CliIo } from "./cli/io.ts";
2
+ /**
3
+ * 로컬 bin (cli.local, §5). .mext/bin.js 가 호출하는 런타임 (@obov/mext/start).
4
+ * 생성 파일 계약 (mext build 가 쓰는 bin.js, 바꾸면 기존 빌드가 깨진다):
5
+ * await mainLocalBin(new URL(".", import.meta.url), { bin, version, errors? })
6
+ * 명령: <bin> help · <bin> <ns> <cmd> [flags] · <bin> mcp serve · <bin> mcp install · <bin> --version.
7
+ * 명령어 실행 = mext run (로컬) 과 같은 코어 (cli/run.ts runLocalCommand), Registry = .mext/manifest.json (소스 · TypeScript 없음)
8
+ */
9
+ export interface LocalBinOptions {
10
+ /** 실행 파일 이름 (cli.bin) */
11
+ bin: string;
12
+ /** --version (cli.version > 도입 프로젝트 package.json version) */
13
+ version: string;
14
+ /** .mext/ 기준 cli.errors 번들 모듈 (예: "cli-errors.js") */
15
+ errors?: string;
16
+ }
17
+ /** 로컬 bin 실행. argv = 실행 파일 뒤 인자. 반환 = exit code */
18
+ export declare function runLocalBin(dir: string | URL, options: LocalBinOptions, argv: readonly string[], partialIo?: Partial<CliIo>): Promise<number>;
19
+ /** 프로세스 진입점 (bin.js). 첫 SIGINT = ctx.signal abort (exit 130), 두 번째 = 즉시 종료 */
20
+ export declare function mainLocalBin(dir: string | URL, options: LocalBinOptions): Promise<void>;
@@ -0,0 +1,108 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { resolve } from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+ import { table } from "./cli/help.js";
12
+ import { resolveIo } from "./cli/io.js";
13
+ import { EXIT } from "./cli/output.js";
14
+ import { runLocalCommand } from "./cli/run.js";
15
+ import { mcpInstallText } from "./mcp/install.js";
16
+ import { loadBuild, startFromBuild } from "./start.js";
17
+ /** 로컬 bin 실행. argv = 실행 파일 뒤 인자. 반환 = exit code */
18
+ export async function runLocalBin(dir, options, argv, partialIo = {}) {
19
+ const io = resolveIo(partialIo);
20
+ const [first, ...rest] = argv;
21
+ if (first === "--version" || first === "-v") {
22
+ io.stdout.write(`${options.version}\n`);
23
+ return EXIT.ok;
24
+ }
25
+ if (first === "mcp")
26
+ return mcp(dir, options, rest, io);
27
+ return runLocalCommand(argv, io, buildSource(dir, options));
28
+ }
29
+ /** 프로세스 진입점 (bin.js). 첫 SIGINT = ctx.signal abort (exit 130), 두 번째 = 즉시 종료 */
30
+ export async function mainLocalBin(dir, options) {
31
+ const controller = new AbortController();
32
+ process.on("SIGINT", () => {
33
+ if (controller.signal.aborted)
34
+ process.exit(130);
35
+ controller.abort();
36
+ });
37
+ const code = await runLocalBin(dir, options, process.argv.slice(2), { signal: controller.signal });
38
+ process.stdout.write("", () => process.stderr.write("", () => process.exit(code)));
39
+ }
40
+ /** .mext/ → 로컬 실행 코어 입력. CLI 필드 = 번들된 명령어의 Zod 입력 분석 (mext run 과 같음) */
41
+ function buildSource(dir, options) {
42
+ return {
43
+ prog: options.bin,
44
+ footer: builtinHelp(options.bin),
45
+ async load(setFormatter) {
46
+ const build = await loadBuild(dir);
47
+ if (options.errors !== undefined)
48
+ setFormatter(await loadFormatter(dir, options.errors));
49
+ return { instrumentation: build.instrumentation, config: build.config, registry: build.registry };
50
+ },
51
+ };
52
+ }
53
+ async function loadFormatter(dir, file) {
54
+ const href = typeof dir === "string" ? pathToFileURL(resolve(dir, file)).href : new URL(file, dir.href.endsWith("/") ? dir : `${dir.href}/`).href;
55
+ const mod = (await import(__rewriteRelativeImportExtension(href)));
56
+ if (typeof mod.default !== "function")
57
+ throw new TypeError(`${file}: default export must be a function (CliErrorFormatter)`);
58
+ return mod.default;
59
+ }
60
+ async function mcp(dir, options, argv, io) {
61
+ const { bin } = options;
62
+ const [sub, ...rest] = argv;
63
+ const isHelp = (t) => t === "--help" || t === "-h";
64
+ const usage = [
65
+ "Usage:",
66
+ ...table([
67
+ [`${bin} mcp serve`, "Run the MCP server over stdio (handlers run on this machine)"],
68
+ [`${bin} mcp install`, "Print MCP client registration commands (does not modify client config files)"],
69
+ ]).map((l) => ` ${l}`),
70
+ "",
71
+ ].join("\n");
72
+ if (sub !== undefined && (isHelp(sub) || rest.some(isHelp))) {
73
+ io.stdout.write(usage);
74
+ return EXIT.ok;
75
+ }
76
+ if (sub === "serve" && rest.length === 0) {
77
+ // 종료 = SIGINT (io.signal) · SIGTERM → close → cleanup 후 exit 0 (mext start --stdio 와 같음)
78
+ const term = new AbortController();
79
+ const onTerm = () => term.abort();
80
+ process.once("SIGTERM", onTerm);
81
+ try {
82
+ const signal = io.signal ? AbortSignal.any([io.signal, term.signal]) : term.signal;
83
+ return await startFromBuild(dir, { argv: ["--stdio"], stderr: io.stderr, signal });
84
+ }
85
+ finally {
86
+ process.off("SIGTERM", onTerm);
87
+ }
88
+ }
89
+ if (sub === "install" && rest.length === 0) {
90
+ io.stdout.write(`${mcpInstallText({ name: bin, url: false, stdioCommand: `${bin} mcp serve` })}\n`);
91
+ return EXIT.ok;
92
+ }
93
+ io.stderr.write(`error: ${sub === undefined ? "missing subcommand" : `unknown argument "${argv.join(" ")}"`}\n\n${usage}`);
94
+ return EXIT.usage;
95
+ }
96
+ /** 사람용 루트 help 뒤의 내장 명령 */
97
+ function builtinHelp(bin) {
98
+ return [
99
+ "CLI commands:",
100
+ ...table([
101
+ ["mcp serve", `MCP server over stdio (${bin} mcp install for registration)`],
102
+ ["mcp install", "Print MCP client registration commands"],
103
+ ["--version", "Print the version"],
104
+ ]).map((l) => ` ${l}`),
105
+ "",
106
+ ].join("\n");
107
+ }
108
+ //# sourceMappingURL=local-bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-bin.js","sourceRoot":"","sources":["../src/local-bin.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport { table } from \"./cli/help.ts\"\nimport { resolveIo, type CliIo } from \"./cli/io.ts\"\nimport { EXIT, type CliErrorFormatter } from \"./cli/output.ts\"\nimport { runLocalCommand, type LocalRunSource } from \"./cli/run.ts\"\nimport { mcpInstallText } from \"./mcp/install.ts\"\nimport { loadBuild, startFromBuild } from \"./start.ts\"\n\n/**\n * 로컬 bin (cli.local, §5). .mext/bin.js 가 호출하는 런타임 (@obov/mext/start).\n * 생성 파일 계약 (mext build 가 쓰는 bin.js, 바꾸면 기존 빌드가 깨진다):\n * await mainLocalBin(new URL(\".\", import.meta.url), { bin, version, errors? })\n * 명령: <bin> help · <bin> <ns> <cmd> [flags] · <bin> mcp serve · <bin> mcp install · <bin> --version.\n * 명령어 실행 = mext run (로컬) 과 같은 코어 (cli/run.ts runLocalCommand), Registry = .mext/manifest.json (소스 · TypeScript 없음)\n */\n\nexport interface LocalBinOptions {\n /** 실행 파일 이름 (cli.bin) */\n bin: string\n /** --version (cli.version > 도입 프로젝트 package.json version) */\n version: string\n /** .mext/ 기준 cli.errors 번들 모듈 (예: \"cli-errors.js\") */\n errors?: string\n}\n\n/** 로컬 bin 실행. argv = 실행 파일 뒤 인자. 반환 = exit code */\nexport async function runLocalBin(\n dir: string | URL,\n options: LocalBinOptions,\n argv: readonly string[],\n partialIo: Partial<CliIo> = {},\n): Promise<number> {\n const io = resolveIo(partialIo)\n const [first, ...rest] = argv\n if (first === \"--version\" || first === \"-v\") {\n io.stdout.write(`${options.version}\\n`)\n return EXIT.ok\n }\n if (first === \"mcp\") return mcp(dir, options, rest, io)\n return runLocalCommand(argv, io, buildSource(dir, options))\n}\n\n/** 프로세스 진입점 (bin.js). 첫 SIGINT = ctx.signal abort (exit 130), 두 번째 = 즉시 종료 */\nexport async function mainLocalBin(dir: string | URL, options: LocalBinOptions): Promise<void> {\n const controller = new AbortController()\n process.on(\"SIGINT\", () => {\n if (controller.signal.aborted) process.exit(130)\n controller.abort()\n })\n const code = await runLocalBin(dir, options, process.argv.slice(2), { signal: controller.signal })\n process.stdout.write(\"\", () => process.stderr.write(\"\", () => process.exit(code)))\n}\n\n/** .mext/ → 로컬 실행 코어 입력. CLI 필드 = 번들된 명령어의 Zod 입력 분석 (mext run 과 같음) */\nfunction buildSource(dir: string | URL, options: LocalBinOptions): LocalRunSource {\n return {\n prog: options.bin,\n footer: builtinHelp(options.bin),\n async load(setFormatter) {\n const build = await loadBuild(dir)\n if (options.errors !== undefined) setFormatter(await loadFormatter(dir, options.errors))\n return { instrumentation: build.instrumentation, config: build.config, registry: build.registry }\n },\n }\n}\n\nasync function loadFormatter(dir: string | URL, file: string): Promise<CliErrorFormatter> {\n const href = typeof dir === \"string\" ? pathToFileURL(resolve(dir, file)).href : new URL(file, dir.href.endsWith(\"/\") ? dir : `${dir.href}/`).href\n const mod = (await import(href)) as { default?: unknown }\n if (typeof mod.default !== \"function\") throw new TypeError(`${file}: default export must be a function (CliErrorFormatter)`)\n return mod.default as CliErrorFormatter\n}\n\nasync function mcp(dir: string | URL, options: LocalBinOptions, argv: readonly string[], io: CliIo): Promise<number> {\n const { bin } = options\n const [sub, ...rest] = argv\n const isHelp = (t: string): boolean => t === \"--help\" || t === \"-h\"\n const usage = [\n \"Usage:\",\n ...table([\n [`${bin} mcp serve`, \"Run the MCP server over stdio (handlers run on this machine)\"],\n [`${bin} mcp install`, \"Print MCP client registration commands (does not modify client config files)\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n if (sub !== undefined && (isHelp(sub) || rest.some(isHelp))) {\n io.stdout.write(usage)\n return EXIT.ok\n }\n if (sub === \"serve\" && rest.length === 0) {\n // 종료 = SIGINT (io.signal) · SIGTERM → close → cleanup 후 exit 0 (mext start --stdio 와 같음)\n const term = new AbortController()\n const onTerm = (): void => term.abort()\n process.once(\"SIGTERM\", onTerm)\n try {\n const signal = io.signal ? AbortSignal.any([io.signal, term.signal]) : term.signal\n return await startFromBuild(dir, { argv: [\"--stdio\"], stderr: io.stderr, signal })\n } finally {\n process.off(\"SIGTERM\", onTerm)\n }\n }\n if (sub === \"install\" && rest.length === 0) {\n io.stdout.write(`${mcpInstallText({ name: bin, url: false, stdioCommand: `${bin} mcp serve` })}\\n`)\n return EXIT.ok\n }\n io.stderr.write(`error: ${sub === undefined ? \"missing subcommand\" : `unknown argument \"${argv.join(\" \")}\"`}\\n\\n${usage}`)\n return EXIT.usage\n}\n\n/** 사람용 루트 help 뒤의 내장 명령 */\nfunction builtinHelp(bin: string): string {\n return [\n \"CLI commands:\",\n ...table([\n [\"mcp serve\", `MCP server over stdio (${bin} mcp install for registration)`],\n [\"mcp install\", \"Print MCP client registration commands\"],\n [\"--version\", \"Print the version\"],\n ]).map((l) => ` ${l}`),\n \"\",\n ].join(\"\\n\")\n}\n"]}
@@ -60,7 +60,7 @@ export type DispatchResult = ToolCallResult | InputRequiredResult;
60
60
  * options.input 이 있으면 ctx.ask 가 InputRequiredResult 를 돌려준다 (mrtr.ts).
61
61
  */
62
62
  export declare function callDispatcher(registry: Registry, tool: DispatcherTool, rawArguments: unknown, options: DispatchOptions): Promise<DispatchResult>;
63
- /** 오류 → isError 결과. MextError = toJSON, 그 외 = EXECUTION_ERROR */
63
+ /** 오류 → isError 결과. MextError · CommandError = toJSON, 그 외 = EXECUTION_ERROR (message 만) */
64
64
  export declare function errorResult(error: unknown): ToolCallResult;
65
65
  /** dispatcher 도구 inputSchema. command 를 Mcp-Param-Command 헤더로 복사 (SEP-2243) */
66
66
  export declare function dispatcherInputSchema(): JsonSchema;
@@ -1,4 +1,4 @@
1
- import { isMextError, MextError } from "../errors.js";
1
+ import { isDeclaredError, MextError } from "../errors.js";
2
2
  import { invoke } from "../invoke.js";
3
3
  import { formatHelp, help, HELP_COMMAND } from "./help.js";
4
4
  import { createAskSession, hasPending, inputRequiredResult, } from "./mrtr.js";
@@ -129,9 +129,9 @@ function invalidRequestState(tool, command) {
129
129
  examples: [],
130
130
  });
131
131
  }
132
- /** 오류 → isError 결과. MextError = toJSON, 그 외 = EXECUTION_ERROR */
132
+ /** 오류 → isError 결과. MextError · CommandError = toJSON, 그 외 = EXECUTION_ERROR (message 만) */
133
133
  export function errorResult(error) {
134
- const body = isMextError(error)
134
+ const body = isDeclaredError(error)
135
135
  ? error.toJSON()
136
136
  : { code: EXECUTION_ERROR, message: error instanceof Error ? error.message : String(error) };
137
137
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/mcp/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,MAAM,EAAsB,MAAM,cAAc,CAAA;AAEzD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC1D,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,mBAAmB,GAKpB,MAAM,WAAW,CAAA;AA4ClB,sDAAsD;AACtD,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAEhD,wCAAwC;AACxC,MAAM,yBAAyB,GAAG,EAAE,CAAA;AAEpC,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAAC,QAAkB,EAAE,QAAQ,GAAiB,SAAS;IACpF,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAC3C,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACnD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACrD,OAAO;YACL;gBACE,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,YAAY,CACvB,OAAO,EACP,0BAA0B,EAC1B,KAAK,EACL,sFAAsF,CACvF;gBACD,WAAW,EAAE,qBAAqB,EAAE;gBACpC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE;gBACjG,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,YAAY,CAAC,QAAQ,EAAE,mCAAmC,EAAE,MAAM,CAAC;gBAChF,WAAW,EAAE,qBAAqB,EAAE;gBACpC,WAAW,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE;gBAC5E,QAAQ,EAAE,KAAK;aAChB;SACF,CAAA;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD,OAAO;QACL;YACE,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,YAAY,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,CAAC;YAC3D,WAAW,EAAE,qBAAqB,EAAE;YACpC,+CAA+C;YAC/C,WAAW,EAAE,QAAQ;gBACnB,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE;gBAClE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YACtG,QAAQ,EAAE,KAAK;SAChB;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAgC,EAAE,IAAY;IAC/E,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC3C,CAAC;AAeD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAkB,EAClB,IAAoB,EACpB,YAAqB,EACrB,OAAwB;IAExB,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,wBAAwB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;QACtE,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAI,IAAyC,EAAE,MAAM,CAAA;YACjE,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACvD,MAAM,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,8BAA8B,EAAE,EAAE,EAAE;oBAC9E,IAAI,EAAE,aAAa;oBACnB,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,iBAAiB;iBAC3B,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAChE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAA;QACtG,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAA;QAC1D,MAAM,MAAM,GAAG,KAAK;YAClB,CAAC,CAAC,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC;YACxE,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,aAAa,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACxF,IAAI,YAAY,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;QAC9D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAA;IAC1F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,WAAW,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,WAAW,CACxB,QAAkB,EAClB,IAAoB,EACpB,OAAe,EACf,IAAa,EACb,OAAwC,EACxC,KAA4C;IAE5C,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAA;IACtC,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;IACpG,MAAM,OAAO,GAAwB;QACnC,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE;QAC9D,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO;QACP,IAAI,EAAE,IAAI,IAAI,EAAE;KACjB,CAAA;IACD,IAAI,OAAO,GAA4B,EAAE,GAAG,KAAK,CAAC,cAAc,EAAE,CAAA;IAClE,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;QAC9D,IAAI,CAAC,KAAK;YAAE,MAAM,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACpD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,EAAE,CAAA;IACpC,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAA;IAClG,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACpH,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC3F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACnF,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,SAAS,mBAAmB,CAAC,IAAoB,EAAE,OAAe;IAChE,OAAO,IAAI,SAAS,CAClB,kBAAkB,EAClB,yBAAyB,OAAO,8CAA8C,IAAI,CAAC,IAAI,iDAAiD,EACxI;QACE,OAAO;QACP,cAAc,EAAE,EAAE;QAClB,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,cAAc;gBACpB,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,6FAA6F;aACvG;SACF;QACD,eAAe,EAAE,IAAI,CAAC,WAAW;QACjC,QAAQ,EAAE,EAAE;KACb,CACF,CAAA;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC;QAC7B,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE;QAChB,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;IAC9F,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACtF,iBAAiB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QAClC,OAAO,EAAE,IAAI;KACd,CAAA;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,2EAA2E;gBACxF,cAAc,EAAE,SAAS;aAC1B;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uEAAuE;gBACpF,oBAAoB,EAAE,IAAI;aAC3B;SACF;QACD,QAAQ,EAAE,CAAC,SAAS,CAAC;QACrB,oBAAoB,EAAE,KAAK;KAC5B,CAAA;AACH,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAoB,EAAE,GAAY;IAClE,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;IAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,6BAA6B,EAAE,EAAE,EAAE;YAChF,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,KAAgC,CAAA;IACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,2BAA2B,EAAE,CAAC,SAAS,CAAC,EAAE;YACvF,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,qCAAqC;SAC/C,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,0BAA0B,EAAE,EAAE,EAAE;YAC7E,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACrC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,sBAAsB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE;YACjG,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,sBAAsB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,mCAAmC;SACzF,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7F,MAAM,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,wBAAwB,EAAE,EAAE,EAAE;YACzE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,cAAc;YACpB,OAAO,EAAE,iBAAiB;SAC3B,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC;AAED,SAAS,uBAAuB,CAC9B,IAAoB,EACpB,OAAe,EACf,OAAe,EACf,OAAiB,EACjB,KAAsD;IAEtD,OAAO,IAAI,SAAS,CAAC,kBAAkB,EAAE,yBAAyB,OAAO,KAAK,OAAO,EAAE,EAAE;QACvF,OAAO;QACP,cAAc,EAAE,OAAO;QACvB,MAAM,EAAE,CAAC,KAAK,CAAC;QACf,eAAe,EAAE,IAAI,CAAC,WAAW;QACjC,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;KACtC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAA4C;IAChE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACnD,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACjE,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,QAAsC,EAAE,IAAa;IACrG,MAAM,KAAK,GAAG;QACZ,IAAI;QACJ,kEAAkE;YAChE,QAAQ,IAAI,2DAA2D;YACvE,GAAG,IAAI,4GAA4G;KACtH,CAAA;IACD,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,IAAI,yBAAyB,EAAE,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAA;QAC3B,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;AACvC,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAA;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC","sourcesContent":["import { isMextError, MextError } from \"../errors.ts\"\nimport { invoke, type InvokeOptions } from \"../invoke.ts\"\nimport type { JsonSchema, RegisteredCommand, Registry } from \"../registry.ts\"\nimport { formatHelp, help, HELP_COMMAND } from \"./help.ts\"\nimport {\n createAskSession,\n hasPending,\n inputRequiredResult,\n type InputRequiredResult,\n type InputRound,\n type RequestStateBinding,\n type RequestStateSigner,\n} from \"./mrtr.ts\"\n\n/**\n * MCP 어댑터의 transport 무관 부분: dispatcher 도구 정의 · tools/call 실행 · help · 오류 → 결과 변환.\n * SDK · HTTP 에 의존하지 않는다 (HTTP · stdio 가 공유).\n */\n\n/** execute = 도구 1개 (기본), split = query (readOnly · help) + mutate (그 외) */\nexport type ToolExposure = \"execute\" | \"split\"\n\nexport type DispatcherToolName = \"execute\" | \"query\" | \"mutate\"\n\n/** MCP ToolAnnotations 중 mext 가 쓰는 항목 */\nexport interface DispatcherAnnotations {\n title?: string\n readOnlyHint: boolean\n destructiveHint?: boolean\n idempotentHint?: boolean\n openWorldHint?: boolean\n}\n\nexport interface DispatcherTool {\n name: DispatcherToolName\n description: string\n /** JSON Schema 2020-12. command 에 x-mcp-header: \"Command\" */\n inputSchema: JsonSchema\n annotations: DispatcherAnnotations\n /** readOnly 명령어만 실행 (split 의 query). 그 외 = PERMISSION_DENIED */\n readOnly: boolean\n}\n\nexport interface TextContent {\n type: \"text\"\n text: string\n}\n\n/** MCP CallToolResult 와 같은 형태 (SDK 타입 비의존) */\nexport interface ToolCallResult {\n content: TextContent[]\n /** 성공 = 핸들러 반환값 (undefined 는 null), 오류 = { error: MextError.toJSON() } */\n structuredContent: unknown\n isError?: true\n}\n\n/** 핸들러 예외 (MextError 외) 의 결과 code. CLI --json 과 같음 */\nexport const EXECUTION_ERROR = \"EXECUTION_ERROR\"\n\n/** 도구 설명에 나열할 명령어 수 상한. 넘으면 help 안내만 */\nconst DESCRIPTION_COMMAND_LIMIT = 50\n\n/** 노출 방식별 dispatcher 도구 (정렬 순서 고정: execute · query, mutate) */\nexport function dispatcherTools(registry: Registry, exposure: ToolExposure = \"execute\"): DispatcherTool[] {\n const all = [...registry.commands.values()]\n if (exposure === \"split\") {\n const reads = all.filter((e) => e.command.readOnly)\n const writes = all.filter((e) => !e.command.readOnly)\n return [\n {\n name: \"query\",\n description: describeTool(\n \"query\",\n \"Run a read-only command.\",\n reads,\n 'Commands that are not read-only are rejected here; call them with the \"mutate\" tool.',\n ),\n inputSchema: dispatcherInputSchema(),\n annotations: { title: \"Query\", readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n readOnly: true,\n },\n {\n name: \"mutate\",\n description: describeTool(\"mutate\", \"Run a command that changes state.\", writes),\n inputSchema: dispatcherInputSchema(),\n annotations: { title: \"Mutate\", readOnlyHint: false, destructiveHint: true },\n readOnly: false,\n },\n ]\n }\n const readOnly = all.every((e) => e.command.readOnly)\n return [\n {\n name: \"execute\",\n description: describeTool(\"execute\", \"Run a command.\", all),\n inputSchema: dispatcherInputSchema(),\n // 단일 dispatcher 는 최악값 (researches/others.md 9)\n annotations: readOnly\n ? { title: \"Execute\", readOnlyHint: true, destructiveHint: false }\n : { title: \"Execute\", readOnlyHint: false, destructiveHint: all.some((e) => e.command.destructive) },\n readOnly: false,\n },\n ]\n}\n\nexport function findDispatcherTool(tools: readonly DispatcherTool[], name: string): DispatcherTool | undefined {\n return tools.find((t) => t.name === name)\n}\n\nexport interface DispatchOptions extends Omit<InvokeOptions, \"readOnly\"> {\n /** help 의 tool 표시용. dispatcherTools 결과 */\n tools?: readonly DispatcherTool[]\n /**\n * MRTR (MCP 2026-07-28). 지정하면 ctx.ask = InputRequiredResult 반환 · 재시도의 inputResponses 로 resolve\n * (ask 옵션 무시). requiresConfirmation 확인도 이 경로\n */\n input?: InputRound & { signer: RequestStateSigner }\n}\n\n/** tools/call 결과: 완료 (CallToolResult 형태) 또는 입력 요청 (InputRequiredResult) */\nexport type DispatchResult = ToolCallResult | InputRequiredResult\n\n/**\n * dispatcher 도구 호출: { command, args } → help 또는 invoke.\n * MextError · 핸들러 예외는 isError 결과로 (throw 하지 않음). 인증 (UNAUTHENTICATED → 401) 은 transport 몫.\n * options.input 이 있으면 ctx.ask 가 InputRequiredResult 를 돌려준다 (mrtr.ts).\n */\nexport async function callDispatcher(\n registry: Registry,\n tool: DispatcherTool,\n rawArguments: unknown,\n options: DispatchOptions,\n): Promise<DispatchResult> {\n try {\n const { command, args } = parseDispatcherArguments(tool, rawArguments)\n if (command === HELP_COMMAND) {\n const target = (args as { target?: unknown } | undefined)?.target\n if (target !== undefined && typeof target !== \"string\") {\n throw dispatcherArgumentError(tool, \"help\", \"args.target must be a string\", [], {\n path: \"args.target\",\n code: \"invalid_type\",\n message: \"expected string\",\n })\n }\n const toolFor = splitToolFor(options.tools)\n const value = help(registry, target, toolFor ? { toolFor } : {})\n return { content: [{ type: \"text\", text: formatHelp(value, tool.name) }], structuredContent: value }\n }\n const { input, tools: _tools, ...invokeOptions } = options\n const result = input\n ? await invokeRound(registry, tool, command, args, invokeOptions, input)\n : await invoke(registry, command, args, { ...invokeOptions, readOnly: tool.readOnly })\n if (\"resultType\" in result) return result\n const value = result.value === undefined ? null : result.value\n return { content: [{ type: \"text\", text: stringify(value) }], structuredContent: value }\n } catch (error) {\n return errorResult(error)\n }\n}\n\n/**\n * MRTR 한 라운드: requestState 검증 → ask 세션으로 invoke. 답 없는 ask 가 있으면 (핸들러가 오류를 삼켜도)\n * 결과를 버리고 InputRequiredResult. elicitation 미선언 클라이언트 = ask 미주입 (INPUT_UNAVAILABLE)\n */\nasync function invokeRound(\n registry: Registry,\n tool: DispatcherTool,\n command: string,\n args: unknown,\n options: Omit<InvokeOptions, \"readOnly\">,\n input: NonNullable<DispatchOptions[\"input\"]>,\n): Promise<{ command: string; value: unknown } | InputRequiredResult> {\n const { ask: _ask, ...rest } = options\n if (!input.elicitation) return invoke(registry, command, args, { ...rest, readOnly: tool.readOnly })\n const binding: RequestStateBinding = {\n principal: `${options.principal.kind}:${options.principal.id}`,\n tool: tool.name,\n command,\n args: args ?? {},\n }\n let answers: Record<string, unknown> = { ...input.inputResponses }\n if (input.requestState !== undefined) {\n const prior = input.signer.verify(input.requestState, binding)\n if (!prior) throw invalidRequestState(tool, command)\n answers = { ...answers, ...prior }\n }\n const session = createAskSession(answers)\n const signal = options.signal ? AbortSignal.any([options.signal, session.signal]) : session.signal\n try {\n const result = await invoke(registry, command, args, { ...rest, signal, ask: session.ask, readOnly: tool.readOnly })\n return hasPending(session) ? inputRequiredResult(session, input.signer, binding) : result\n } catch (error) {\n if (hasPending(session)) return inputRequiredResult(session, input.signer, binding)\n throw error\n }\n}\n\n/** 서명 · 만료 · 바인딩 검증 실패한 requestState. 처음부터 다시 호출하도록 안내 */\nfunction invalidRequestState(tool: DispatcherTool, command: string): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\n \"INVALID_ARGUMENT\",\n `Invalid arguments for ${command}: requestState is invalid or expired. Call ${tool.name} again without requestState and inputResponses.`,\n {\n command,\n missing_fields: [],\n issues: [\n {\n path: \"requestState\",\n code: \"invalid_request_state\",\n message: \"requestState failed verification (tampered, expired, or for a different call or server key)\",\n },\n ],\n expected_schema: tool.inputSchema,\n examples: [],\n },\n )\n}\n\n/** 오류 → isError 결과. MextError = toJSON, 그 외 = EXECUTION_ERROR */\nexport function errorResult(error: unknown): ToolCallResult {\n const body = isMextError(error)\n ? error.toJSON()\n : { code: EXECUTION_ERROR, message: error instanceof Error ? error.message : String(error) }\n return {\n content: [{ type: \"text\", text: `${body.code}: ${body.message}\\n${stringify(body)}` }],\n structuredContent: { error: body },\n isError: true,\n }\n}\n\n/** dispatcher 도구 inputSchema. command 를 Mcp-Param-Command 헤더로 복사 (SEP-2243) */\nexport function dispatcherInputSchema(): JsonSchema {\n return {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description: 'Command ID (e.g. \"users.get\"), or \"help\" to list namespaces and commands.',\n \"x-mcp-header\": \"Command\",\n },\n args: {\n type: \"object\",\n description: 'Command input. For \"help\": { \"target\": \"<namespace or command ID>\" }.',\n additionalProperties: true,\n },\n },\n required: [\"command\"],\n additionalProperties: false,\n }\n}\n\nfunction parseDispatcherArguments(tool: DispatcherTool, raw: unknown): { command: string; args: unknown } {\n const input = raw === undefined ? {} : raw\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n throw dispatcherArgumentError(tool, tool.name, \"arguments must be an object\", [], {\n path: \"\",\n code: \"invalid_type\",\n message: \"expected object\",\n })\n }\n const { command, args, ...rest } = input as Record<string, unknown>\n if (command === undefined) {\n throw dispatcherArgumentError(tool, tool.name, \"missing required: command\", [\"command\"], {\n path: \"command\",\n code: \"invalid_type\",\n message: \"expected string, received undefined\",\n })\n }\n if (typeof command !== \"string\") {\n throw dispatcherArgumentError(tool, tool.name, \"command must be a string\", [], {\n path: \"command\",\n code: \"invalid_type\",\n message: \"expected string\",\n })\n }\n const unknownKeys = Object.keys(rest)\n if (unknownKeys.length > 0) {\n throw dispatcherArgumentError(tool, tool.name, `unrecognized keys: ${unknownKeys.join(\", \")}`, [], {\n path: \"\",\n code: \"unrecognized_keys\",\n message: `Unrecognized keys: ${unknownKeys.join(\", \")}. Put command input under \"args\".`,\n })\n }\n if (args !== undefined && (args === null || typeof args !== \"object\" || Array.isArray(args))) {\n throw dispatcherArgumentError(tool, command, \"args must be an object\", [], {\n path: \"args\",\n code: \"invalid_type\",\n message: \"expected object\",\n })\n }\n return { command, args }\n}\n\nfunction dispatcherArgumentError(\n tool: DispatcherTool,\n command: string,\n summary: string,\n missing: string[],\n issue: { path: string; code: string; message: string },\n): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\"INVALID_ARGUMENT\", `Invalid arguments for ${command}: ${summary}`, {\n command,\n missing_fields: missing,\n issues: [issue],\n expected_schema: tool.inputSchema,\n examples: [{ command: HELP_COMMAND }],\n })\n}\n\nfunction splitToolFor(tools: readonly DispatcherTool[] | undefined): ((e: RegisteredCommand) => string) | undefined {\n if (!tools || tools.length < 2) return undefined\n const query = tools.find((t) => t.readOnly)?.name\n const mutate = tools.find((t) => !t.readOnly)?.name\n if (query === undefined || mutate === undefined) return undefined\n return (e) => (e.command.readOnly ? query : mutate)\n}\n\nfunction describeTool(name: string, lead: string, commands: readonly RegisteredCommand[], note?: string): string {\n const lines = [\n lead,\n `Arguments: { \"command\": \"<command ID>\", \"args\": { ...input } }. ` +\n `Call ${name}({ \"command\": \"help\" }) to list namespaces and commands, ` +\n `${name}({ \"command\": \"help\", \"args\": { \"target\": \"<namespace or command ID>\" } }) for input schemas and examples.`,\n ]\n if (note) lines.push(note)\n if (commands.length > 0 && commands.length <= DESCRIPTION_COMMAND_LIMIT) {\n lines.push(\"\", \"Commands:\")\n for (const e of commands) lines.push(`- ${e.id}: ${firstLine(e.command.description)}`)\n }\n return lines.join(\"\\n\")\n}\n\nfunction firstLine(text: string): string {\n return text.split(\"\\n\", 1)[0]!.trim()\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2) ?? \"null\"\n } catch {\n return String(value)\n }\n}\n"]}
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/mcp/dispatch.ts"],"names":[],"mappings":"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","sourcesContent":["import { isDeclaredError, MextError } from \"../errors.ts\"\nimport { invoke, type InvokeOptions } from \"../invoke.ts\"\nimport type { JsonSchema, RegisteredCommand, Registry } from \"../registry.ts\"\nimport { formatHelp, help, HELP_COMMAND } from \"./help.ts\"\nimport {\n createAskSession,\n hasPending,\n inputRequiredResult,\n type InputRequiredResult,\n type InputRound,\n type RequestStateBinding,\n type RequestStateSigner,\n} from \"./mrtr.ts\"\n\n/**\n * MCP 어댑터의 transport 무관 부분: dispatcher 도구 정의 · tools/call 실행 · help · 오류 → 결과 변환.\n * SDK · HTTP 에 의존하지 않는다 (HTTP · stdio 가 공유).\n */\n\n/** execute = 도구 1개 (기본), split = query (readOnly · help) + mutate (그 외) */\nexport type ToolExposure = \"execute\" | \"split\"\n\nexport type DispatcherToolName = \"execute\" | \"query\" | \"mutate\"\n\n/** MCP ToolAnnotations 중 mext 가 쓰는 항목 */\nexport interface DispatcherAnnotations {\n title?: string\n readOnlyHint: boolean\n destructiveHint?: boolean\n idempotentHint?: boolean\n openWorldHint?: boolean\n}\n\nexport interface DispatcherTool {\n name: DispatcherToolName\n description: string\n /** JSON Schema 2020-12. command 에 x-mcp-header: \"Command\" */\n inputSchema: JsonSchema\n annotations: DispatcherAnnotations\n /** readOnly 명령어만 실행 (split 의 query). 그 외 = PERMISSION_DENIED */\n readOnly: boolean\n}\n\nexport interface TextContent {\n type: \"text\"\n text: string\n}\n\n/** MCP CallToolResult 와 같은 형태 (SDK 타입 비의존) */\nexport interface ToolCallResult {\n content: TextContent[]\n /** 성공 = 핸들러 반환값 (undefined 는 null), 오류 = { error: MextError.toJSON() } */\n structuredContent: unknown\n isError?: true\n}\n\n/** 핸들러 예외 (MextError 외) 의 결과 code. CLI --json 과 같음 */\nexport const EXECUTION_ERROR = \"EXECUTION_ERROR\"\n\n/** 도구 설명에 나열할 명령어 수 상한. 넘으면 help 안내만 */\nconst DESCRIPTION_COMMAND_LIMIT = 50\n\n/** 노출 방식별 dispatcher 도구 (정렬 순서 고정: execute · query, mutate) */\nexport function dispatcherTools(registry: Registry, exposure: ToolExposure = \"execute\"): DispatcherTool[] {\n const all = [...registry.commands.values()]\n if (exposure === \"split\") {\n const reads = all.filter((e) => e.command.readOnly)\n const writes = all.filter((e) => !e.command.readOnly)\n return [\n {\n name: \"query\",\n description: describeTool(\n \"query\",\n \"Run a read-only command.\",\n reads,\n 'Commands that are not read-only are rejected here; call them with the \"mutate\" tool.',\n ),\n inputSchema: dispatcherInputSchema(),\n annotations: { title: \"Query\", readOnlyHint: true, destructiveHint: false, idempotentHint: true },\n readOnly: true,\n },\n {\n name: \"mutate\",\n description: describeTool(\"mutate\", \"Run a command that changes state.\", writes),\n inputSchema: dispatcherInputSchema(),\n annotations: { title: \"Mutate\", readOnlyHint: false, destructiveHint: true },\n readOnly: false,\n },\n ]\n }\n const readOnly = all.every((e) => e.command.readOnly)\n return [\n {\n name: \"execute\",\n description: describeTool(\"execute\", \"Run a command.\", all),\n inputSchema: dispatcherInputSchema(),\n // 단일 dispatcher 는 최악값 (researches/others.md 9)\n annotations: readOnly\n ? { title: \"Execute\", readOnlyHint: true, destructiveHint: false }\n : { title: \"Execute\", readOnlyHint: false, destructiveHint: all.some((e) => e.command.destructive) },\n readOnly: false,\n },\n ]\n}\n\nexport function findDispatcherTool(tools: readonly DispatcherTool[], name: string): DispatcherTool | undefined {\n return tools.find((t) => t.name === name)\n}\n\nexport interface DispatchOptions extends Omit<InvokeOptions, \"readOnly\"> {\n /** help 의 tool 표시용. dispatcherTools 결과 */\n tools?: readonly DispatcherTool[]\n /**\n * MRTR (MCP 2026-07-28). 지정하면 ctx.ask = InputRequiredResult 반환 · 재시도의 inputResponses 로 resolve\n * (ask 옵션 무시). requiresConfirmation 확인도 이 경로\n */\n input?: InputRound & { signer: RequestStateSigner }\n}\n\n/** tools/call 결과: 완료 (CallToolResult 형태) 또는 입력 요청 (InputRequiredResult) */\nexport type DispatchResult = ToolCallResult | InputRequiredResult\n\n/**\n * dispatcher 도구 호출: { command, args } → help 또는 invoke.\n * MextError · 핸들러 예외는 isError 결과로 (throw 하지 않음). 인증 (UNAUTHENTICATED → 401) 은 transport 몫.\n * options.input 이 있으면 ctx.ask 가 InputRequiredResult 를 돌려준다 (mrtr.ts).\n */\nexport async function callDispatcher(\n registry: Registry,\n tool: DispatcherTool,\n rawArguments: unknown,\n options: DispatchOptions,\n): Promise<DispatchResult> {\n try {\n const { command, args } = parseDispatcherArguments(tool, rawArguments)\n if (command === HELP_COMMAND) {\n const target = (args as { target?: unknown } | undefined)?.target\n if (target !== undefined && typeof target !== \"string\") {\n throw dispatcherArgumentError(tool, \"help\", \"args.target must be a string\", [], {\n path: \"args.target\",\n code: \"invalid_type\",\n message: \"expected string\",\n })\n }\n const toolFor = splitToolFor(options.tools)\n const value = help(registry, target, toolFor ? { toolFor } : {})\n return { content: [{ type: \"text\", text: formatHelp(value, tool.name) }], structuredContent: value }\n }\n const { input, tools: _tools, ...invokeOptions } = options\n const result = input\n ? await invokeRound(registry, tool, command, args, invokeOptions, input)\n : await invoke(registry, command, args, { ...invokeOptions, readOnly: tool.readOnly })\n if (\"resultType\" in result) return result\n const value = result.value === undefined ? null : result.value\n return { content: [{ type: \"text\", text: stringify(value) }], structuredContent: value }\n } catch (error) {\n return errorResult(error)\n }\n}\n\n/**\n * MRTR 한 라운드: requestState 검증 → ask 세션으로 invoke. 답 없는 ask 가 있으면 (핸들러가 오류를 삼켜도)\n * 결과를 버리고 InputRequiredResult. elicitation 미선언 클라이언트 = ask 미주입 (INPUT_UNAVAILABLE)\n */\nasync function invokeRound(\n registry: Registry,\n tool: DispatcherTool,\n command: string,\n args: unknown,\n options: Omit<InvokeOptions, \"readOnly\">,\n input: NonNullable<DispatchOptions[\"input\"]>,\n): Promise<{ command: string; value: unknown } | InputRequiredResult> {\n const { ask: _ask, ...rest } = options\n if (!input.elicitation) return invoke(registry, command, args, { ...rest, readOnly: tool.readOnly })\n const binding: RequestStateBinding = {\n principal: `${options.principal.kind}:${options.principal.id}`,\n tool: tool.name,\n command,\n args: args ?? {},\n }\n let answers: Record<string, unknown> = { ...input.inputResponses }\n if (input.requestState !== undefined) {\n const prior = input.signer.verify(input.requestState, binding)\n if (!prior) throw invalidRequestState(tool, command)\n answers = { ...answers, ...prior }\n }\n const session = createAskSession(answers)\n const signal = options.signal ? AbortSignal.any([options.signal, session.signal]) : session.signal\n try {\n const result = await invoke(registry, command, args, { ...rest, signal, ask: session.ask, readOnly: tool.readOnly })\n return hasPending(session) ? inputRequiredResult(session, input.signer, binding) : result\n } catch (error) {\n if (hasPending(session)) return inputRequiredResult(session, input.signer, binding)\n throw error\n }\n}\n\n/** 서명 · 만료 · 바인딩 검증 실패한 requestState. 처음부터 다시 호출하도록 안내 */\nfunction invalidRequestState(tool: DispatcherTool, command: string): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\n \"INVALID_ARGUMENT\",\n `Invalid arguments for ${command}: requestState is invalid or expired. Call ${tool.name} again without requestState and inputResponses.`,\n {\n command,\n missing_fields: [],\n issues: [\n {\n path: \"requestState\",\n code: \"invalid_request_state\",\n message: \"requestState failed verification (tampered, expired, or for a different call or server key)\",\n },\n ],\n expected_schema: tool.inputSchema,\n examples: [],\n },\n )\n}\n\n/** 오류 → isError 결과. MextError · CommandError = toJSON, 그 외 = EXECUTION_ERROR (message 만) */\nexport function errorResult(error: unknown): ToolCallResult {\n const body: Record<string, unknown> & { code: string; message: string } = isDeclaredError(error)\n ? error.toJSON()\n : { code: EXECUTION_ERROR, message: error instanceof Error ? error.message : String(error) }\n return {\n content: [{ type: \"text\", text: `${body.code}: ${body.message}\\n${stringify(body)}` }],\n structuredContent: { error: body },\n isError: true,\n }\n}\n\n/** dispatcher 도구 inputSchema. command 를 Mcp-Param-Command 헤더로 복사 (SEP-2243) */\nexport function dispatcherInputSchema(): JsonSchema {\n return {\n type: \"object\",\n properties: {\n command: {\n type: \"string\",\n description: 'Command ID (e.g. \"users.get\"), or \"help\" to list namespaces and commands.',\n \"x-mcp-header\": \"Command\",\n },\n args: {\n type: \"object\",\n description: 'Command input. For \"help\": { \"target\": \"<namespace or command ID>\" }.',\n additionalProperties: true,\n },\n },\n required: [\"command\"],\n additionalProperties: false,\n }\n}\n\nfunction parseDispatcherArguments(tool: DispatcherTool, raw: unknown): { command: string; args: unknown } {\n const input = raw === undefined ? {} : raw\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n throw dispatcherArgumentError(tool, tool.name, \"arguments must be an object\", [], {\n path: \"\",\n code: \"invalid_type\",\n message: \"expected object\",\n })\n }\n const { command, args, ...rest } = input as Record<string, unknown>\n if (command === undefined) {\n throw dispatcherArgumentError(tool, tool.name, \"missing required: command\", [\"command\"], {\n path: \"command\",\n code: \"invalid_type\",\n message: \"expected string, received undefined\",\n })\n }\n if (typeof command !== \"string\") {\n throw dispatcherArgumentError(tool, tool.name, \"command must be a string\", [], {\n path: \"command\",\n code: \"invalid_type\",\n message: \"expected string\",\n })\n }\n const unknownKeys = Object.keys(rest)\n if (unknownKeys.length > 0) {\n throw dispatcherArgumentError(tool, tool.name, `unrecognized keys: ${unknownKeys.join(\", \")}`, [], {\n path: \"\",\n code: \"unrecognized_keys\",\n message: `Unrecognized keys: ${unknownKeys.join(\", \")}. Put command input under \"args\".`,\n })\n }\n if (args !== undefined && (args === null || typeof args !== \"object\" || Array.isArray(args))) {\n throw dispatcherArgumentError(tool, command, \"args must be an object\", [], {\n path: \"args\",\n code: \"invalid_type\",\n message: \"expected object\",\n })\n }\n return { command, args }\n}\n\nfunction dispatcherArgumentError(\n tool: DispatcherTool,\n command: string,\n summary: string,\n missing: string[],\n issue: { path: string; code: string; message: string },\n): MextError<\"INVALID_ARGUMENT\"> {\n return new MextError(\"INVALID_ARGUMENT\", `Invalid arguments for ${command}: ${summary}`, {\n command,\n missing_fields: missing,\n issues: [issue],\n expected_schema: tool.inputSchema,\n examples: [{ command: HELP_COMMAND }],\n })\n}\n\nfunction splitToolFor(tools: readonly DispatcherTool[] | undefined): ((e: RegisteredCommand) => string) | undefined {\n if (!tools || tools.length < 2) return undefined\n const query = tools.find((t) => t.readOnly)?.name\n const mutate = tools.find((t) => !t.readOnly)?.name\n if (query === undefined || mutate === undefined) return undefined\n return (e) => (e.command.readOnly ? query : mutate)\n}\n\nfunction describeTool(name: string, lead: string, commands: readonly RegisteredCommand[], note?: string): string {\n const lines = [\n lead,\n `Arguments: { \"command\": \"<command ID>\", \"args\": { ...input } }. ` +\n `Call ${name}({ \"command\": \"help\" }) to list namespaces and commands, ` +\n `${name}({ \"command\": \"help\", \"args\": { \"target\": \"<namespace or command ID>\" } }) for input schemas and examples.`,\n ]\n if (note) lines.push(note)\n if (commands.length > 0 && commands.length <= DESCRIPTION_COMMAND_LIMIT) {\n lines.push(\"\", \"Commands:\")\n for (const e of commands) lines.push(`- ${e.id}: ${firstLine(e.command.description)}`)\n }\n return lines.join(\"\\n\")\n}\n\nfunction firstLine(text: string): string {\n return text.split(\"\\n\", 1)[0]!.trim()\n}\n\nfunction stringify(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2) ?? \"null\"\n } catch {\n return String(value)\n }\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  export interface McpInstallOptions {
2
2
  /** MCP 클라이언트에 등록할 서버 이름. 기본 "mext" */
3
3
  name?: string;
4
- /** Streamable HTTP 엔드포인트. 기본 http://127.0.0.1:3100/mcp */
5
- url?: string;
4
+ /** Streamable HTTP 엔드포인트. 기본 http://127.0.0.1:3100/mcp. false = HTTP 안내 생략 (로컬 bin 은 stdio 만) */
5
+ url?: string | false;
6
6
  /**
7
7
  * stdio 실행 명령. 기본 "npx mext dev --stdio" (운영 = "npx mext start --stdio"). false = stdio 안내 생략 (고객 CLI 는 원격만)
8
8
  */
@@ -8,6 +8,24 @@ export function mcpInstallText(options = {}) {
8
8
  const url = options.url ?? `http://${DEFAULT_HOST}:${DEFAULT_PORT}${MCP_PATH}`;
9
9
  const stdio = options.stdioCommand === undefined ? "npx mext dev --stdio" : options.stdioCommand;
10
10
  const n = shellQuote(name);
11
+ if (url === false) {
12
+ if (stdio === false)
13
+ throw new TypeError("mcpInstallText: url and stdioCommand cannot both be false");
14
+ const [command, ...args] = stdio.split(" ");
15
+ return [
16
+ `Register the "${name}" MCP server with your MCP client.`,
17
+ "",
18
+ "Claude Code (stdio, runs commands locally):",
19
+ ` claude mcp add ${n} -- ${stdio}`,
20
+ "",
21
+ "Other MCP clients (JSON config):",
22
+ ...JSON.stringify({ mcpServers: { [name]: { command, args } } }, null, 2)
23
+ .split("\n")
24
+ .map((l) => ` ${l}`),
25
+ "",
26
+ "This command only prints instructions; it does not modify client configuration files.",
27
+ ].join("\n");
28
+ }
11
29
  const lines = [
12
30
  `Register the "${name}" MCP server with your MCP client.`,
13
31
  "",
@@ -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":"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","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"]}
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
@@ -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 = {}) {
package/dist/start.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\"\nimport { resolve, sep } from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { COMMAND, isCommand, type Command } from \"./command.ts\"\nimport { mcpDeclarations, mcpStdioDeclarations, resolveLocalPrincipal, type MextConfig } from \"./config.ts\"\nimport { loadInstrumentation, type Instrumentation } from \"./instrumentation.ts\"\nimport { MANIFEST_SCHEMA_VERSION, type BuildManifest, type BuildManifestCommand } from \"./manifest.ts\"\nimport { DEFAULT_HOST, DEFAULT_PORT } from \"./mcp/constants.ts\"\nimport { createMcpFetchHandler, startMcpHttpServer, type McpFetchHandler, type McpHttpOptions } from \"./mcp/http.ts\"\nimport { redirectStdout, serveMcpStdio } from \"./mcp/stdio.ts\"\nimport { createLazyRegistry, type Registry } from \"./registry.ts\"\nimport { mextVersion } from \"./version.ts\"\n\n/**\n * @obov/mext/start: .mext/ (mext build 산출) 실행 런타임. 소스 스캔 · TypeScript 없음.\n * 생성 파일 계약 (mext build 가 쓰는 server.js · handler.js, 바꾸면 기존 빌드가 깨진다):\n * server.js process.exitCode = await startFromBuild(new URL(\".\", import.meta.url), { argv: process.argv.slice(2), env: process.env })\n * handler.js export default createFetchHandler(new URL(\".\", import.meta.url))\n *\n * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.\n * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)\n */\n\nexport interface StartOptions {\n /** server.js · mext start 뒤 인자. --stdio · --port <n> · --host <h> */\n argv?: readonly string[]\n /** PORT · HOST. 기본 process.env */\n env?: Record<string, string | undefined>\n /** abort = 종료. 기본 SIGINT · SIGTERM */\n signal?: AbortSignal\n /** 기본 process.stderr */\n stderr?: { write(chunk: string): unknown }\n}\n\nexport interface FetchHandler {\n /** 거부하지 않는다. 초기화 실패 · 내부 오류 = 500 JSON-RPC 오류 */\n fetch(request: Request): Promise<Response>\n}\n\n/** .mext/ 로드 결과 */\nexport interface LoadedBuild {\n manifest: BuildManifest\n config: MextConfig\n registry: Registry\n instrumentation: Instrumentation\n}\n\nexport interface LoadBuildOptions {\n /** 명령어 모듈 import (테스트 계측). 기본 = 동적 import */\n importModule?: (href: string) => Promise<unknown>\n}\n\nconst USAGE = \"Usage: mext start [--stdio] [--port <n>] [--host <h>] (node .mext/server.js, PORT · HOST environment variables)\"\n\n/** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */\nexport async function startFromBuild(dir: string | URL, options: StartOptions = {}): Promise<number> {\n const stderr = options.stderr ?? process.stderr\n const env = options.env ?? process.env\n const args = parseStartArgs(options.argv ?? [])\n if (typeof args === \"string\") {\n stderr.write(`error: ${args}\\n${USAGE}\\n`)\n return 2\n }\n // stdio: 사용자 코드 import 전부터 그 외 stdout 쓰기를 stderr 로 (mext dev --stdio 와 같음)\n const redirect = args.stdio ? redirectStdout() : undefined\n const { signal, dispose } = options.signal ? { signal: options.signal, dispose: () => {} } : processSignal()\n try {\n const build = await loadBuild(dir)\n const onerror = (error: Error): void => void stderr.write(`mcp: ${error.message}\\n`)\n if (args.stdio) {\n const server = serveMcpStdio({\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n principal: await resolveLocalPrincipal(build.config),\n ...(build.config.app ? { app: build.config.app } : {}),\n ...(build.config.tools ? { tools: build.config.tools } : {}),\n ...mcpStdioDeclarations(build.config),\n stdout: redirect!.wire,\n onerror,\n })\n const onAbort = (): void => void server.close()\n if (signal.aborted) onAbort()\n signal.addEventListener(\"abort\", onAbort, { once: true })\n await server.closed\n return 0\n }\n const port = args.port ?? envPort(env.PORT) ?? build.config.server?.port ?? DEFAULT_PORT\n const host = args.host ?? (env.HOST === undefined || env.HOST === \"\" ? undefined : env.HOST) ?? build.config.server?.host ?? DEFAULT_HOST\n const http = httpOptions(build, onerror)\n const server = await startMcpHttpServer({ ...http, server: { ...http.server, host, port } })\n stderr.write(`MCP server (Streamable HTTP) listening on ${server.url} (buildId ${build.manifest.buildId})\\n`)\n await new Promise<void>((done) => {\n if (signal.aborted) return done()\n signal.addEventListener(\"abort\", () => done(), { once: true })\n })\n await server.close()\n return 0\n } catch (error) {\n // 시작 실패 = stdout 원복 (stdio 서버를 띄우기 전)\n redirect?.restore()\n stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n } finally {\n dispose()\n }\n}\n\n/**\n * handler.js 의 Web fetch handler ({ fetch(request) → Response }). 동기 반환.\n * D7: 초기화 (manifest 검증 · instrumentation register · config · Registry) = 첫 요청에서 1회 (memoized promise).\n * 초기화 실패 = 그 요청 500 + JSON-RPC 오류 (data.loadError), 다음 요청에서 재시도.\n * MCP 처리 (Origin · auth · 헤더 검증 · SSE progress · MRTR) = node 서버와 같은 createMcpFetchHandler\n */\nexport function createFetchHandler(dir: string | URL, options: LoadBuildOptions = {}): FetchHandler {\n const base = dirUrl(dir)\n const onerror = (error: Error): void => console.error(`mcp: ${error.message}`)\n let pending: Promise<McpFetchHandler> | undefined\n const init = (): Promise<McpFetchHandler> =>\n (pending ??= loadBuild(base, options)\n .then((build) => createMcpFetchHandler(httpOptions(build, onerror)))\n .catch((error: unknown) => {\n pending = undefined\n throw error\n }))\n return {\n async fetch(request) {\n let handler: McpFetchHandler\n try {\n handler = await init()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n onerror(error instanceof Error ? error : new Error(message))\n const body = { jsonrpc: \"2.0\", id: null, error: { code: -32_603, message: \"Server failed to start\", data: { loadError: message } } }\n return new Response(JSON.stringify(body), { status: 500, headers: { \"Content-Type\": \"application/json\" } })\n }\n return handler.fetch(request)\n },\n }\n}\n\n/** .mext/manifest.json 읽기 + 검증 (없음 · schemaVersion · mextVersion 불일치 = 오류) */\nexport function readBuildManifest(dir: string | URL): BuildManifest {\n const base = dirUrl(dir)\n let text: string\n try {\n text = readFileSync(new URL(\"manifest.json\", base), \"utf8\")\n } catch {\n throw new Error(`no build found in ${fileURLToPath(base)} (manifest.json missing). Run \"mext build\" first`)\n }\n const manifest = JSON.parse(text) as BuildManifest\n if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {\n throw new Error(\n `${fileURLToPath(base)} was built with manifest schemaVersion ${String(manifest.schemaVersion)} (expected ${MANIFEST_SCHEMA_VERSION}). Run \"mext build\" again`,\n )\n }\n if (manifest.mextVersion !== mextVersion()) {\n throw new Error(\n `${fileURLToPath(base)} was built with @obov/mext ${manifest.mextVersion}, but ${mextVersion()} is installed. Run \"mext build\" again`,\n )\n }\n return manifest\n}\n\n/** manifest 검증 → instrumentation.js (register, 사용자 코드보다 먼저) → config.js → lazy Registry */\nexport async function loadBuild(dir: string | URL, options: LoadBuildOptions = {}): Promise<LoadedBuild> {\n const base = dirUrl(dir)\n const manifest = readBuildManifest(base)\n const instrumentation = await loadInstrumentation(\n manifest.instrumentation === undefined ? undefined : new URL(manifest.instrumentation, base),\n )\n const config =\n manifest.config === undefined\n ? {}\n : ((await import(new URL(manifest.config, base).href)) as { default: MextConfig }).default\n const registry = registryFromManifest(manifest, base, options)\n return { manifest, config, registry, instrumentation }\n}\n\n/** manifest → lazy Registry. 명령어 모듈 (manifest.commands[].module) 은 load() 에서 import */\nexport function registryFromManifest(manifest: BuildManifest, dir: string | URL, options: LoadBuildOptions = {}): Registry {\n const base = dirUrl(dir)\n const importModule = options.importModule ?? ((href: string) => import(href))\n return createLazyRegistry({\n commands: manifest.commands.map((c) => ({\n id: c.id,\n command: metadataCommand(c),\n aliases: c.aliases,\n ...(c.file !== undefined ? { file: c.file } : {}),\n inputSchema: c.inputSchema,\n ...(c.outputSchema !== undefined ? { outputSchema: c.outputSchema } : {}),\n async load() {\n const href = new URL(c.module, base).href\n const mod = (await importModule(href)) as { default?: unknown } | undefined\n if (!isCommand(mod?.default)) throw new Error(`${c.module}: default export is not a defineCommand result (command ${c.id})`)\n return mod.default\n },\n })),\n namespaces: [\n ...(manifest.description !== undefined ? [{ id: \"\", description: manifest.description }] : []),\n ...manifest.namespaces,\n ],\n })\n}\n\n/**\n * manifest 메타데이터만 있는 Command (RegisteredCommand.command). input · execute 는 모듈 로드 전이라 접근 = 오류\n * (invoke 는 loadCommand 결과를 쓴다)\n */\nfunction metadataCommand(c: BuildManifestCommand): Command {\n const parent = c.id.includes(\".\") ? c.id.slice(0, c.id.lastIndexOf(\".\") + 1) : \"\"\n const notLoaded = (): never => {\n throw new Error(`${c.id}: command module is not loaded (use loadCommand)`)\n }\n return Object.freeze({\n [COMMAND]: true as const,\n description: c.description,\n get input(): never {\n return notLoaded()\n },\n output: undefined,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n scopes: Object.freeze([...c.scopes]),\n examples: Object.freeze([...c.examples]) as Command[\"examples\"],\n positional: Object.freeze([...c.positional]),\n aliases: Object.freeze(c.aliases.map((a) => (a.startsWith(parent) ? a.slice(parent.length) : a))),\n execute: notLoaded,\n })\n}\n\nfunction httpOptions(build: LoadedBuild, onerror: (error: Error) => void): McpHttpOptions {\n const { config } = build\n return {\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n ...(config.app ? { app: config.app } : {}),\n ...(config.auth ? { auth: config.auth } : {}),\n ...(config.tools ? { tools: config.tools } : {}),\n ...(config.oauth ? { oauth: config.oauth } : {}),\n ...mcpDeclarations(config),\n server: config.server?.allowedOrigins ? { allowedOrigins: config.server.allowedOrigins } : {},\n onerror,\n }\n}\n\ninterface StartArgs {\n stdio: boolean\n port?: number\n host?: string\n}\n\n/** 반환 문자열 = 사용법 오류 */\nexport function parseStartArgs(argv: readonly string[]): StartArgs | string {\n const args: StartArgs = { stdio: false }\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!\n if (tok === \"--stdio\") {\n args.stdio = true\n continue\n }\n if (!tok.startsWith(\"--\")) return `unexpected argument \"${tok}\"`\n const eq = tok.indexOf(\"=\")\n const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq)\n if (name !== \"port\" && name !== \"host\") return `unknown flag --${name}`\n const value = eq < 0 ? argv[++i] : tok.slice(eq + 1)\n if (value === undefined || value === \"\") return `--${name} requires a value`\n if (name === \"port\") {\n const port = Number(value)\n if (!isPort(port)) return \"--port must be an integer between 0 and 65535\"\n args.port = port\n } else args.host = value\n }\n if (args.stdio && (args.port !== undefined || args.host !== undefined)) return \"--port and --host do not apply to --stdio\"\n return args\n}\n\nfunction envPort(value: string | undefined): number | undefined {\n if (value === undefined || value === \"\") return undefined\n const port = Number(value)\n if (!isPort(port)) throw new Error(`PORT must be an integer between 0 and 65535 (got \"${value}\")`)\n return port\n}\n\nfunction isPort(n: number): boolean {\n return Number.isInteger(n) && n >= 0 && n <= 65_535\n}\n\nfunction dirUrl(dir: string | URL): URL {\n if (typeof dir !== \"string\") return dir.href.endsWith(\"/\") ? dir : new URL(`${dir.href}/`)\n const abs = resolve(dir)\n return pathToFileURL(abs.endsWith(sep) ? abs : `${abs}${sep}`)\n}\n\nfunction processSignal(): { signal: AbortSignal; dispose(): void } {\n const controller = new AbortController()\n const abort = (): void => controller.abort()\n process.once(\"SIGINT\", abort)\n process.once(\"SIGTERM\", abort)\n return {\n signal: controller.signal,\n dispose() {\n process.off(\"SIGINT\", abort)\n process.off(\"SIGTERM\", abort)\n },\n }\n}\n"]}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\"\nimport { resolve, sep } from \"node:path\"\nimport { fileURLToPath, pathToFileURL } from \"node:url\"\nimport { COMMAND, isCommand, type Command } from \"./command.ts\"\nimport { mcpDeclarations, mcpStdioDeclarations, resolveLocalPrincipal, type MextConfig } from \"./config.ts\"\nimport { loadInstrumentation, type Instrumentation } from \"./instrumentation.ts\"\nimport { MANIFEST_SCHEMA_VERSION, type BuildManifest, type BuildManifestCommand } from \"./manifest.ts\"\nimport { DEFAULT_HOST, DEFAULT_PORT } from \"./mcp/constants.ts\"\nimport { createMcpFetchHandler, startMcpHttpServer, type McpFetchHandler, type McpHttpOptions } from \"./mcp/http.ts\"\nimport { redirectStdout, serveMcpStdio } from \"./mcp/stdio.ts\"\nimport { createLazyRegistry, type Registry } from \"./registry.ts\"\nimport { mextVersion } from \"./version.ts\"\n\n/**\n * @obov/mext/start: .mext/ (mext build 산출) 실행 런타임. 소스 스캔 · TypeScript 없음.\n * 생성 파일 계약 (mext build 가 쓰는 server.js · handler.js, 바꾸면 기존 빌드가 깨진다):\n * server.js process.exitCode = await startFromBuild(new URL(\".\", import.meta.url), { argv: process.argv.slice(2), env: process.env })\n * handler.js export default createFetchHandler(new URL(\".\", import.meta.url))\n *\n * 로드 순서 = manifest.json (검증) → instrumentation.js (register) → config.js → manifest 기반 lazy Registry.\n * 명령어 모듈은 그 명령어를 실행할 때 import (help · tools/list · UNKNOWN_COMMAND · 권한 거부는 import 없음)\n */\n\nexport { mainLocalBin, runLocalBin } from \"./local-bin.ts\"\nexport type { LocalBinOptions } from \"./local-bin.ts\"\n\nexport interface StartOptions {\n /** server.js · mext start 뒤 인자. --stdio · --port <n> · --host <h> */\n argv?: readonly string[]\n /** PORT · HOST. 기본 process.env */\n env?: Record<string, string | undefined>\n /** abort = 종료. 기본 SIGINT · SIGTERM */\n signal?: AbortSignal\n /** 기본 process.stderr */\n stderr?: { write(chunk: string): unknown }\n}\n\nexport interface FetchHandler {\n /** 거부하지 않는다. 초기화 실패 · 내부 오류 = 500 JSON-RPC 오류 */\n fetch(request: Request): Promise<Response>\n}\n\n/** .mext/ 로드 결과 */\nexport interface LoadedBuild {\n manifest: BuildManifest\n config: MextConfig\n registry: Registry\n instrumentation: Instrumentation\n}\n\nexport interface LoadBuildOptions {\n /** 명령어 모듈 import (테스트 계측). 기본 = 동적 import */\n importModule?: (href: string) => Promise<unknown>\n}\n\nconst USAGE = \"Usage: mext start [--stdio] [--port <n>] [--host <h>] (node .mext/server.js, PORT · HOST environment variables)\"\n\n/** .mext/ 실행 (mext start · node .mext/server.js). 반환 = exit code */\nexport async function startFromBuild(dir: string | URL, options: StartOptions = {}): Promise<number> {\n const stderr = options.stderr ?? process.stderr\n const env = options.env ?? process.env\n const args = parseStartArgs(options.argv ?? [])\n if (typeof args === \"string\") {\n stderr.write(`error: ${args}\\n${USAGE}\\n`)\n return 2\n }\n // stdio: 사용자 코드 import 전부터 그 외 stdout 쓰기를 stderr 로 (mext dev --stdio 와 같음)\n const redirect = args.stdio ? redirectStdout() : undefined\n const { signal, dispose } = options.signal ? { signal: options.signal, dispose: () => {} } : processSignal()\n try {\n const build = await loadBuild(dir)\n const onerror = (error: Error): void => void stderr.write(`mcp: ${error.message}\\n`)\n if (args.stdio) {\n const server = serveMcpStdio({\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n principal: await resolveLocalPrincipal(build.config),\n ...(build.config.app ? { app: build.config.app } : {}),\n ...(build.config.tools ? { tools: build.config.tools } : {}),\n ...mcpStdioDeclarations(build.config),\n stdout: redirect!.wire,\n onerror,\n })\n const onAbort = (): void => void server.close()\n if (signal.aborted) onAbort()\n signal.addEventListener(\"abort\", onAbort, { once: true })\n await server.closed\n return 0\n }\n const port = args.port ?? envPort(env.PORT) ?? build.config.server?.port ?? DEFAULT_PORT\n const host = args.host ?? (env.HOST === undefined || env.HOST === \"\" ? undefined : env.HOST) ?? build.config.server?.host ?? DEFAULT_HOST\n const http = httpOptions(build, onerror)\n const server = await startMcpHttpServer({ ...http, server: { ...http.server, host, port } })\n stderr.write(`MCP server (Streamable HTTP) listening on ${server.url} (buildId ${build.manifest.buildId})\\n`)\n await new Promise<void>((done) => {\n if (signal.aborted) return done()\n signal.addEventListener(\"abort\", () => done(), { once: true })\n })\n await server.close()\n return 0\n } catch (error) {\n // 시작 실패 = stdout 원복 (stdio 서버를 띄우기 전)\n redirect?.restore()\n stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n } finally {\n dispose()\n }\n}\n\n/**\n * handler.js 의 Web fetch handler ({ fetch(request) → Response }). 동기 반환.\n * D7: 초기화 (manifest 검증 · instrumentation register · config · Registry) = 첫 요청에서 1회 (memoized promise).\n * 초기화 실패 = 그 요청 500 + JSON-RPC 오류 (data.loadError), 다음 요청에서 재시도.\n * MCP 처리 (Origin · auth · 헤더 검증 · SSE progress · MRTR) = node 서버와 같은 createMcpFetchHandler\n */\nexport function createFetchHandler(dir: string | URL, options: LoadBuildOptions = {}): FetchHandler {\n const base = dirUrl(dir)\n const onerror = (error: Error): void => console.error(`mcp: ${error.message}`)\n let pending: Promise<McpFetchHandler> | undefined\n const init = (): Promise<McpFetchHandler> =>\n (pending ??= loadBuild(base, options)\n .then((build) => createMcpFetchHandler(httpOptions(build, onerror)))\n .catch((error: unknown) => {\n pending = undefined\n throw error\n }))\n return {\n async fetch(request) {\n let handler: McpFetchHandler\n try {\n handler = await init()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n onerror(error instanceof Error ? error : new Error(message))\n const body = { jsonrpc: \"2.0\", id: null, error: { code: -32_603, message: \"Server failed to start\", data: { loadError: message } } }\n return new Response(JSON.stringify(body), { status: 500, headers: { \"Content-Type\": \"application/json\" } })\n }\n return handler.fetch(request)\n },\n }\n}\n\n/** .mext/manifest.json 읽기 + 검증 (없음 · schemaVersion · mextVersion 불일치 = 오류) */\nexport function readBuildManifest(dir: string | URL): BuildManifest {\n const base = dirUrl(dir)\n let text: string\n try {\n text = readFileSync(new URL(\"manifest.json\", base), \"utf8\")\n } catch {\n throw new Error(`no build found in ${fileURLToPath(base)} (manifest.json missing). Run \"mext build\" first`)\n }\n const manifest = JSON.parse(text) as BuildManifest\n if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {\n throw new Error(\n `${fileURLToPath(base)} was built with manifest schemaVersion ${String(manifest.schemaVersion)} (expected ${MANIFEST_SCHEMA_VERSION}). Run \"mext build\" again`,\n )\n }\n if (manifest.mextVersion !== mextVersion()) {\n throw new Error(\n `${fileURLToPath(base)} was built with @obov/mext ${manifest.mextVersion}, but ${mextVersion()} is installed. Run \"mext build\" again`,\n )\n }\n return manifest\n}\n\n/** manifest 검증 → instrumentation.js (register, 사용자 코드보다 먼저) → config.js → lazy Registry */\nexport async function loadBuild(dir: string | URL, options: LoadBuildOptions = {}): Promise<LoadedBuild> {\n const base = dirUrl(dir)\n const manifest = readBuildManifest(base)\n const instrumentation = await loadInstrumentation(\n manifest.instrumentation === undefined ? undefined : new URL(manifest.instrumentation, base),\n )\n const config =\n manifest.config === undefined\n ? {}\n : ((await import(new URL(manifest.config, base).href)) as { default: MextConfig }).default\n const registry = registryFromManifest(manifest, base, options)\n return { manifest, config, registry, instrumentation }\n}\n\n/** manifest → lazy Registry. 명령어 모듈 (manifest.commands[].module) 은 load() 에서 import */\nexport function registryFromManifest(manifest: BuildManifest, dir: string | URL, options: LoadBuildOptions = {}): Registry {\n const base = dirUrl(dir)\n const importModule = options.importModule ?? ((href: string) => import(href))\n return createLazyRegistry({\n commands: manifest.commands.map((c) => ({\n id: c.id,\n command: metadataCommand(c),\n aliases: c.aliases,\n ...(c.file !== undefined ? { file: c.file } : {}),\n inputSchema: c.inputSchema,\n ...(c.outputSchema !== undefined ? { outputSchema: c.outputSchema } : {}),\n async load() {\n const href = new URL(c.module, base).href\n const mod = (await importModule(href)) as { default?: unknown } | undefined\n if (!isCommand(mod?.default)) throw new Error(`${c.module}: default export is not a defineCommand result (command ${c.id})`)\n return mod.default\n },\n })),\n namespaces: [\n ...(manifest.description !== undefined ? [{ id: \"\", description: manifest.description }] : []),\n ...manifest.namespaces,\n ],\n })\n}\n\n/**\n * manifest 메타데이터만 있는 Command (RegisteredCommand.command). input · execute 는 모듈 로드 전이라 접근 = 오류\n * (invoke 는 loadCommand 결과를 쓴다)\n */\nfunction metadataCommand(c: BuildManifestCommand): Command {\n const parent = c.id.includes(\".\") ? c.id.slice(0, c.id.lastIndexOf(\".\") + 1) : \"\"\n const notLoaded = (): never => {\n throw new Error(`${c.id}: command module is not loaded (use loadCommand)`)\n }\n return Object.freeze({\n [COMMAND]: true as const,\n description: c.description,\n get input(): never {\n return notLoaded()\n },\n output: undefined,\n readOnly: c.readOnly,\n destructive: c.destructive,\n requiresConfirmation: c.requiresConfirmation,\n scopes: Object.freeze([...c.scopes]),\n examples: Object.freeze([...c.examples]) as Command[\"examples\"],\n positional: Object.freeze([...c.positional]),\n aliases: Object.freeze(c.aliases.map((a) => (a.startsWith(parent) ? a.slice(parent.length) : a))),\n execute: notLoaded,\n })\n}\n\nfunction httpOptions(build: LoadedBuild, onerror: (error: Error) => void): McpHttpOptions {\n const { config } = build\n return {\n registry: build.registry,\n instrumentation: build.instrumentation,\n manifestHash: build.manifest.manifestHash,\n ...(config.app ? { app: config.app } : {}),\n ...(config.auth ? { auth: config.auth } : {}),\n ...(config.tools ? { tools: config.tools } : {}),\n ...(config.oauth ? { oauth: config.oauth } : {}),\n ...mcpDeclarations(config),\n server: config.server?.allowedOrigins ? { allowedOrigins: config.server.allowedOrigins } : {},\n onerror,\n }\n}\n\ninterface StartArgs {\n stdio: boolean\n port?: number\n host?: string\n}\n\n/** 반환 문자열 = 사용법 오류 */\nexport function parseStartArgs(argv: readonly string[]): StartArgs | string {\n const args: StartArgs = { stdio: false }\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!\n if (tok === \"--stdio\") {\n args.stdio = true\n continue\n }\n if (!tok.startsWith(\"--\")) return `unexpected argument \"${tok}\"`\n const eq = tok.indexOf(\"=\")\n const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq)\n if (name !== \"port\" && name !== \"host\") return `unknown flag --${name}`\n const value = eq < 0 ? argv[++i] : tok.slice(eq + 1)\n if (value === undefined || value === \"\") return `--${name} requires a value`\n if (name === \"port\") {\n const port = Number(value)\n if (!isPort(port)) return \"--port must be an integer between 0 and 65535\"\n args.port = port\n } else args.host = value\n }\n if (args.stdio && (args.port !== undefined || args.host !== undefined)) return \"--port and --host do not apply to --stdio\"\n return args\n}\n\nfunction envPort(value: string | undefined): number | undefined {\n if (value === undefined || value === \"\") return undefined\n const port = Number(value)\n if (!isPort(port)) throw new Error(`PORT must be an integer between 0 and 65535 (got \"${value}\")`)\n return port\n}\n\nfunction isPort(n: number): boolean {\n return Number.isInteger(n) && n >= 0 && n <= 65_535\n}\n\nfunction dirUrl(dir: string | URL): URL {\n if (typeof dir !== \"string\") return dir.href.endsWith(\"/\") ? dir : new URL(`${dir.href}/`)\n const abs = resolve(dir)\n return pathToFileURL(abs.endsWith(sep) ? abs : `${abs}${sep}`)\n}\n\nfunction processSignal(): { signal: AbortSignal; dispose(): void } {\n const controller = new AbortController()\n const abort = (): void => controller.abort()\n process.once(\"SIGINT\", abort)\n process.once(\"SIGTERM\", abort)\n return {\n signal: controller.signal,\n dispose() {\n process.off(\"SIGINT\", abort)\n process.off(\"SIGTERM\", abort)\n },\n }\n}\n"]}