@obov/mext 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/build-cli.js +2 -1
  2. package/dist/build-cli.js.map +1 -1
  3. package/dist/build.js +67 -4
  4. package/dist/build.js.map +1 -1
  5. package/dist/cli/ask-url.d.ts +16 -0
  6. package/dist/cli/ask-url.js +27 -0
  7. package/dist/cli/ask-url.js.map +1 -0
  8. package/dist/cli/build.js +9 -2
  9. package/dist/cli/build.js.map +1 -1
  10. package/dist/cli/dev-server.d.ts +7 -1
  11. package/dist/cli/dev-server.js +15 -2
  12. package/dist/cli/dev-server.js.map +1 -1
  13. package/dist/cli/dev.js +22 -7
  14. package/dist/cli/dev.js.map +1 -1
  15. package/dist/cli/login.d.ts +5 -0
  16. package/dist/cli/login.js +11 -7
  17. package/dist/cli/login.js.map +1 -1
  18. package/dist/cli/logout.js +4 -6
  19. package/dist/cli/logout.js.map +1 -1
  20. package/dist/cli/output.d.ts +1 -1
  21. package/dist/cli/output.js.map +1 -1
  22. package/dist/cli/run.js +7 -3
  23. package/dist/cli/run.js.map +1 -1
  24. package/dist/client/cache.d.ts +14 -4
  25. package/dist/client/cache.js +10 -3
  26. package/dist/client/cache.js.map +1 -1
  27. package/dist/client/main.js +11 -10
  28. package/dist/client/main.js.map +1 -1
  29. package/dist/client/manifest.d.ts +13 -1
  30. package/dist/client/manifest.js +43 -0
  31. package/dist/client/manifest.js.map +1 -1
  32. package/dist/client/runner.d.ts +3 -1
  33. package/dist/client/runner.js +80 -19
  34. package/dist/client/runner.js.map +1 -1
  35. package/dist/client/task.d.ts +47 -0
  36. package/dist/client/task.js +126 -0
  37. package/dist/client/task.js.map +1 -0
  38. package/dist/command.d.ts +30 -3
  39. package/dist/command.js +16 -0
  40. package/dist/command.js.map +1 -1
  41. package/dist/config.d.ts +40 -6
  42. package/dist/config.js +57 -8
  43. package/dist/config.js.map +1 -1
  44. package/dist/errors.d.ts +3 -1
  45. package/dist/errors.js.map +1 -1
  46. package/dist/index.d.ts +17 -5
  47. package/dist/index.js +9 -2
  48. package/dist/index.js.map +1 -1
  49. package/dist/instrumentation.d.ts +14 -2
  50. package/dist/instrumentation.js +12 -4
  51. package/dist/instrumentation.js.map +1 -1
  52. package/dist/invoke.d.ts +38 -0
  53. package/dist/invoke.js +72 -3
  54. package/dist/invoke.js.map +1 -1
  55. package/dist/local-bin.js +5 -3
  56. package/dist/local-bin.js.map +1 -1
  57. package/dist/manifest.d.ts +48 -3
  58. package/dist/manifest.js +14 -3
  59. package/dist/manifest.js.map +1 -1
  60. package/dist/mcp/constants.d.ts +4 -0
  61. package/dist/mcp/constants.js +4 -0
  62. package/dist/mcp/constants.js.map +1 -1
  63. package/dist/mcp/dispatch.d.ts +21 -7
  64. package/dist/mcp/dispatch.js +42 -7
  65. package/dist/mcp/dispatch.js.map +1 -1
  66. package/dist/mcp/http.d.ts +23 -5
  67. package/dist/mcp/http.js +64 -9
  68. package/dist/mcp/http.js.map +1 -1
  69. package/dist/mcp/index.d.ts +14 -6
  70. package/dist/mcp/index.js +7 -3
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/mcp/install.d.ts +1 -1
  73. package/dist/mcp/install.js +3 -2
  74. package/dist/mcp/install.js.map +1 -1
  75. package/dist/mcp/mrtr.d.ts +40 -8
  76. package/dist/mcp/mrtr.js +32 -11
  77. package/dist/mcp/mrtr.js.map +1 -1
  78. package/dist/mcp/prompts.d.ts +25 -0
  79. package/dist/mcp/prompts.js +164 -0
  80. package/dist/mcp/prompts.js.map +1 -0
  81. package/dist/mcp/resources.d.ts +48 -0
  82. package/dist/mcp/resources.js +142 -0
  83. package/dist/mcp/resources.js.map +1 -0
  84. package/dist/mcp/server.d.ts +30 -5
  85. package/dist/mcp/server.js +143 -16
  86. package/dist/mcp/server.js.map +1 -1
  87. package/dist/mcp/stdio.d.ts +5 -0
  88. package/dist/mcp/stdio.js +41 -3
  89. package/dist/mcp/stdio.js.map +1 -1
  90. package/dist/mcp/subscriptions.d.ts +61 -0
  91. package/dist/mcp/subscriptions.js +188 -0
  92. package/dist/mcp/subscriptions.js.map +1 -0
  93. package/dist/mcp/tasks.d.ts +127 -0
  94. package/dist/mcp/tasks.js +448 -0
  95. package/dist/mcp/tasks.js.map +1 -0
  96. package/dist/module-files.d.ts +24 -0
  97. package/dist/module-files.js +97 -0
  98. package/dist/module-files.js.map +1 -0
  99. package/dist/prompt.d.ts +133 -0
  100. package/dist/prompt.js +222 -0
  101. package/dist/prompt.js.map +1 -0
  102. package/dist/registry.d.ts +6 -0
  103. package/dist/registry.js.map +1 -1
  104. package/dist/remote/client.d.ts +17 -1
  105. package/dist/remote/client.js +18 -2
  106. package/dist/remote/client.js.map +1 -1
  107. package/dist/resource.d.ts +169 -0
  108. package/dist/resource.js +301 -0
  109. package/dist/resource.js.map +1 -0
  110. package/dist/route.d.ts +71 -0
  111. package/dist/route.js +280 -0
  112. package/dist/route.js.map +1 -0
  113. package/dist/runtime.d.ts +10 -0
  114. package/dist/runtime.js +11 -0
  115. package/dist/runtime.js.map +1 -0
  116. package/dist/start.d.ts +14 -3
  117. package/dist/start.js +76 -11
  118. package/dist/start.js.map +1 -1
  119. package/dist/state.d.ts +38 -0
  120. package/dist/state.js +98 -0
  121. package/dist/state.js.map +1 -0
  122. package/package.json +3 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-files.js","sourceRoot":"","sources":["../src/module-files.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, statSync, type Dirent } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport type { BuildIssue, BuildIssueCode } from \"./errors.ts\"\nimport { isSegment } from \"./ids.ts\"\n\n/**\n * commands/ 외 스캔 폴더 (resources/ · prompts/) 의 공통 파일 규칙 (§12.3 \"공통 = §3\").\n * private (`_` · `.` 접두) · 제외 (*.test.ts · *.spec.ts · *.d.ts) · 폴더 · 파일 kebab-case.\n * 폴더 없음 = 기능 없음 (issue 아님). 식별자 규칙 (중복 등) 은 호출자 몫\n */\n\nexport interface ModuleFile {\n /** 폴더 기준 상대 경로 (posix) */\n file: string\n /** 경로 세그먼트 (.ts 제외, index 포함) */\n segments: string[]\n}\n\nconst EXCLUDED_SUFFIXES = [\".test.ts\", \".spec.ts\", \".d.ts\"]\n\n/** label = issue 의 file 접두 (폴더 이름) */\nexport function walkModuleFiles(dir: string, label: string): { files: ModuleFile[]; issues: BuildIssue[] } {\n const root = resolve(dir)\n const files: ModuleFile[] = []\n const issues: BuildIssue[] = []\n if (!isDirectory(root)) return { files, issues }\n\n const visit = (abs: string, segments: string[]): void => {\n const entries = readdirSync(abs, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))\n for (const entry of entries) {\n const name = entry.name\n if (name.startsWith(\"_\") || name.startsWith(\".\")) continue\n const rel = [...segments, name].join(\"/\")\n const childAbs = join(abs, name)\n if (isDirEntry(abs, entry)) {\n if (!isSegment(name)) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `directory \"${name}\" is not kebab-case`, file: `${label}/${rel}` })\n continue\n }\n visit(childAbs, [...segments, name])\n continue\n }\n if (!name.endsWith(\".ts\") || EXCLUDED_SUFFIXES.some((s) => name.endsWith(s))) continue\n if (!isFileEntry(abs, entry)) continue\n const base = name.slice(0, -\".ts\".length)\n if (!isSegment(base)) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `file \"${name}\" is not kebab-case`, file: `${label}/${rel}` })\n continue\n }\n files.push({ file: rel, segments: [...segments, base] })\n }\n }\n\n visit(root, [])\n return { files, issues }\n}\n\n/** 각 파일의 default export import. import 실패 = LOAD_FAILED, guard 불통과 = invalidCode */\nexport async function importModuleDefaults<T>(\n dir: string,\n files: readonly ModuleFile[],\n label: string,\n guard: (value: unknown) => value is T,\n invalidCode: BuildIssueCode,\n definer: string,\n): Promise<{ modules: (ModuleFile & { value: T })[]; issues: BuildIssue[] }> {\n const root = resolve(dir)\n const modules: (ModuleFile & { value: T })[] = []\n const issues: BuildIssue[] = []\n for (const entry of files) {\n let mod: { default?: unknown }\n try {\n mod = (await import(pathToFileURL(join(root, entry.file)).href)) as { default?: unknown }\n } catch (error) {\n issues.push({ code: \"LOAD_FAILED\", message: error instanceof Error ? error.message : String(error), file: `${label}/${entry.file}` })\n continue\n }\n if (!guard(mod.default)) {\n issues.push({ code: invalidCode, message: `default export is not a ${definer} result`, file: `${label}/${entry.file}` })\n continue\n }\n modules.push({ ...entry, value: mod.default })\n }\n return { modules, issues }\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory()\n } catch {\n return false\n }\n}\n\nfunction isDirEntry(parent: string, entry: Dirent): boolean {\n return entry.isDirectory() || (entry.isSymbolicLink() && isDirectory(join(parent, entry.name)))\n}\n\nfunction isFileEntry(parent: string, entry: Dirent): boolean {\n if (entry.isFile()) return true\n if (!entry.isSymbolicLink()) return false\n try {\n return statSync(join(parent, entry.name)).isFile()\n } catch {\n return false\n }\n}\n"]}
@@ -0,0 +1,133 @@
1
+ import type { PromptMessage as McpPromptMessage } from "@modelcontextprotocol/server";
2
+ import * as z from "zod";
3
+ import type { MextApp } from "./command.ts";
4
+ import { type BuildIssue } from "./errors.ts";
5
+ import type { Principal } from "./principal.ts";
6
+ import type { JsonSchema } from "./registry.ts";
7
+ /**
8
+ * MCP 프롬프트 (§12.3). prompts/**\/*.ts 파일 하나 = definePrompt 하나. 이름 = 점 구분 경로 (prompts/github/review.ts → github.review).
9
+ * MCP 전용 (CLI 노출 없음). 실행 어댑터 = mcp/prompts.ts
10
+ */
11
+ /** render 의 ctx. ask · progress 없음 */
12
+ export interface PromptContext {
13
+ principal: Principal;
14
+ signal: AbortSignal;
15
+ app: MextApp;
16
+ }
17
+ /** complete 의 ctx. arguments = 이미 정한 다른 인자 */
18
+ export interface PromptCompleteContext extends PromptContext {
19
+ arguments: Record<string, string>;
20
+ }
21
+ /** completion/complete 후보 (§12.3 completion) */
22
+ export type PromptCompleter = (value: string, ctx: PromptCompleteContext) => readonly string[] | Promise<readonly string[]>;
23
+ /** MCP PromptMessage (prompts/get 결과 messages 항목: role · content = text · image · audio · resource_link · resource) */
24
+ export type PromptMessage = McpPromptMessage;
25
+ /** render 반환. string = user 메시지 1개, 배열 = 그대로 */
26
+ export type PromptResult = string | readonly PromptMessage[];
27
+ /** prompts/list 의 arguments 항목 */
28
+ export interface PromptArgument {
29
+ name: string;
30
+ description?: string;
31
+ required: boolean;
32
+ }
33
+ export type PromptArgs = z.ZodObject;
34
+ type ArgKey<A extends PromptArgs> = keyof z.input<A> & string;
35
+ export interface PromptDefinition<A extends PromptArgs = PromptArgs> {
36
+ title?: string;
37
+ /** 필수. prompts/list 설명 */
38
+ description: string;
39
+ /** 선택. 문자열 필드만 (그 외 = 빌드 오류). .describe() = 인자 설명 */
40
+ args?: A;
41
+ /** 인자별 completion/complete */
42
+ complete?: {
43
+ [K in ArgKey<A>]?: PromptCompleter;
44
+ };
45
+ /** prompts/get · completion 에 필요한 scopes. 전부 보유해야 실행 */
46
+ scopes?: readonly string[];
47
+ render(args: z.output<A>, ctx: PromptContext): PromptResult | Promise<PromptResult>;
48
+ }
49
+ export declare const PROMPT: unique symbol;
50
+ export interface Prompt<A extends PromptArgs = PromptArgs> {
51
+ readonly [PROMPT]: true;
52
+ readonly title: string | undefined;
53
+ readonly description: string;
54
+ readonly args: A | undefined;
55
+ readonly scopes: readonly string[];
56
+ readonly complete: Readonly<Record<string, PromptCompleter>>;
57
+ render(args: z.output<A>, ctx: PromptContext): PromptResult | Promise<PromptResult>;
58
+ }
59
+ export declare function definePrompt<A extends PromptArgs = PromptArgs>(definition: PromptDefinition<A>): Prompt<A>;
60
+ export declare function isPrompt(value: unknown): value is Prompt;
61
+ /** 등록된 프롬프트의 메타데이터 + 모듈 로더. eager (스캔) = load 가 정의 그대로, lazy (mext start) = 모듈 import (1회 캐시) */
62
+ export interface RegisteredPrompt {
63
+ /** 점 구분 경로 */
64
+ name: string;
65
+ title: string | undefined;
66
+ description: string;
67
+ scopes: readonly string[];
68
+ /** args 의 JSON Schema 2020-12 (io input). args 없음 = undefined */
69
+ argsSchema: JsonSchema | undefined;
70
+ /** prompts/list arguments (argsSchema 에서) */
71
+ arguments: readonly PromptArgument[];
72
+ /** complete 를 정의한 인자 (정렬) */
73
+ complete: readonly string[];
74
+ /** promptsDir 기준 상대 경로 (posix) */
75
+ file: string | undefined;
76
+ load(): Promise<Prompt>;
77
+ }
78
+ /** 이름 → 프롬프트. 이름 순 */
79
+ export type PromptSet = ReadonlyMap<string, RegisteredPrompt>;
80
+ /** manifest 의 프롬프트 (mext build). module 은 BuildManifestPrompt */
81
+ export interface ManifestPrompt {
82
+ name: string;
83
+ title?: string;
84
+ description: string;
85
+ scopes: string[];
86
+ /** args 의 JSON Schema 2020-12. args 없으면 생략 */
87
+ argsSchema?: JsonSchema;
88
+ complete: string[];
89
+ /** promptsDir 기준 상대 경로 (posix) */
90
+ file?: string;
91
+ }
92
+ export interface PromptInput {
93
+ name: string;
94
+ prompt: Prompt;
95
+ /** promptsDir 기준 상대 경로 (posix). 스캔 결과에만 있음 */
96
+ file?: string;
97
+ }
98
+ export interface LazyPromptInput extends ManifestPrompt {
99
+ /** get · complete 때 1회 호출 (결과 캐시, 실패는 다음 호출에서 재시도) */
100
+ load: () => Promise<Prompt>;
101
+ }
102
+ /** 프롬프트 목록 → PromptSet. 중복 이름 · 문자열 아닌 args = MextBuildError */
103
+ export declare function createPromptSet(inputs: readonly PromptInput[]): PromptSet;
104
+ /**
105
+ * createPromptSet 의 수집형 (mext build). 던지지 않는다. prompts = 규칙을 통과한 프롬프트만.
106
+ * label = issue 의 file 접두 (promptsDir 이름, "prompts/github/review.ts")
107
+ */
108
+ export declare function collectPromptSet(inputs: readonly PromptInput[], label?: string): {
109
+ prompts: PromptSet;
110
+ issues: BuildIssue[];
111
+ };
112
+ /** manifest (mext build 산출) → PromptSet. 모듈은 get · complete 때 import */
113
+ export declare function createLazyPromptSet(inputs: readonly LazyPromptInput[]): PromptSet;
114
+ /** 프롬프트 메타데이터 (manifest) */
115
+ export declare function describePrompt(entry: RegisteredPrompt): ManifestPrompt;
116
+ /** args JSON Schema → prompts/list arguments (properties 순서 · description · required) */
117
+ export declare function promptArguments(schema: JsonSchema | undefined): PromptArgument[];
118
+ /**
119
+ * prompts/ 스캔 (수집형, mext build). 폴더 없음 = 프롬프트 없음 (issue 아님).
120
+ * 파일 규칙 = §3 의 private · 제외 · kebab-case. 이름 = 점 구분 경로, index.ts = 폴더 이름 (루트 index.ts = 이름 없음 오류).
121
+ * 같은 이름 (x.ts + x/index.ts) = collectPromptSet 의 DUPLICATE_ID
122
+ */
123
+ export declare function collectPrompts(dir: string): Promise<{
124
+ inputs: PromptInput[];
125
+ issues: BuildIssue[];
126
+ }>;
127
+ /** Registry 에 프롬프트 집합 연결 (없으면 그대로) */
128
+ export declare function withPrompts<R extends object>(registry: R, prompts: PromptSet | undefined): R & {
129
+ prompts?: PromptSet;
130
+ };
131
+ /** 개발 모드 런타임 스캔: prompts/ → PromptSet. 폴더 없음 · 프롬프트 없음 = undefined (기능 없음). 규칙 위반 = MextBuildError */
132
+ export declare function loadPromptSet(dir: string): Promise<PromptSet | undefined>;
133
+ export {};
package/dist/prompt.js ADDED
@@ -0,0 +1,222 @@
1
+ import { basename } from "node:path";
2
+ import * as z from "zod";
3
+ import { MextBuildError } from "./errors.js";
4
+ import { importModuleDefaults, walkModuleFiles } from "./module-files.js";
5
+ export const PROMPT = Symbol.for("@obov/mext/prompt");
6
+ export function definePrompt(definition) {
7
+ const d = definition;
8
+ const fail = (message) => {
9
+ throw new TypeError(`definePrompt: ${message}`);
10
+ };
11
+ if (typeof d.description !== "string" || d.description.trim() === "")
12
+ fail("description must be a non-empty string");
13
+ if (typeof d.render !== "function")
14
+ fail("render must be a function");
15
+ if (d.title !== undefined && typeof d.title !== "string")
16
+ fail("title must be a string");
17
+ if (d.args !== undefined && !isObjectSchema(d.args))
18
+ fail("args must be a top-level z.object schema");
19
+ if (d.scopes !== undefined && (!Array.isArray(d.scopes) || d.scopes.some((s) => typeof s !== "string"))) {
20
+ fail("scopes must be an array of strings");
21
+ }
22
+ const keys = d.args ? Object.keys(d.args.shape) : [];
23
+ const complete = {};
24
+ if (d.complete !== undefined) {
25
+ if (typeof d.complete !== "object" || d.complete === null || Array.isArray(d.complete))
26
+ fail("complete must be an object");
27
+ for (const [key, fn] of Object.entries(d.complete)) {
28
+ if (!keys.includes(key))
29
+ fail(`complete.${key}: args has no "${key}" field`);
30
+ if (typeof fn !== "function")
31
+ fail(`complete.${key} must be a function`);
32
+ complete[key] = fn;
33
+ }
34
+ }
35
+ const render = d.render;
36
+ return Object.freeze({
37
+ [PROMPT]: true,
38
+ title: d.title,
39
+ description: d.description,
40
+ args: d.args,
41
+ scopes: Object.freeze([...(d.scopes ?? [])]),
42
+ complete: Object.freeze(complete),
43
+ render(args, ctx) {
44
+ return render.call(definition, args, ctx);
45
+ },
46
+ });
47
+ }
48
+ export function isPrompt(value) {
49
+ return typeof value === "object" && value !== null && value[PROMPT] === true;
50
+ }
51
+ function isObjectSchema(value) {
52
+ const def = value?._zod?.def;
53
+ return def?.type === "object" && typeof value.safeParseAsync === "function";
54
+ }
55
+ /** 프롬프트 목록 → PromptSet. 중복 이름 · 문자열 아닌 args = MextBuildError */
56
+ export function createPromptSet(inputs) {
57
+ const { prompts, issues } = collectPromptSet(inputs);
58
+ if (issues.length > 0)
59
+ throw new MextBuildError(issues);
60
+ return prompts;
61
+ }
62
+ /**
63
+ * createPromptSet 의 수집형 (mext build). 던지지 않는다. prompts = 규칙을 통과한 프롬프트만.
64
+ * label = issue 의 file 접두 (promptsDir 이름, "prompts/github/review.ts")
65
+ */
66
+ export function collectPromptSet(inputs, label) {
67
+ const issues = [];
68
+ const byName = new Map();
69
+ for (const { name, prompt, file } of inputs) {
70
+ const where = { id: name, ...issueFile(file, label) };
71
+ if (!isPrompt(prompt)) {
72
+ issues.push({ code: "INVALID_PROMPT", message: "not a definePrompt result", ...where });
73
+ continue;
74
+ }
75
+ let argsSchema;
76
+ try {
77
+ argsSchema = prompt.args ? z.toJSONSchema(prompt.args, { target: "draft-2020-12", io: "input", unrepresentable: "throw" }) : undefined;
78
+ }
79
+ catch (error) {
80
+ issues.push({ code: "SCHEMA_UNREPRESENTABLE", message: error instanceof Error ? error.message : String(error), ...where });
81
+ continue;
82
+ }
83
+ const nonString = nonStringArgs(argsSchema);
84
+ if (nonString.length > 0) {
85
+ issues.push({ code: "INVALID_PROMPT", message: `args must be string fields (not strings: ${nonString.join(", ")})`, ...where });
86
+ continue;
87
+ }
88
+ const prev = byName.get(name);
89
+ if (prev) {
90
+ const other = prev.file === undefined ? prev.name : (issueFile(prev.file, label).file ?? prev.file);
91
+ issues.push({ code: "DUPLICATE_ID", message: `duplicate prompt name ${name}, also ${other}`, ...where });
92
+ continue;
93
+ }
94
+ byName.set(name, {
95
+ ...metadata(describe(name, prompt, argsSchema, file)),
96
+ load: () => Promise.resolve(prompt),
97
+ });
98
+ }
99
+ return { prompts: sortByName(byName), issues };
100
+ }
101
+ /** manifest (mext build 산출) → PromptSet. 모듈은 get · complete 때 import */
102
+ export function createLazyPromptSet(inputs) {
103
+ const byName = new Map();
104
+ for (const input of inputs) {
105
+ const load = input.load;
106
+ let pending;
107
+ byName.set(input.name, {
108
+ ...metadata(input),
109
+ load: () => (pending ??= load().catch((error) => {
110
+ pending = undefined;
111
+ throw error;
112
+ })),
113
+ });
114
+ }
115
+ return sortByName(byName);
116
+ }
117
+ /** 프롬프트 메타데이터 (manifest) */
118
+ export function describePrompt(entry) {
119
+ const out = {
120
+ name: entry.name,
121
+ description: entry.description,
122
+ scopes: [...entry.scopes],
123
+ complete: [...entry.complete],
124
+ };
125
+ if (entry.title !== undefined)
126
+ out.title = entry.title;
127
+ if (entry.argsSchema !== undefined)
128
+ out.argsSchema = entry.argsSchema;
129
+ if (entry.file !== undefined)
130
+ out.file = entry.file;
131
+ return out;
132
+ }
133
+ /** args JSON Schema → prompts/list arguments (properties 순서 · description · required) */
134
+ export function promptArguments(schema) {
135
+ const properties = (schema?.properties ?? {});
136
+ const required = new Set(Array.isArray(schema?.required) ? schema.required : []);
137
+ return Object.entries(properties).map(([name, property]) => ({
138
+ name,
139
+ ...(typeof property.description === "string" ? { description: property.description } : {}),
140
+ required: required.has(name),
141
+ }));
142
+ }
143
+ /** 문자열이 아닌 args 필드 (JSON Schema type 이 "string" 아님) */
144
+ function nonStringArgs(schema) {
145
+ const properties = (schema?.properties ?? {});
146
+ return Object.entries(properties)
147
+ .filter(([, property]) => property.type !== "string")
148
+ .map(([name]) => name);
149
+ }
150
+ function describe(name, prompt, argsSchema, file) {
151
+ return {
152
+ name,
153
+ ...(prompt.title !== undefined ? { title: prompt.title } : {}),
154
+ description: prompt.description,
155
+ scopes: [...prompt.scopes],
156
+ ...(argsSchema !== undefined ? { argsSchema } : {}),
157
+ complete: Object.keys(prompt.complete).sort(),
158
+ ...(file !== undefined ? { file } : {}),
159
+ };
160
+ }
161
+ function metadata(m) {
162
+ return {
163
+ name: m.name,
164
+ title: m.title,
165
+ description: m.description,
166
+ scopes: Object.freeze([...m.scopes]),
167
+ argsSchema: m.argsSchema,
168
+ arguments: Object.freeze(promptArguments(m.argsSchema)),
169
+ complete: Object.freeze([...m.complete]),
170
+ file: m.file,
171
+ };
172
+ }
173
+ function sortByName(byName) {
174
+ return new Map([...byName].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
175
+ }
176
+ function issueFile(file, label) {
177
+ if (file === undefined)
178
+ return {};
179
+ return { file: label === undefined ? file : `${label}/${file}` };
180
+ }
181
+ /* ---------------------------------------------------------------- 스캔 (prompts/) */
182
+ /**
183
+ * prompts/ 스캔 (수집형, mext build). 폴더 없음 = 프롬프트 없음 (issue 아님).
184
+ * 파일 규칙 = §3 의 private · 제외 · kebab-case. 이름 = 점 구분 경로, index.ts = 폴더 이름 (루트 index.ts = 이름 없음 오류).
185
+ * 같은 이름 (x.ts + x/index.ts) = collectPromptSet 의 DUPLICATE_ID
186
+ */
187
+ export async function collectPrompts(dir) {
188
+ const label = basename(dir);
189
+ const walked = walkModuleFiles(dir, label);
190
+ const issues = [...walked.issues];
191
+ const files = walked.files.filter((f) => {
192
+ if (f.segments.length === 1 && f.segments[0] === "index") {
193
+ issues.push({ code: "INVALID_PROMPT", message: "index.ts at the prompts root has no name; name the file", file: `${label}/${f.file}` });
194
+ return false;
195
+ }
196
+ return true;
197
+ });
198
+ const imported = await importModuleDefaults(dir, files, label, isPrompt, "INVALID_PROMPT", "definePrompt");
199
+ return {
200
+ inputs: imported.modules.map(({ file, segments, value }) => ({ name: promptName(segments), prompt: value, file })),
201
+ issues: [...issues, ...imported.issues],
202
+ };
203
+ }
204
+ /** 경로 세그먼트 → 이름. 끝 index = 폴더 이름 */
205
+ function promptName(segments) {
206
+ const parts = segments.at(-1) === "index" ? segments.slice(0, -1) : segments;
207
+ return parts.join(".");
208
+ }
209
+ /** Registry 에 프롬프트 집합 연결 (없으면 그대로) */
210
+ export function withPrompts(registry, prompts) {
211
+ return prompts ? { ...registry, prompts } : registry;
212
+ }
213
+ /** 개발 모드 런타임 스캔: prompts/ → PromptSet. 폴더 없음 · 프롬프트 없음 = undefined (기능 없음). 규칙 위반 = MextBuildError */
214
+ export async function loadPromptSet(dir) {
215
+ const scanned = await collectPrompts(dir);
216
+ const { prompts, issues } = collectPromptSet(scanned.inputs, basename(dir));
217
+ const all = [...scanned.issues, ...issues];
218
+ if (all.length > 0)
219
+ throw new MextBuildError(all);
220
+ return prompts.size > 0 ? prompts : undefined;
221
+ }
222
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename } from \"node:path\"\nimport type { PromptMessage as McpPromptMessage } from \"@modelcontextprotocol/server\"\nimport * as z from \"zod\"\nimport type { MextApp } from \"./command.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport { importModuleDefaults, walkModuleFiles } from \"./module-files.ts\"\nimport type { Principal } from \"./principal.ts\"\nimport type { JsonSchema } from \"./registry.ts\"\n\n/**\n * MCP 프롬프트 (§12.3). prompts/**\\/*.ts 파일 하나 = definePrompt 하나. 이름 = 점 구분 경로 (prompts/github/review.ts → github.review).\n * MCP 전용 (CLI 노출 없음). 실행 어댑터 = mcp/prompts.ts\n */\n\n/** render 의 ctx. ask · progress 없음 */\nexport interface PromptContext {\n principal: Principal\n signal: AbortSignal\n app: MextApp\n}\n\n/** complete 의 ctx. arguments = 이미 정한 다른 인자 */\nexport interface PromptCompleteContext extends PromptContext {\n arguments: Record<string, string>\n}\n\n/** completion/complete 후보 (§12.3 completion) */\nexport type PromptCompleter = (value: string, ctx: PromptCompleteContext) => readonly string[] | Promise<readonly string[]>\n\n/** MCP PromptMessage (prompts/get 결과 messages 항목: role · content = text · image · audio · resource_link · resource) */\nexport type PromptMessage = McpPromptMessage\n\n/** render 반환. string = user 메시지 1개, 배열 = 그대로 */\nexport type PromptResult = string | readonly PromptMessage[]\n\n/** prompts/list 의 arguments 항목 */\nexport interface PromptArgument {\n name: string\n description?: string\n required: boolean\n}\n\nexport type PromptArgs = z.ZodObject\n\ntype ArgKey<A extends PromptArgs> = keyof z.input<A> & string\n\nexport interface PromptDefinition<A extends PromptArgs = PromptArgs> {\n title?: string\n /** 필수. prompts/list 설명 */\n description: string\n /** 선택. 문자열 필드만 (그 외 = 빌드 오류). .describe() = 인자 설명 */\n args?: A\n /** 인자별 completion/complete */\n complete?: { [K in ArgKey<A>]?: PromptCompleter }\n /** prompts/get · completion 에 필요한 scopes. 전부 보유해야 실행 */\n scopes?: readonly string[]\n render(args: z.output<A>, ctx: PromptContext): PromptResult | Promise<PromptResult>\n}\n\nexport const PROMPT: unique symbol = Symbol.for(\"@obov/mext/prompt\") as never\n\nexport interface Prompt<A extends PromptArgs = PromptArgs> {\n readonly [PROMPT]: true\n readonly title: string | undefined\n readonly description: string\n readonly args: A | undefined\n readonly scopes: readonly string[]\n readonly complete: Readonly<Record<string, PromptCompleter>>\n render(args: z.output<A>, ctx: PromptContext): PromptResult | Promise<PromptResult>\n}\n\nexport function definePrompt<A extends PromptArgs = PromptArgs>(definition: PromptDefinition<A>): Prompt<A> {\n const d = definition as Partial<PromptDefinition<A>>\n const fail = (message: string): never => {\n throw new TypeError(`definePrompt: ${message}`)\n }\n if (typeof d.description !== \"string\" || d.description.trim() === \"\") fail(\"description must be a non-empty string\")\n if (typeof d.render !== \"function\") fail(\"render must be a function\")\n if (d.title !== undefined && typeof d.title !== \"string\") fail(\"title must be a string\")\n if (d.args !== undefined && !isObjectSchema(d.args)) fail(\"args must be a top-level z.object schema\")\n if (d.scopes !== undefined && (!Array.isArray(d.scopes) || d.scopes.some((s) => typeof s !== \"string\"))) {\n fail(\"scopes must be an array of strings\")\n }\n const keys = d.args ? Object.keys(d.args.shape) : []\n const complete: Record<string, PromptCompleter> = {}\n if (d.complete !== undefined) {\n if (typeof d.complete !== \"object\" || d.complete === null || Array.isArray(d.complete)) fail(\"complete must be an object\")\n for (const [key, fn] of Object.entries(d.complete as Record<string, unknown>)) {\n if (!keys.includes(key)) fail(`complete.${key}: args has no \"${key}\" field`)\n if (typeof fn !== \"function\") fail(`complete.${key} must be a function`)\n complete[key] = fn as PromptCompleter\n }\n }\n const render = d.render!\n return Object.freeze({\n [PROMPT]: true as const,\n title: d.title,\n description: d.description!,\n args: d.args,\n scopes: Object.freeze([...(d.scopes ?? [])]),\n complete: Object.freeze(complete),\n render(args: z.output<A>, ctx: PromptContext) {\n return render.call(definition, args, ctx)\n },\n })\n}\n\nexport function isPrompt(value: unknown): value is Prompt {\n return typeof value === \"object\" && value !== null && (value as Record<symbol, unknown>)[PROMPT] === true\n}\n\nfunction isObjectSchema(value: unknown): value is PromptArgs {\n const def = (value as { _zod?: { def?: { type?: unknown } } } | undefined)?._zod?.def\n return def?.type === \"object\" && typeof (value as { safeParseAsync?: unknown }).safeParseAsync === \"function\"\n}\n\n/* ---------------------------------------------------------------- 프롬프트 집합 (Registry.prompts) */\n\n/** 등록된 프롬프트의 메타데이터 + 모듈 로더. eager (스캔) = load 가 정의 그대로, lazy (mext start) = 모듈 import (1회 캐시) */\nexport interface RegisteredPrompt {\n /** 점 구분 경로 */\n name: string\n title: string | undefined\n description: string\n scopes: readonly string[]\n /** args 의 JSON Schema 2020-12 (io input). args 없음 = undefined */\n argsSchema: JsonSchema | undefined\n /** prompts/list arguments (argsSchema 에서) */\n arguments: readonly PromptArgument[]\n /** complete 를 정의한 인자 (정렬) */\n complete: readonly string[]\n /** promptsDir 기준 상대 경로 (posix) */\n file: string | undefined\n load(): Promise<Prompt>\n}\n\n/** 이름 → 프롬프트. 이름 순 */\nexport type PromptSet = ReadonlyMap<string, RegisteredPrompt>\n\n/** manifest 의 프롬프트 (mext build). module 은 BuildManifestPrompt */\nexport interface ManifestPrompt {\n name: string\n title?: string\n description: string\n scopes: string[]\n /** args 의 JSON Schema 2020-12. args 없으면 생략 */\n argsSchema?: JsonSchema\n complete: string[]\n /** promptsDir 기준 상대 경로 (posix) */\n file?: string\n}\n\nexport interface PromptInput {\n name: string\n prompt: Prompt\n /** promptsDir 기준 상대 경로 (posix). 스캔 결과에만 있음 */\n file?: string\n}\n\nexport interface LazyPromptInput extends ManifestPrompt {\n /** get · complete 때 1회 호출 (결과 캐시, 실패는 다음 호출에서 재시도) */\n load: () => Promise<Prompt>\n}\n\n/** 프롬프트 목록 → PromptSet. 중복 이름 · 문자열 아닌 args = MextBuildError */\nexport function createPromptSet(inputs: readonly PromptInput[]): PromptSet {\n const { prompts, issues } = collectPromptSet(inputs)\n if (issues.length > 0) throw new MextBuildError(issues)\n return prompts\n}\n\n/**\n * createPromptSet 의 수집형 (mext build). 던지지 않는다. prompts = 규칙을 통과한 프롬프트만.\n * label = issue 의 file 접두 (promptsDir 이름, \"prompts/github/review.ts\")\n */\nexport function collectPromptSet(inputs: readonly PromptInput[], label?: string): { prompts: PromptSet; issues: BuildIssue[] } {\n const issues: BuildIssue[] = []\n const byName = new Map<string, RegisteredPrompt>()\n for (const { name, prompt, file } of inputs) {\n const where = { id: name, ...issueFile(file, label) }\n if (!isPrompt(prompt)) {\n issues.push({ code: \"INVALID_PROMPT\", message: \"not a definePrompt result\", ...where })\n continue\n }\n let argsSchema: JsonSchema | undefined\n try {\n argsSchema = prompt.args ? (z.toJSONSchema(prompt.args, { target: \"draft-2020-12\", io: \"input\", unrepresentable: \"throw\" }) as JsonSchema) : undefined\n } catch (error) {\n issues.push({ code: \"SCHEMA_UNREPRESENTABLE\", message: error instanceof Error ? error.message : String(error), ...where })\n continue\n }\n const nonString = nonStringArgs(argsSchema)\n if (nonString.length > 0) {\n issues.push({ code: \"INVALID_PROMPT\", message: `args must be string fields (not strings: ${nonString.join(\", \")})`, ...where })\n continue\n }\n const prev = byName.get(name)\n if (prev) {\n const other = prev.file === undefined ? prev.name : (issueFile(prev.file, label).file ?? prev.file)\n issues.push({ code: \"DUPLICATE_ID\", message: `duplicate prompt name ${name}, also ${other}`, ...where })\n continue\n }\n byName.set(name, {\n ...metadata(describe(name, prompt, argsSchema, file)),\n load: () => Promise.resolve(prompt),\n })\n }\n return { prompts: sortByName(byName), issues }\n}\n\n/** manifest (mext build 산출) → PromptSet. 모듈은 get · complete 때 import */\nexport function createLazyPromptSet(inputs: readonly LazyPromptInput[]): PromptSet {\n const byName = new Map<string, RegisteredPrompt>()\n for (const input of inputs) {\n const load = input.load\n let pending: Promise<Prompt> | undefined\n byName.set(input.name, {\n ...metadata(input),\n load: () =>\n (pending ??= load().catch((error: unknown) => {\n pending = undefined\n throw error\n })),\n })\n }\n return sortByName(byName)\n}\n\n/** 프롬프트 메타데이터 (manifest) */\nexport function describePrompt(entry: RegisteredPrompt): ManifestPrompt {\n const out: ManifestPrompt = {\n name: entry.name,\n description: entry.description,\n scopes: [...entry.scopes],\n complete: [...entry.complete],\n }\n if (entry.title !== undefined) out.title = entry.title\n if (entry.argsSchema !== undefined) out.argsSchema = entry.argsSchema\n if (entry.file !== undefined) out.file = entry.file\n return out\n}\n\n/** args JSON Schema → prompts/list arguments (properties 순서 · description · required) */\nexport function promptArguments(schema: JsonSchema | undefined): PromptArgument[] {\n const properties = (schema?.properties ?? {}) as Record<string, { description?: unknown }>\n const required = new Set(Array.isArray(schema?.required) ? (schema.required as string[]) : [])\n return Object.entries(properties).map(([name, property]) => ({\n name,\n ...(typeof property.description === \"string\" ? { description: property.description } : {}),\n required: required.has(name),\n }))\n}\n\n/** 문자열이 아닌 args 필드 (JSON Schema type 이 \"string\" 아님) */\nfunction nonStringArgs(schema: JsonSchema | undefined): string[] {\n const properties = (schema?.properties ?? {}) as Record<string, { type?: unknown }>\n return Object.entries(properties)\n .filter(([, property]) => property.type !== \"string\")\n .map(([name]) => name)\n}\n\nfunction describe(name: string, prompt: Prompt, argsSchema: JsonSchema | undefined, file: string | undefined): ManifestPrompt {\n return {\n name,\n ...(prompt.title !== undefined ? { title: prompt.title } : {}),\n description: prompt.description,\n scopes: [...prompt.scopes],\n ...(argsSchema !== undefined ? { argsSchema } : {}),\n complete: Object.keys(prompt.complete).sort(),\n ...(file !== undefined ? { file } : {}),\n }\n}\n\nfunction metadata(m: ManifestPrompt): Omit<RegisteredPrompt, \"load\"> {\n return {\n name: m.name,\n title: m.title,\n description: m.description,\n scopes: Object.freeze([...m.scopes]),\n argsSchema: m.argsSchema,\n arguments: Object.freeze(promptArguments(m.argsSchema)),\n complete: Object.freeze([...m.complete]),\n file: m.file,\n }\n}\n\nfunction sortByName(byName: ReadonlyMap<string, RegisteredPrompt>): PromptSet {\n return new Map([...byName].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))\n}\n\nfunction issueFile(file: string | undefined, label: string | undefined): { file?: string } {\n if (file === undefined) return {}\n return { file: label === undefined ? file : `${label}/${file}` }\n}\n\n/* ---------------------------------------------------------------- 스캔 (prompts/) */\n\n/**\n * prompts/ 스캔 (수집형, mext build). 폴더 없음 = 프롬프트 없음 (issue 아님).\n * 파일 규칙 = §3 의 private · 제외 · kebab-case. 이름 = 점 구분 경로, index.ts = 폴더 이름 (루트 index.ts = 이름 없음 오류).\n * 같은 이름 (x.ts + x/index.ts) = collectPromptSet 의 DUPLICATE_ID\n */\nexport async function collectPrompts(dir: string): Promise<{ inputs: PromptInput[]; issues: BuildIssue[] }> {\n const label = basename(dir)\n const walked = walkModuleFiles(dir, label)\n const issues = [...walked.issues]\n const files = walked.files.filter((f) => {\n if (f.segments.length === 1 && f.segments[0] === \"index\") {\n issues.push({ code: \"INVALID_PROMPT\", message: \"index.ts at the prompts root has no name; name the file\", file: `${label}/${f.file}` })\n return false\n }\n return true\n })\n const imported = await importModuleDefaults(dir, files, label, isPrompt, \"INVALID_PROMPT\", \"definePrompt\")\n return {\n inputs: imported.modules.map(({ file, segments, value }) => ({ name: promptName(segments), prompt: value, file })),\n issues: [...issues, ...imported.issues],\n }\n}\n\n/** 경로 세그먼트 → 이름. 끝 index = 폴더 이름 */\nfunction promptName(segments: readonly string[]): string {\n const parts = segments.at(-1) === \"index\" ? segments.slice(0, -1) : segments\n return parts.join(\".\")\n}\n\n/** Registry 에 프롬프트 집합 연결 (없으면 그대로) */\nexport function withPrompts<R extends object>(registry: R, prompts: PromptSet | undefined): R & { prompts?: PromptSet } {\n return prompts ? { ...registry, prompts } : registry\n}\n\n/** 개발 모드 런타임 스캔: prompts/ → PromptSet. 폴더 없음 · 프롬프트 없음 = undefined (기능 없음). 규칙 위반 = MextBuildError */\nexport async function loadPromptSet(dir: string): Promise<PromptSet | undefined> {\n const scanned = await collectPrompts(dir)\n const { prompts, issues } = collectPromptSet(scanned.inputs, basename(dir))\n const all = [...scanned.issues, ...issues]\n if (all.length > 0) throw new MextBuildError(all)\n return prompts.size > 0 ? prompts : undefined\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  import { type Command } from "./command.ts";
2
2
  import { type BuildIssue } from "./errors.ts";
3
+ import type { PromptSet } from "./prompt.ts";
4
+ import type { ResourceSet } from "./resource.ts";
3
5
  export type JsonSchema = Record<string, unknown>;
4
6
  export interface RegistryCommandInput {
5
7
  /** 정식 ID. 점 구분 kebab-case 세그먼트 */
@@ -52,6 +54,10 @@ export interface Registry {
52
54
  readonly aliases: ReadonlyMap<string, string>;
53
55
  /** 네임스페이스 ID → 정보. 루트 ("") 포함, ID 순 */
54
56
  readonly namespaces: ReadonlyMap<string, NamespaceInfo>;
57
+ /** MCP 자원 (resources/, §12.3). 없음 = 기능 없음 (capability 미선언). 명령어 Map 과 별개 */
58
+ readonly resources?: ResourceSet;
59
+ /** MCP 프롬프트 (prompts/, §12.3). 없음 = 기능 없음 (capability 미선언). 이름 순 */
60
+ readonly prompts?: PromptSet;
55
61
  }
56
62
  /** 명령어 목록에서 Registry 생성. ID · 별칭 · 스키마 규칙 위반은 MextBuildError */
57
63
  export declare function createRegistry(input: RegistryInput): Registry;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from \"zod\"\nimport { isCommand, type Command } from \"./command.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport { isSegment, parentNamespace, RESERVED_ROOT_SEGMENTS } from \"./ids.ts\"\n\nexport type JsonSchema = Record<string, unknown>\n\nexport interface RegistryCommandInput {\n /** 정식 ID. 점 구분 kebab-case 세그먼트 */\n id: string\n command: Command\n /** commands/ 기준 상대 경로 (posix). 스캔 결과에만 있음 */\n file?: string\n}\n\nexport interface RegistryNamespaceInput {\n /** \"\" = 루트 */\n id: string\n description?: string\n}\n\nexport interface RegistryInput {\n commands: readonly RegistryCommandInput[]\n namespaces?: readonly RegistryNamespaceInput[]\n}\n\nexport interface RegisteredCommand {\n id: string\n /**\n * 메타데이터 (description · 안전성 · scopes · examples · positional · aliases) 의 원본.\n * eager (createRegistry) = 실행 가능한 명령어 그대로. lazy (createLazyRegistry) = manifest 로 만든 메타데이터만\n * (input · execute 는 load() 결과에서)\n */\n command: Command\n /** lazy Registry: 명령어 모듈 import (1회 캐시) → 실행 가능한 명령어. 없음 = command 가 실행 가능. loadCommand 로 호출 */\n load?: () => Promise<Command>\n /** 별칭의 전체 ID (정렬) */\n aliases: readonly string[]\n file: string | undefined\n /** input 의 JSON Schema 2020-12 (io: input) */\n inputSchema: JsonSchema\n /** output 의 JSON Schema 2020-12 (io: output) */\n outputSchema: JsonSchema | undefined\n}\n\nexport interface NamespaceInfo {\n /** \"\" = 루트 */\n id: string\n /** _index.md 내용 */\n description: string | undefined\n /** 직속 명령어 정식 ID (정렬). index.ts 명령어 (네임스페이스와 같은 ID) 는 상위 네임스페이스에 속한다 */\n commands: readonly string[]\n /** 직속 하위 네임스페이스 ID (정렬) */\n namespaces: readonly string[]\n}\n\nexport interface Registry {\n /** 정식 ID → 명령어. ID 순 */\n readonly commands: ReadonlyMap<string, RegisteredCommand>\n /** 별칭 전체 ID → 정식 ID */\n readonly aliases: ReadonlyMap<string, string>\n /** 네임스페이스 ID → 정보. 루트 (\"\") 포함, ID 순 */\n readonly namespaces: ReadonlyMap<string, NamespaceInfo>\n}\n\n/** 명령어 목록에서 Registry 생성. ID · 별칭 · 스키마 규칙 위반은 MextBuildError */\nexport function createRegistry(input: RegistryInput): Registry {\n const { registry, issues } = collectRegistry(input)\n if (issues.length > 0) throw new MextBuildError(issues)\n return registry\n}\n\n/**\n * createRegistry 의 수집형 (mext build 의 일괄 검증용). 던지지 않는다.\n * registry = 규칙을 통과한 명령어만, issues = 위반 전부\n */\nexport function collectRegistry(input: RegistryInput): { registry: Registry; issues: BuildIssue[] } {\n const issues: BuildIssue[] = []\n const accepted: RegistryCommandInput[] = []\n const seen = new Map<string, RegistryCommandInput>()\n\n for (const entry of input.commands) {\n const where = { id: entry.id, file: entry.file }\n if (!isCommand(entry.command)) {\n issues.push({ code: \"INVALID_COMMAND\", message: \"not a defineCommand result\", ...where })\n continue\n }\n const segments = entry.id.split(\".\")\n const bad = segments.find((s) => !isSegment(s))\n if (bad !== undefined) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `segment \"${bad}\" is not kebab-case`, ...where })\n continue\n }\n if (RESERVED_ROOT_SEGMENTS.includes(segments[0]!)) {\n issues.push({ code: \"RESERVED_ID\", message: `root segment \"${segments[0]}\" is reserved`, ...where })\n continue\n }\n const prev = seen.get(entry.id)\n if (prev) {\n issues.push({ code: \"DUPLICATE_ID\", message: `duplicate ID (also ${prev.file ?? prev.id})`, ...where })\n continue\n }\n seen.set(entry.id, entry)\n accepted.push(entry)\n }\n\n // 네임스페이스 = 명령어 ID 의 모든 상위 경로\n const namespaceIds = new Set<string>([\"\"])\n for (const { id } of accepted) {\n for (let ns = parentNamespace(id); ns !== \"\"; ns = parentNamespace(ns)) namespaceIds.add(ns)\n }\n\n const aliases = new Map<string, string>()\n const aliasesOf = new Map<string, string[]>()\n for (const entry of accepted) {\n const where = { id: entry.id, file: entry.file }\n const parent = parentNamespace(entry.id)\n const own: string[] = []\n for (const alias of entry.command.aliases) {\n if (!isSegment(alias)) {\n issues.push({ code: \"INVALID_ALIAS\", message: `alias \"${alias}\" is not a kebab-case segment`, ...where })\n continue\n }\n if (parent === \"\") {\n issues.push({ code: \"INVALID_ALIAS\", message: `alias \"${alias}\": aliases are not allowed at the root`, ...where })\n continue\n }\n const full = `${parent}.${alias}`\n const owner = seen.has(full) ? full : aliases.get(full)\n if (owner !== undefined || namespaceIds.has(full)) {\n issues.push({\n code: \"DUPLICATE_ID\",\n message: `alias ${full} duplicates ${owner !== undefined ? owner : \"a namespace\"}`,\n ...where,\n })\n continue\n }\n aliases.set(full, entry.id)\n own.push(full)\n }\n aliasesOf.set(entry.id, own.sort())\n }\n\n const commands = new Map<string, RegisteredCommand>()\n for (const entry of [...accepted].sort((a, b) => compareIds(a.id, b.id))) {\n const where = { id: entry.id, file: entry.file }\n let inputSchema: JsonSchema\n let outputSchema: JsonSchema | undefined\n try {\n inputSchema = toJsonSchema(entry.command.input, \"input\")\n outputSchema = entry.command.output ? toJsonSchema(entry.command.output, \"output\") : undefined\n } catch (error) {\n issues.push({ code: \"SCHEMA_UNREPRESENTABLE\", message: errorMessage(error), ...where })\n continue\n }\n commands.set(entry.id, {\n id: entry.id,\n command: entry.command,\n aliases: aliasesOf.get(entry.id) ?? [],\n file: entry.file,\n inputSchema,\n outputSchema,\n })\n }\n\n const namespaces = namespaceTable(namespaceIds, [...commands.keys()], input.namespaces)\n return { registry: { commands, aliases, namespaces }, issues }\n}\n\n/** lazy Registry 의 명령어: manifest 메타데이터 + 모듈 로더 (규칙 검증은 mext build 가 이미 함) */\nexport interface LazyCommandInput {\n id: string\n /** 메타데이터만 있는 명령어 (input · execute 는 load 결과). RegisteredCommand.command */\n command: Command\n /** 별칭의 전체 ID */\n aliases: readonly string[]\n file?: string\n inputSchema: JsonSchema\n outputSchema?: JsonSchema\n /** 실행 시 1회 호출 (결과 캐시, 실패는 다음 호출에서 재시도) */\n load: () => Promise<Command>\n}\n\n/**\n * manifest (mext build 산출) 로 Registry 생성. 명령어 모듈을 import 하지 않는다 (mext start · handler.js).\n * help · tools/list · UNKNOWN_COMMAND · 권한 검사 = 메타데이터만, 입력 검증 · execute = loadCommand 후\n */\nexport function createLazyRegistry(input: {\n commands: readonly LazyCommandInput[]\n namespaces?: readonly RegistryNamespaceInput[]\n}): Registry {\n const commands = new Map<string, RegisteredCommand>()\n const aliases = new Map<string, string>()\n for (const entry of [...input.commands].sort((a, b) => compareIds(a.id, b.id))) {\n const load = entry.load\n let pending: Promise<Command> | undefined\n commands.set(entry.id, {\n id: entry.id,\n command: entry.command,\n aliases: [...entry.aliases].sort(),\n file: entry.file,\n inputSchema: entry.inputSchema,\n outputSchema: entry.outputSchema,\n load: () =>\n (pending ??= load().catch((error: unknown) => {\n pending = undefined\n throw error\n })),\n })\n for (const alias of entry.aliases) aliases.set(alias, entry.id)\n }\n const namespaceIds = new Set<string>([\"\"])\n for (const id of commands.keys()) {\n for (let ns = parentNamespace(id); ns !== \"\"; ns = parentNamespace(ns)) namespaceIds.add(ns)\n }\n return { commands, aliases, namespaces: namespaceTable(namespaceIds, [...commands.keys()], input.namespaces) }\n}\n\n/** 실행 가능한 명령어. eager = entry.command, lazy = entry.load() (모듈 import) */\nexport function loadCommand(entry: RegisteredCommand): Promise<Command> {\n return entry.load ? entry.load() : Promise.resolve(entry.command)\n}\n\nfunction namespaceTable(\n namespaceIds: ReadonlySet<string>,\n commandIds: readonly string[],\n inputs: readonly RegistryNamespaceInput[] | undefined,\n): Map<string, NamespaceInfo> {\n const descriptions = new Map<string, string | undefined>()\n for (const ns of inputs ?? []) descriptions.set(ns.id, ns.description)\n const namespaces = new Map<string, NamespaceInfo>()\n for (const id of [...namespaceIds].sort(compareIds)) {\n namespaces.set(id, {\n id,\n description: descriptions.get(id),\n commands: commandIds.filter((c) => parentNamespace(c) === id),\n namespaces: [...namespaceIds].filter((n) => n !== \"\" && parentNamespace(n) === id).sort(compareIds),\n })\n }\n return namespaces\n}\n\n/** 정식 ID 또는 별칭으로 명령어 찾기 */\nexport function resolveCommand(registry: Registry, id: string): RegisteredCommand | undefined {\n return registry.commands.get(id) ?? registry.commands.get(registry.aliases.get(id) ?? \"\")\n}\n\nfunction toJsonSchema(schema: z.ZodType, io: \"input\" | \"output\"): JsonSchema {\n return z.toJSONSchema(schema, { target: \"draft-2020-12\", io, unrepresentable: \"throw\" }) as JsonSchema\n}\n\nfunction compareIds(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from \"zod\"\nimport { isCommand, type Command } from \"./command.ts\"\nimport { MextBuildError, type BuildIssue } from \"./errors.ts\"\nimport { isSegment, parentNamespace, RESERVED_ROOT_SEGMENTS } from \"./ids.ts\"\nimport type { PromptSet } from \"./prompt.ts\"\nimport type { ResourceSet } from \"./resource.ts\"\n\nexport type JsonSchema = Record<string, unknown>\n\nexport interface RegistryCommandInput {\n /** 정식 ID. 점 구분 kebab-case 세그먼트 */\n id: string\n command: Command\n /** commands/ 기준 상대 경로 (posix). 스캔 결과에만 있음 */\n file?: string\n}\n\nexport interface RegistryNamespaceInput {\n /** \"\" = 루트 */\n id: string\n description?: string\n}\n\nexport interface RegistryInput {\n commands: readonly RegistryCommandInput[]\n namespaces?: readonly RegistryNamespaceInput[]\n}\n\nexport interface RegisteredCommand {\n id: string\n /**\n * 메타데이터 (description · 안전성 · scopes · examples · positional · aliases) 의 원본.\n * eager (createRegistry) = 실행 가능한 명령어 그대로. lazy (createLazyRegistry) = manifest 로 만든 메타데이터만\n * (input · execute 는 load() 결과에서)\n */\n command: Command\n /** lazy Registry: 명령어 모듈 import (1회 캐시) → 실행 가능한 명령어. 없음 = command 가 실행 가능. loadCommand 로 호출 */\n load?: () => Promise<Command>\n /** 별칭의 전체 ID (정렬) */\n aliases: readonly string[]\n file: string | undefined\n /** input 의 JSON Schema 2020-12 (io: input) */\n inputSchema: JsonSchema\n /** output 의 JSON Schema 2020-12 (io: output) */\n outputSchema: JsonSchema | undefined\n}\n\nexport interface NamespaceInfo {\n /** \"\" = 루트 */\n id: string\n /** _index.md 내용 */\n description: string | undefined\n /** 직속 명령어 정식 ID (정렬). index.ts 명령어 (네임스페이스와 같은 ID) 는 상위 네임스페이스에 속한다 */\n commands: readonly string[]\n /** 직속 하위 네임스페이스 ID (정렬) */\n namespaces: readonly string[]\n}\n\nexport interface Registry {\n /** 정식 ID → 명령어. ID 순 */\n readonly commands: ReadonlyMap<string, RegisteredCommand>\n /** 별칭 전체 ID → 정식 ID */\n readonly aliases: ReadonlyMap<string, string>\n /** 네임스페이스 ID → 정보. 루트 (\"\") 포함, ID 순 */\n readonly namespaces: ReadonlyMap<string, NamespaceInfo>\n /** MCP 자원 (resources/, §12.3). 없음 = 기능 없음 (capability 미선언). 명령어 Map 과 별개 */\n readonly resources?: ResourceSet\n /** MCP 프롬프트 (prompts/, §12.3). 없음 = 기능 없음 (capability 미선언). 이름 순 */\n readonly prompts?: PromptSet\n}\n\n/** 명령어 목록에서 Registry 생성. ID · 별칭 · 스키마 규칙 위반은 MextBuildError */\nexport function createRegistry(input: RegistryInput): Registry {\n const { registry, issues } = collectRegistry(input)\n if (issues.length > 0) throw new MextBuildError(issues)\n return registry\n}\n\n/**\n * createRegistry 의 수집형 (mext build 의 일괄 검증용). 던지지 않는다.\n * registry = 규칙을 통과한 명령어만, issues = 위반 전부\n */\nexport function collectRegistry(input: RegistryInput): { registry: Registry; issues: BuildIssue[] } {\n const issues: BuildIssue[] = []\n const accepted: RegistryCommandInput[] = []\n const seen = new Map<string, RegistryCommandInput>()\n\n for (const entry of input.commands) {\n const where = { id: entry.id, file: entry.file }\n if (!isCommand(entry.command)) {\n issues.push({ code: \"INVALID_COMMAND\", message: \"not a defineCommand result\", ...where })\n continue\n }\n const segments = entry.id.split(\".\")\n const bad = segments.find((s) => !isSegment(s))\n if (bad !== undefined) {\n issues.push({ code: \"INVALID_SEGMENT\", message: `segment \"${bad}\" is not kebab-case`, ...where })\n continue\n }\n if (RESERVED_ROOT_SEGMENTS.includes(segments[0]!)) {\n issues.push({ code: \"RESERVED_ID\", message: `root segment \"${segments[0]}\" is reserved`, ...where })\n continue\n }\n const prev = seen.get(entry.id)\n if (prev) {\n issues.push({ code: \"DUPLICATE_ID\", message: `duplicate ID (also ${prev.file ?? prev.id})`, ...where })\n continue\n }\n seen.set(entry.id, entry)\n accepted.push(entry)\n }\n\n // 네임스페이스 = 명령어 ID 의 모든 상위 경로\n const namespaceIds = new Set<string>([\"\"])\n for (const { id } of accepted) {\n for (let ns = parentNamespace(id); ns !== \"\"; ns = parentNamespace(ns)) namespaceIds.add(ns)\n }\n\n const aliases = new Map<string, string>()\n const aliasesOf = new Map<string, string[]>()\n for (const entry of accepted) {\n const where = { id: entry.id, file: entry.file }\n const parent = parentNamespace(entry.id)\n const own: string[] = []\n for (const alias of entry.command.aliases) {\n if (!isSegment(alias)) {\n issues.push({ code: \"INVALID_ALIAS\", message: `alias \"${alias}\" is not a kebab-case segment`, ...where })\n continue\n }\n if (parent === \"\") {\n issues.push({ code: \"INVALID_ALIAS\", message: `alias \"${alias}\": aliases are not allowed at the root`, ...where })\n continue\n }\n const full = `${parent}.${alias}`\n const owner = seen.has(full) ? full : aliases.get(full)\n if (owner !== undefined || namespaceIds.has(full)) {\n issues.push({\n code: \"DUPLICATE_ID\",\n message: `alias ${full} duplicates ${owner !== undefined ? owner : \"a namespace\"}`,\n ...where,\n })\n continue\n }\n aliases.set(full, entry.id)\n own.push(full)\n }\n aliasesOf.set(entry.id, own.sort())\n }\n\n const commands = new Map<string, RegisteredCommand>()\n for (const entry of [...accepted].sort((a, b) => compareIds(a.id, b.id))) {\n const where = { id: entry.id, file: entry.file }\n let inputSchema: JsonSchema\n let outputSchema: JsonSchema | undefined\n try {\n inputSchema = toJsonSchema(entry.command.input, \"input\")\n outputSchema = entry.command.output ? toJsonSchema(entry.command.output, \"output\") : undefined\n } catch (error) {\n issues.push({ code: \"SCHEMA_UNREPRESENTABLE\", message: errorMessage(error), ...where })\n continue\n }\n commands.set(entry.id, {\n id: entry.id,\n command: entry.command,\n aliases: aliasesOf.get(entry.id) ?? [],\n file: entry.file,\n inputSchema,\n outputSchema,\n })\n }\n\n const namespaces = namespaceTable(namespaceIds, [...commands.keys()], input.namespaces)\n return { registry: { commands, aliases, namespaces }, issues }\n}\n\n/** lazy Registry 의 명령어: manifest 메타데이터 + 모듈 로더 (규칙 검증은 mext build 가 이미 함) */\nexport interface LazyCommandInput {\n id: string\n /** 메타데이터만 있는 명령어 (input · execute 는 load 결과). RegisteredCommand.command */\n command: Command\n /** 별칭의 전체 ID */\n aliases: readonly string[]\n file?: string\n inputSchema: JsonSchema\n outputSchema?: JsonSchema\n /** 실행 시 1회 호출 (결과 캐시, 실패는 다음 호출에서 재시도) */\n load: () => Promise<Command>\n}\n\n/**\n * manifest (mext build 산출) 로 Registry 생성. 명령어 모듈을 import 하지 않는다 (mext start · handler.js).\n * help · tools/list · UNKNOWN_COMMAND · 권한 검사 = 메타데이터만, 입력 검증 · execute = loadCommand 후\n */\nexport function createLazyRegistry(input: {\n commands: readonly LazyCommandInput[]\n namespaces?: readonly RegistryNamespaceInput[]\n}): Registry {\n const commands = new Map<string, RegisteredCommand>()\n const aliases = new Map<string, string>()\n for (const entry of [...input.commands].sort((a, b) => compareIds(a.id, b.id))) {\n const load = entry.load\n let pending: Promise<Command> | undefined\n commands.set(entry.id, {\n id: entry.id,\n command: entry.command,\n aliases: [...entry.aliases].sort(),\n file: entry.file,\n inputSchema: entry.inputSchema,\n outputSchema: entry.outputSchema,\n load: () =>\n (pending ??= load().catch((error: unknown) => {\n pending = undefined\n throw error\n })),\n })\n for (const alias of entry.aliases) aliases.set(alias, entry.id)\n }\n const namespaceIds = new Set<string>([\"\"])\n for (const id of commands.keys()) {\n for (let ns = parentNamespace(id); ns !== \"\"; ns = parentNamespace(ns)) namespaceIds.add(ns)\n }\n return { commands, aliases, namespaces: namespaceTable(namespaceIds, [...commands.keys()], input.namespaces) }\n}\n\n/** 실행 가능한 명령어. eager = entry.command, lazy = entry.load() (모듈 import) */\nexport function loadCommand(entry: RegisteredCommand): Promise<Command> {\n return entry.load ? entry.load() : Promise.resolve(entry.command)\n}\n\nfunction namespaceTable(\n namespaceIds: ReadonlySet<string>,\n commandIds: readonly string[],\n inputs: readonly RegistryNamespaceInput[] | undefined,\n): Map<string, NamespaceInfo> {\n const descriptions = new Map<string, string | undefined>()\n for (const ns of inputs ?? []) descriptions.set(ns.id, ns.description)\n const namespaces = new Map<string, NamespaceInfo>()\n for (const id of [...namespaceIds].sort(compareIds)) {\n namespaces.set(id, {\n id,\n description: descriptions.get(id),\n commands: commandIds.filter((c) => parentNamespace(c) === id),\n namespaces: [...namespaceIds].filter((n) => n !== \"\" && parentNamespace(n) === id).sort(compareIds),\n })\n }\n return namespaces\n}\n\n/** 정식 ID 또는 별칭으로 명령어 찾기 */\nexport function resolveCommand(registry: Registry, id: string): RegisteredCommand | undefined {\n return registry.commands.get(id) ?? registry.commands.get(registry.aliases.get(id) ?? \"\")\n}\n\nfunction toJsonSchema(schema: z.ZodType, io: \"input\" | \"output\"): JsonSchema {\n return z.toJSONSchema(schema, { target: \"draft-2020-12\", io, unrepresentable: \"throw\" }) as JsonSchema\n}\n\nfunction compareIds(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n"]}
@@ -3,6 +3,8 @@
3
3
  * 요청마다 POST 1회: _meta envelope (버전 · clientInfo · clientCapabilities) + MCP-Protocol-Version · Mcp-Method ·
4
4
  * Mcp-Name · Mcp-Param-* 헤더. 응답 = application/json 또는 text/event-stream (progress 알림 → onNotification).
5
5
  * MRTR (InputRequiredResult) 재시도 루프는 callTool 의 onInputRequired 가 담당.
6
+ * tasks 확장 (§12.6) 선언: tools/call 이 CreateTaskResult (resultType "task") 를 그대로 돌려주고, 폴링 · 답 · 취소 =
7
+ * getTask · updateTask · cancelTask (Mcp-Name = taskId). 폴링 루프는 client/task.ts
6
8
  */
7
9
  export declare const PROTOCOL_VERSION = "2026-07-28";
8
10
  export declare const META_KEYS: {
@@ -10,10 +12,16 @@ export declare const META_KEYS: {
10
12
  readonly clientInfo: "io.modelcontextprotocol/clientInfo";
11
13
  readonly clientCapabilities: "io.modelcontextprotocol/clientCapabilities";
12
14
  };
13
- /** 클라이언트가 처리하는 입력 요청 = elicitation form 만 (sampling · roots 없음) */
15
+ /** tasks 확장 ID (mcp/tasks.ts TASKS_EXTENSION 과 같은 값. 이 모듈은 서버 SDK 를 import 하지 않는다) */
16
+ export declare const TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
17
+ /** 클라이언트가 처리하는 입력 요청 = elicitation form · url (sampling · roots 없음) + tasks 확장 (폴링 · 답 · 취소) */
14
18
  export declare const CLIENT_CAPABILITIES: {
15
19
  readonly elicitation: {
16
20
  readonly form: {};
21
+ readonly url: {};
22
+ };
23
+ readonly extensions: {
24
+ readonly "io.modelcontextprotocol/tasks": {};
17
25
  };
18
26
  };
19
27
  /** MRTR 재시도 상한 (같은 요청의 input_required 연속 횟수) */
@@ -22,7 +30,9 @@ export interface RemoteTool {
22
30
  name: string;
23
31
  description?: string;
24
32
  inputSchema?: Record<string, unknown>;
33
+ outputSchema?: Record<string, unknown>;
25
34
  annotations?: Record<string, unknown>;
35
+ _meta?: Record<string, unknown>;
26
36
  }
27
37
  export interface CallToolResult {
28
38
  content?: unknown[];
@@ -99,6 +109,12 @@ export interface RemoteClient {
99
109
  listTools(options?: RequestOptions): Promise<RemoteTool[]>;
100
110
  listToolsResult(options?: RequestOptions): Promise<ToolsListResult>;
101
111
  callTool(tool: RemoteTool, args: Record<string, unknown>, options?: CallToolOptions): Promise<CallToolResult>;
112
+ /** tasks/get (Mcp-Name = taskId) */
113
+ getTask(taskId: string, options?: RequestOptions): Promise<Record<string, unknown>>;
114
+ /** tasks/update { taskId, inputResponses } */
115
+ updateTask(taskId: string, inputResponses: InputResponses, options?: RequestOptions): Promise<void>;
116
+ /** tasks/cancel { taskId } */
117
+ cancelTask(taskId: string, options?: RequestOptions): Promise<void>;
102
118
  }
103
119
  export declare function createRemoteClient(options: RemoteClientOptions): RemoteClient;
104
120
  export interface BearerChallenge {
@@ -4,6 +4,8 @@ import { VERSION } from "../version.js";
4
4
  * 요청마다 POST 1회: _meta envelope (버전 · clientInfo · clientCapabilities) + MCP-Protocol-Version · Mcp-Method ·
5
5
  * Mcp-Name · Mcp-Param-* 헤더. 응답 = application/json 또는 text/event-stream (progress 알림 → onNotification).
6
6
  * MRTR (InputRequiredResult) 재시도 루프는 callTool 의 onInputRequired 가 담당.
7
+ * tasks 확장 (§12.6) 선언: tools/call 이 CreateTaskResult (resultType "task") 를 그대로 돌려주고, 폴링 · 답 · 취소 =
8
+ * getTask · updateTask · cancelTask (Mcp-Name = taskId). 폴링 루프는 client/task.ts
7
9
  */
8
10
  export const PROTOCOL_VERSION = "2026-07-28";
9
11
  export const META_KEYS = {
@@ -11,8 +13,13 @@ export const META_KEYS = {
11
13
  clientInfo: "io.modelcontextprotocol/clientInfo",
12
14
  clientCapabilities: "io.modelcontextprotocol/clientCapabilities",
13
15
  };
14
- /** 클라이언트가 처리하는 입력 요청 = elicitation form 만 (sampling · roots 없음) */
15
- export const CLIENT_CAPABILITIES = { elicitation: { form: {} } };
16
+ /** tasks 확장 ID (mcp/tasks.ts TASKS_EXTENSION 과 같은 값. 이 모듈은 서버 SDK 를 import 하지 않는다) */
17
+ export const TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
18
+ /** 클라이언트가 처리하는 입력 요청 = elicitation form · url (sampling · roots 없음) + tasks 확장 (폴링 · 답 · 취소) */
19
+ export const CLIENT_CAPABILITIES = {
20
+ elicitation: { form: {}, url: {} },
21
+ extensions: { [TASKS_EXTENSION_ID]: {} },
22
+ };
16
23
  /** MRTR 재시도 상한 (같은 요청의 input_required 연속 횟수) */
17
24
  export const MAX_INPUT_ROUNDS = 8;
18
25
  /** HTTP 401. challenge = WWW-Authenticate 파싱 결과, data = JSON-RPC error.data (mext 는 UNAUTHENTICATED toJSON) */
@@ -170,12 +177,21 @@ export function createRemoteClient(options) {
170
177
  retry.requestState = required.requestState;
171
178
  }
172
179
  };
180
+ /** tasks/* = Mcp-Name 헤더 = taskId (SDK 검증, 없으면 400) */
181
+ const taskRequest = (method, taskId, params, opts = {}) => request(method, { taskId, ...params }, opts, { "Mcp-Name": taskId });
173
182
  return {
174
183
  url: options.url,
175
184
  request: (method, params, opts) => request(method, params, opts),
176
185
  listTools: async (opts) => (await listToolsResult(opts)).tools,
177
186
  listToolsResult,
178
187
  callTool,
188
+ getTask: (taskId, opts) => taskRequest("tasks/get", taskId, {}, opts),
189
+ updateTask: async (taskId, inputResponses, opts) => {
190
+ await taskRequest("tasks/update", taskId, { inputResponses }, opts);
191
+ },
192
+ cancelTask: async (taskId, opts) => {
193
+ await taskRequest("tasks/cancel", taskId, {}, opts);
194
+ },
179
195
  };
180
196
  }
181
197
  /** inputSchema 의 x-mcp-header 속성 → Mcp-Param-<Name> 헤더 (SEP-2243). 문자열 · 숫자 · boolean 값만 */