@warlock.js/ai-tools 5.31.0 → 5.33.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.
- package/CHANGELOG.md +26 -0
- package/cjs/index.cjs +327 -60
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/http.type.d.mts +4 -5
- package/esm/contracts/http.type.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -1
- package/esm/contracts/mcp-serve.type.d.mts +97 -37
- package/esm/contracts/mcp-serve.type.d.mts.map +1 -1
- package/esm/contracts/mcp.type.d.mts +29 -19
- package/esm/contracts/mcp.type.d.mts.map +1 -1
- package/esm/contracts/utility.type.d.mts +7 -8
- package/esm/contracts/utility.type.d.mts.map +1 -1
- package/esm/contracts/web.type.d.mts +9 -10
- package/esm/contracts/web.type.d.mts.map +1 -1
- package/esm/errors.d.mts +31 -26
- package/esm/errors.d.mts.map +1 -1
- package/esm/http/http-request.d.mts +2 -3
- package/esm/http/http-request.d.mts.map +1 -1
- package/esm/http/http-request.mjs +7 -5
- package/esm/http/http-request.mjs.map +1 -1
- package/esm/index.d.mts +6 -2
- package/esm/index.mjs +2 -1
- package/esm/mcp/client.mjs +2 -1
- package/esm/mcp/client.mjs.map +1 -1
- package/esm/mcp/index.d.mts +5 -4
- package/esm/mcp/index.d.mts.map +1 -1
- package/esm/mcp/index.mjs +2 -1
- package/esm/mcp/index.mjs.map +1 -1
- package/esm/mcp/json-schema-to-standard.d.mts +1 -3
- package/esm/mcp/json-schema-to-standard.d.mts.map +1 -1
- package/esm/mcp/protocol.d.mts +1 -2
- package/esm/mcp/protocol.d.mts.map +1 -1
- package/esm/mcp/result.d.mts +17 -0
- package/esm/mcp/result.d.mts.map +1 -0
- package/esm/mcp/result.mjs +85 -0
- package/esm/mcp/result.mjs.map +1 -0
- package/esm/mcp/serve-http.mjs +64 -25
- package/esm/mcp/serve-http.mjs.map +1 -1
- package/esm/mcp/serve-requests.mjs +62 -0
- package/esm/mcp/serve-requests.mjs.map +1 -0
- package/esm/mcp/serve.d.mts +5 -5
- package/esm/mcp/serve.d.mts.map +1 -1
- package/esm/mcp/serve.mjs +97 -18
- package/esm/mcp/serve.mjs.map +1 -1
- package/esm/mcp/transport.d.mts +4 -5
- package/esm/mcp/transport.d.mts.map +1 -1
- package/esm/mcp/transport.mjs +12 -6
- package/esm/mcp/transport.mjs.map +1 -1
- package/esm/mcp/transport.type.d.mts +2 -2
- package/esm/mcp/transport.type.d.mts.map +1 -1
- package/esm/register.d.mts +3 -4
- package/esm/register.d.mts.map +1 -1
- package/esm/utility/calculator.d.mts +1 -3
- package/esm/utility/calculator.d.mts.map +1 -1
- package/esm/utility/date-time.d.mts +3 -5
- package/esm/utility/date-time.d.mts.map +1 -1
- package/esm/utility/date-time.mjs +2 -1
- package/esm/utility/date-time.mjs.map +1 -1
- package/esm/utility/index.d.mts +2 -1
- package/esm/utility/index.mjs +1 -1
- package/esm/web/fetch-url.d.mts +2 -3
- package/esm/web/fetch-url.d.mts.map +1 -1
- package/esm/web/fetch-url.mjs +2 -1
- package/esm/web/fetch-url.mjs.map +1 -1
- package/esm/web/index.d.mts +2 -1
- package/esm/web/index.mjs +1 -1
- package/esm/web/web-search.d.mts +2 -3
- package/esm/web/web-search.d.mts.map +1 -1
- package/esm/web/web-search.mjs +3 -4
- package/esm/web/web-search.mjs.map +1 -1
- package/llms-full.txt +122 -3
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/skills/connect-mcp-server/SKILL.md +15 -0
- package/skills/expose-as-mcp-server/SKILL.md +107 -3
package/esm/mcp/serve.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { McpServeError, McpTransportError } from "../errors.mjs";
|
|
2
2
|
import { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from "./protocol.mjs";
|
|
3
|
+
import { assertMcpResult, isMcpResult, isRecord, looksLikeMcpResult, unwrapMcpResult } from "./result.mjs";
|
|
4
|
+
import { McpServeRequests } from "./serve-requests.mjs";
|
|
3
5
|
import { startHttpServer } from "./serve-http.mjs";
|
|
4
6
|
import { extractJsonSchema } from "@warlock.js/ai";
|
|
5
7
|
import { createInterface } from "node:readline";
|
|
@@ -121,7 +123,10 @@ var McpServeHandler = class {
|
|
|
121
123
|
*/
|
|
122
124
|
async handle(request, context = {}) {
|
|
123
125
|
try {
|
|
124
|
-
return this.ok(request.id, await this.dispatch(request,
|
|
126
|
+
return this.ok(request.id, await this.dispatch(request, {
|
|
127
|
+
...context,
|
|
128
|
+
requestId: request.id
|
|
129
|
+
}));
|
|
125
130
|
} catch (cause) {
|
|
126
131
|
if (cause instanceof RpcFailure) return this.error(request.id, cause.code, cause.message, cause.data);
|
|
127
132
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
@@ -134,19 +139,19 @@ var McpServeHandler = class {
|
|
|
134
139
|
case "initialize": return this.initializeResult(request.params);
|
|
135
140
|
case "ping": return {};
|
|
136
141
|
case "tools/list": return { tools: this.listTools() };
|
|
137
|
-
case "tools/call": return this.callTool(request.params,
|
|
138
|
-
|
|
142
|
+
case "tools/call": return this.callTool(request.params, {
|
|
143
|
+
...context,
|
|
144
|
+
requestId: request.id
|
|
145
|
+
});
|
|
139
146
|
}
|
|
140
147
|
if (this.resources) switch (request.method) {
|
|
141
148
|
case "resources/list": return { resources: this.listResources(this.resources) };
|
|
142
149
|
case "resources/templates/list": return { resourceTemplates: [] };
|
|
143
150
|
case "resources/read": return this.readResource(this.resources, request.params, context);
|
|
144
|
-
default: break;
|
|
145
151
|
}
|
|
146
152
|
if (this.prompts) switch (request.method) {
|
|
147
153
|
case "prompts/list": return { prompts: this.listPrompts(this.prompts) };
|
|
148
154
|
case "prompts/get": return this.getPrompt(this.prompts, request.params, context);
|
|
149
|
-
default: break;
|
|
150
155
|
}
|
|
151
156
|
throw new RpcFailure(JSON_RPC_METHOD_NOT_FOUND, `Method "${request.method}" is not supported by this MCP server.`);
|
|
152
157
|
}
|
|
@@ -186,15 +191,17 @@ var McpServeHandler = class {
|
|
|
186
191
|
return {
|
|
187
192
|
name,
|
|
188
193
|
description: contract.description,
|
|
189
|
-
inputSchema
|
|
194
|
+
inputSchema,
|
|
195
|
+
...isRecord(contract.meta?.outputSchema) ? { outputSchema: contract.meta.outputSchema } : {}
|
|
190
196
|
};
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
199
|
/**
|
|
194
200
|
* Route a `tools/call` to the named contract's `invoke()` and map the
|
|
195
201
|
* never-throwing {@link import("@warlock.js/ai").ToolInvokeResult}: `data`
|
|
196
|
-
* → a text content block, `error` → an
|
|
197
|
-
* tool name throws (mapped to a
|
|
202
|
+
* → a text content block (or an opted-in native result), `error` → an
|
|
203
|
+
* `isError: true` result. An unknown tool name throws (mapped to a
|
|
204
|
+
* JSON-RPC error by {@link handle}).
|
|
198
205
|
*
|
|
199
206
|
* The contract receives a `ToolContext` carrying the abort signal and,
|
|
200
207
|
* over HTTP, the inbound request's headers on `ctx.request`.
|
|
@@ -206,7 +213,11 @@ var McpServeHandler = class {
|
|
|
206
213
|
type: "protocol",
|
|
207
214
|
method: "tools/call"
|
|
208
215
|
});
|
|
209
|
-
const toolContext = {
|
|
216
|
+
const toolContext = {
|
|
217
|
+
artifacts: {},
|
|
218
|
+
requestId: context.requestId,
|
|
219
|
+
principal: context.principal
|
|
220
|
+
};
|
|
210
221
|
if (context.signal) toolContext.signal = context.signal;
|
|
211
222
|
if (context.request) {
|
|
212
223
|
const { headers, remoteAddress } = context.request;
|
|
@@ -215,6 +226,43 @@ var McpServeHandler = class {
|
|
|
215
226
|
remoteAddress
|
|
216
227
|
};
|
|
217
228
|
}
|
|
229
|
+
if (this.options.beforeCall) {
|
|
230
|
+
let input = args;
|
|
231
|
+
if (contract.input) {
|
|
232
|
+
let validation;
|
|
233
|
+
try {
|
|
234
|
+
validation = await contract.input["~standard"].validate(args);
|
|
235
|
+
} catch (cause) {
|
|
236
|
+
return {
|
|
237
|
+
content: [{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: `Schema validation threw for tool "${name}": ${cause instanceof Error ? cause.message : String(cause)}`
|
|
240
|
+
}],
|
|
241
|
+
isError: true
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (validation.issues) return {
|
|
245
|
+
content: [{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: `Validation failed: ${validation.issues.map((issue) => issue.message).join("; ")}`
|
|
248
|
+
}],
|
|
249
|
+
isError: true
|
|
250
|
+
};
|
|
251
|
+
input = validation.value;
|
|
252
|
+
}
|
|
253
|
+
context.signal?.throwIfAborted();
|
|
254
|
+
const refusal = await this.options.beforeCall(context, contract, input);
|
|
255
|
+
if (refusal !== void 0) {
|
|
256
|
+
try {
|
|
257
|
+
assertMcpResult(refusal);
|
|
258
|
+
if (refusal.isError !== true) throw new Error("A refusal must be an error result.");
|
|
259
|
+
} catch {
|
|
260
|
+
throw new RpcFailure(JSON_RPC_INTERNAL_ERROR, "Invalid MCP beforeCall refusal.");
|
|
261
|
+
}
|
|
262
|
+
return refusal;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
context.signal?.throwIfAborted();
|
|
218
266
|
const result = await contract.invoke(args, toolContext);
|
|
219
267
|
if (result.error) return {
|
|
220
268
|
content: [{
|
|
@@ -223,6 +271,16 @@ var McpServeHandler = class {
|
|
|
223
271
|
}],
|
|
224
272
|
isError: true
|
|
225
273
|
};
|
|
274
|
+
const mode = contract.meta?.mcpResults ?? this.options.results ?? "text";
|
|
275
|
+
if (isMcpResult(result.data) || mode === "native" && looksLikeMcpResult(result.data)) {
|
|
276
|
+
const native = isMcpResult(result.data) ? unwrapMcpResult(result.data) : result.data;
|
|
277
|
+
try {
|
|
278
|
+
assertMcpResult(native);
|
|
279
|
+
} catch {
|
|
280
|
+
throw new RpcFailure(JSON_RPC_INTERNAL_ERROR, "Invalid native MCP CallToolResult.");
|
|
281
|
+
}
|
|
282
|
+
return native;
|
|
283
|
+
}
|
|
226
284
|
return {
|
|
227
285
|
content: [{
|
|
228
286
|
type: "text",
|
|
@@ -335,7 +393,7 @@ const JSON_RPC_INTERNAL_ERROR = -32603;
|
|
|
335
393
|
* answered with a JSON-RPC "Invalid Request" error whose `id` is `null`.
|
|
336
394
|
* Never rejects: a dispatch failure is answered as an internal error.
|
|
337
395
|
*/
|
|
338
|
-
async function answerStdioLine(line, dispatch) {
|
|
396
|
+
async function answerStdioLine(line, dispatch, notify) {
|
|
339
397
|
const trimmed = line.trim();
|
|
340
398
|
if (!trimmed) return;
|
|
341
399
|
let message;
|
|
@@ -354,10 +412,21 @@ async function answerStdioLine(line, dispatch) {
|
|
|
354
412
|
})}\n`;
|
|
355
413
|
const request = message;
|
|
356
414
|
const id = request.id;
|
|
357
|
-
if (id === void 0 || id === null)
|
|
415
|
+
if (id === void 0 || id === null) {
|
|
416
|
+
notify?.(message);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (typeof id !== "string" && typeof id !== "number") return `${JSON.stringify({
|
|
420
|
+
jsonrpc: JSONRPC_VERSION,
|
|
421
|
+
id: null,
|
|
422
|
+
error: {
|
|
423
|
+
code: JSON_RPC_INVALID_REQUEST,
|
|
424
|
+
message: "Invalid request: id must be a string or number."
|
|
425
|
+
}
|
|
426
|
+
})}\n`;
|
|
358
427
|
try {
|
|
359
428
|
const response = await dispatch(request);
|
|
360
|
-
return `${JSON.stringify(response)}\n`;
|
|
429
|
+
return response === void 0 ? void 0 : `${JSON.stringify(response)}\n`;
|
|
361
430
|
} catch {
|
|
362
431
|
return `${JSON.stringify({
|
|
363
432
|
jsonrpc: JSONRPC_VERSION,
|
|
@@ -441,7 +510,7 @@ var McpServerImpl = class {
|
|
|
441
510
|
start() {
|
|
442
511
|
if (!this.running) {
|
|
443
512
|
const transport = this.options.transport ?? { type: "stdio" };
|
|
444
|
-
const starting = transport.type === "http" ? this.startHttp(transport) : Promise.resolve(this.startStdio());
|
|
513
|
+
const starting = transport.type === "http" ? this.startHttp(transport) : Promise.resolve(this.startStdio(transport));
|
|
445
514
|
this.running = starting.catch((error) => {
|
|
446
515
|
this.running = void 0;
|
|
447
516
|
throw error;
|
|
@@ -461,19 +530,29 @@ var McpServerImpl = class {
|
|
|
461
530
|
};
|
|
462
531
|
}
|
|
463
532
|
/** Wire the stdin line reader and answer each request on stdout. */
|
|
464
|
-
startStdio() {
|
|
533
|
+
startStdio(transport) {
|
|
534
|
+
const shutdown = new AbortController();
|
|
535
|
+
const requests = new McpServeRequests();
|
|
465
536
|
const reader = createInterface({ input: process.stdin });
|
|
537
|
+
reader.once("close", () => shutdown.abort());
|
|
466
538
|
reader.on("line", (line) => {
|
|
467
|
-
this.onLine(line).catch(() => {});
|
|
539
|
+
this.onLine(line, transport, requests, shutdown.signal).catch(() => {});
|
|
468
540
|
});
|
|
469
541
|
return { close: async () => {
|
|
542
|
+
shutdown.abort();
|
|
470
543
|
reader.close();
|
|
471
544
|
this.running = void 0;
|
|
472
545
|
} };
|
|
473
546
|
}
|
|
474
547
|
/** Answer one stdin line on stdout (see {@link answerStdioLine}). */
|
|
475
|
-
async onLine(line) {
|
|
476
|
-
const reply = await answerStdioLine(line, (request) =>
|
|
548
|
+
async onLine(line, transport, requests, signal) {
|
|
549
|
+
const reply = await answerStdioLine(line, (request) => requests.run(request, { signal }, async (context) => {
|
|
550
|
+
const principal = transport.principalProvider ? await transport.principalProvider(context) : transport.principal;
|
|
551
|
+
return this.handler.handle(request, {
|
|
552
|
+
...context,
|
|
553
|
+
principal
|
|
554
|
+
});
|
|
555
|
+
}), (message) => requests.cancel(message));
|
|
477
556
|
if (reply !== void 0) process.stdout.write(reply);
|
|
478
557
|
}
|
|
479
558
|
async stop() {
|
|
@@ -493,5 +572,5 @@ function serve(source, options) {
|
|
|
493
572
|
}
|
|
494
573
|
|
|
495
574
|
//#endregion
|
|
496
|
-
export { createServeHandler, serve };
|
|
575
|
+
export { answerStdioLine, createServeHandler, serve };
|
|
497
576
|
//# sourceMappingURL=serve.mjs.map
|
package/esm/mcp/serve.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve.ts"],"sourcesContent":["import { createInterface, type Interface } from \"node:readline\";\nimport { extractJsonSchema, type ToolContext, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpGroupedToolSource,\n McpHttpServeHandle,\n McpHttpServeTransport,\n McpPromptMessage,\n McpServeCallContext,\n McpServeHandle,\n McpServeOptions,\n McpServePrompt,\n McpServeResource,\n McpServeSource,\n McpServer,\n McpToolCallResult,\n McpToolDescriptor,\n McpToolSource,\n} from \"../contracts\";\nimport { McpServeError, McpTransportError } from \"../errors\";\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nimport { startHttpServer } from \"./serve-http\";\n\n/** JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/** Default JSON-Schema dialect emitted for each tool's `inputSchema`. */\nconst DEFAULT_SCHEMA_TARGET = \"draft-2020-12\";\n\n/** Default advertised server version when the caller omits one. */\nconst DEFAULT_VERSION = \"4.4.0\";\n\n/** JSON-RPC standard error codes we emit. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\nconst JSON_RPC_INVALID_PARAMS = -32602;\n\n/** MCP's \"resource not found\" error code (server/resources, error handling). */\nconst MCP_RESOURCE_NOT_FOUND = -32002;\n\n/** One tool as exposed on the wire: its public name plus the contract behind it. */\ntype ExposedTool = {\n /** The name clients see in `tools/list` and send in `tools/call`. */\n name: string;\n /** The contract that runs. */\n contract: ToolContract;\n};\n\n/**\n * A JSON-RPC failure raised inside a method handler, carrying the exact\n * code to answer with (unknown resource, missing prompt argument, …).\n */\nclass RpcFailure extends Error {\n public readonly code: number;\n\n public readonly data: unknown;\n\n public constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.code = code;\n this.data = data;\n }\n}\n\n/** Enumerate one tool set into a flat contract array. */\nfunction toolsOf(source: McpToolSource): ToolContract[] {\n return Array.isArray(source) ? source : source.tools();\n}\n\n/** Narrow a serve source to its grouped shape. */\nfunction isGroupedSource(source: McpServeSource): source is McpGroupedToolSource {\n return !Array.isArray(source) && \"groups\" in source;\n}\n\n/**\n * Resolve the serve source into the exposed tool list, applying\n * `prefixToolNames` and refusing duplicate names. Throws\n * {@link McpServeError} — `\"config\"` for `prefixToolNames` on an ungrouped\n * source, `\"collision\"` when two tools would share a name.\n */\nfunction resolveTools(source: McpServeSource, options: McpServeOptions): ExposedTool[] {\n const prefix = options.prefixToolNames ?? false;\n const exposed: Array<ExposedTool & { origin: string }> = [];\n\n if (isGroupedSource(source)) {\n for (const [group, groupSource] of Object.entries(source.groups)) {\n for (const contract of toolsOf(groupSource)) {\n let name = contract.name;\n\n if (prefix === true) {\n name = `${group}_${contract.name}`;\n } else if (typeof prefix === \"function\") {\n name = prefix(group, contract.name);\n }\n\n exposed.push({ name, contract, origin: `group \"${group}\"` });\n }\n }\n } else {\n if (prefix !== false) {\n throw new McpServeError(\n \"prefixToolNames needs a grouped source: serve({ groups: { name: tools } }, …).\",\n { type: \"config\" },\n );\n }\n\n for (const contract of toolsOf(source)) {\n exposed.push({ name: contract.name, contract, origin: \"the source\" });\n }\n }\n\n const seen = new Map<string, string>();\n\n for (const tool of exposed) {\n const previous = seen.get(tool.name);\n\n if (previous !== undefined) {\n throw new McpServeError(\n `MCP tool name \"${tool.name}\" is exposed twice (from ${previous} and ${tool.origin}). ` +\n \"Rename one tool, or serve a grouped source with prefixToolNames.\",\n { type: \"collision\", context: { name: tool.name } },\n );\n }\n\n seen.set(tool.name, tool.origin);\n }\n\n return exposed.map(({ name, contract }) => ({ name, contract }));\n}\n\n/** Throw a collision error when two entries share the same key. */\nfunction assertUnique<T>(\n entries: readonly T[] | undefined,\n key: (entry: T) => string,\n label: string,\n): void {\n const seen = new Set<string>();\n\n for (const entry of entries ?? []) {\n const value = key(entry);\n\n if (seen.has(value)) {\n throw new McpServeError(`MCP ${label} \"${value}\" is declared twice.`, {\n type: \"collision\",\n context: { [label]: value },\n });\n }\n\n seen.add(value);\n }\n}\n\n/**\n * The pure protocol core of `serve` — maps one JSON-RPC request to its\n * response, with no I/O. Both the stdio and HTTP serve-transports pump\n * their inbound requests through this, and specs can drive it directly.\n *\n * Handles `initialize`, `ping`, `tools/list`, `tools/call`, and — when\n * configured — `resources/list`, `resources/templates/list`,\n * `resources/read`, `prompts/list` and `prompts/get`. Any other method\n * answers with a JSON-RPC `method not found` error.\n *\n * Constructed via {@link createServeHandler}.\n */\nclass McpServeHandler {\n /** The tools this server exposes (snapshotted at construction). */\n private readonly tools: ExposedTool[];\n\n /** Fast lookup by exposed tool name for `tools/call` dispatch. */\n private readonly byName: Map<string, ToolContract>;\n\n /** Resources by URI, or `undefined` when the capability is off. */\n private readonly resources: Map<string, McpServeResource> | undefined;\n\n /** Prompts by name, or `undefined` when the capability is off. */\n private readonly prompts: Map<string, McpServePrompt> | undefined;\n\n /** Construction-time serve options (name / version / schema target). */\n private readonly options: McpServeOptions;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.tools = resolveTools(source, options);\n this.byName = new Map(this.tools.map((tool) => [tool.name, tool.contract]));\n\n assertUnique(options.resources, (resource) => resource.uri, \"resource\");\n assertUnique(options.prompts, (prompt) => prompt.name, \"prompt\");\n\n this.resources = options.resources\n ? new Map(options.resources.map((resource) => [resource.uri, resource]))\n : undefined;\n this.prompts = options.prompts\n ? new Map(options.prompts.map((prompt) => [prompt.name, prompt]))\n : undefined;\n this.options = options;\n }\n\n /**\n * Dispatch one inbound JSON-RPC request to its handler and produce the\n * response. A handler that throws is mapped to a JSON-RPC error response\n * — the serve loop never crashes on a bad request.\n *\n * @param request - The JSON-RPC request.\n * @param context - Per-call context: the inbound HTTP request (headers\n * reach tools as `ctx.request`) and an abort signal.\n */\n public async handle(\n request: JsonRpcRequest,\n context: McpServeCallContext = {},\n ): Promise<JsonRpcResponse> {\n try {\n return this.ok(request.id, await this.dispatch(request, context));\n } catch (cause) {\n if (cause instanceof RpcFailure) {\n return this.error(request.id, cause.code, cause.message, cause.data);\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n return this.error(request.id, JSON_RPC_INVALID_PARAMS, message);\n }\n }\n\n /** Route a method to its result builder. */\n private async dispatch(request: JsonRpcRequest, context: McpServeCallContext): Promise<unknown> {\n switch (request.method) {\n case \"initialize\":\n return this.initializeResult(request.params);\n case \"ping\":\n return {};\n case \"tools/list\":\n return { tools: this.listTools() };\n case \"tools/call\":\n return this.callTool(request.params, context);\n default:\n break;\n }\n\n if (this.resources) {\n switch (request.method) {\n case \"resources/list\":\n return { resources: this.listResources(this.resources) };\n case \"resources/templates/list\":\n return { resourceTemplates: [] };\n case \"resources/read\":\n return this.readResource(this.resources, request.params, context);\n default:\n break;\n }\n }\n\n if (this.prompts) {\n switch (request.method) {\n case \"prompts/list\":\n return { prompts: this.listPrompts(this.prompts) };\n case \"prompts/get\":\n return this.getPrompt(this.prompts, request.params, context);\n default:\n break;\n }\n }\n\n throw new RpcFailure(\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${request.method}\" is not supported by this MCP server.`,\n );\n }\n\n /**\n * Build the `initialize` result advertising name / version /\n * capabilities, negotiating the protocol version: the client's requested\n * version when supported, otherwise the newest this server speaks.\n */\n private initializeResult(params: unknown): Record<string, unknown> {\n const requested = readRecord(params).protocolVersion;\n const protocolVersion =\n typeof requested === \"string\" && SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : LATEST_PROTOCOL_VERSION;\n\n const capabilities: Record<string, unknown> = { tools: {} };\n\n if (this.resources) {\n capabilities.resources = {};\n }\n\n if (this.prompts) {\n capabilities.prompts = {};\n }\n\n return {\n protocolVersion,\n capabilities,\n serverInfo: {\n name: this.options.name,\n version: this.options.version ?? DEFAULT_VERSION,\n },\n };\n }\n\n /**\n * Build the `tools/list` payload: one {@link McpToolDescriptor} per\n * exposed tool, its `inputSchema` extracted via `extractJsonSchema` at\n * the configured dialect (default `draft-2020-12` — overriding\n * `extractJsonSchema`'s own `openai-strict` default to a neutral MCP draft).\n */\n private listTools(): McpToolDescriptor[] {\n const target = this.options.schemaTarget ?? DEFAULT_SCHEMA_TARGET;\n\n return this.tools.map(({ name, contract }) => {\n const inputSchema = extractJsonSchema(contract.input, { target }) ?? {\n type: \"object\",\n properties: {},\n };\n\n return {\n name,\n description: contract.description,\n inputSchema,\n };\n });\n }\n\n /**\n * Route a `tools/call` to the named contract's `invoke()` and map the\n * never-throwing {@link import(\"@warlock.js/ai\").ToolInvokeResult}: `data`\n * → a text content block, `error` → an `isError: true` result. An unknown\n * tool name throws (mapped to a JSON-RPC error by {@link handle}).\n *\n * The contract receives a `ToolContext` carrying the abort signal and,\n * over HTTP, the inbound request's headers on `ctx.request`.\n */\n private async callTool(\n params: unknown,\n context: McpServeCallContext,\n ): Promise<McpToolCallResult> {\n const { name, args } = readCallParams(params);\n const contract = this.byName.get(name);\n\n if (!contract) {\n throw new McpTransportError(`Unknown tool \"${name}\".`, {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const toolContext: ToolContext = { artifacts: {} };\n\n if (context.signal) {\n toolContext.signal = context.signal;\n }\n\n if (context.request) {\n const { headers, remoteAddress } = context.request;\n\n toolContext.request = remoteAddress === undefined ? { headers } : { headers, remoteAddress };\n }\n\n const result = await contract.invoke(args, toolContext);\n\n if (result.error) {\n return {\n content: [{ type: \"text\", text: result.error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: serializeData(result.data) }],\n isError: false,\n };\n }\n\n /** Build the `resources/list` payload (metadata only, no contents). */\n private listResources(resources: Map<string, McpServeResource>): Record<string, unknown>[] {\n return [...resources.values()].map((resource) =>\n compact({\n uri: resource.uri,\n name: resource.name,\n title: resource.title,\n description: resource.description,\n mimeType: resource.mimeType,\n }),\n );\n }\n\n /**\n * Answer `resources/read`: run the resource's `read()` and wrap the\n * result as one text or base64 blob content entry.\n */\n private async readResource(\n resources: Map<string, McpServeResource>,\n params: unknown,\n context: McpServeCallContext,\n ): Promise<Record<string, unknown>> {\n const uri = readRecord(params).uri;\n\n if (typeof uri !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, \"resources/read requires a string `uri`.\");\n }\n\n const resource = resources.get(uri);\n\n if (!resource) {\n throw new RpcFailure(MCP_RESOURCE_NOT_FOUND, \"Resource not found\", { uri });\n }\n\n const body = await resource.read(context);\n const entry =\n typeof body === \"string\"\n ? { uri, mimeType: resource.mimeType, text: body }\n : { uri, mimeType: resource.mimeType, blob: Buffer.from(body).toString(\"base64\") };\n\n return { contents: [compact(entry)] };\n }\n\n /** Build the `prompts/list` payload. */\n private listPrompts(prompts: Map<string, McpServePrompt>): Record<string, unknown>[] {\n return [...prompts.values()].map((prompt) =>\n compact({\n name: prompt.name,\n title: prompt.title,\n description: prompt.description,\n arguments: prompt.arguments,\n }),\n );\n }\n\n /**\n * Answer `prompts/get`: validate the arguments (strings only; required\n * ones present) and build the messages. An unknown prompt or a missing\n * required argument answers invalid-params, per the MCP prompts spec.\n */\n private async getPrompt(\n prompts: Map<string, McpServePrompt>,\n params: unknown,\n context: McpServeCallContext,\n ): Promise<Record<string, unknown>> {\n const record = readRecord(params);\n const name = record.name;\n\n if (typeof name !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, \"prompts/get requires a string `name`.\");\n }\n\n const prompt = prompts.get(name);\n\n if (!prompt) {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, `Unknown prompt \"${name}\".`);\n }\n\n const args: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(readRecord(record.arguments))) {\n if (typeof value !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, `Prompt argument \"${key}\" must be a string.`);\n }\n\n args[key] = value;\n }\n\n for (const argument of prompt.arguments ?? []) {\n if (argument.required && args[argument.name] === undefined) {\n throw new RpcFailure(\n JSON_RPC_INVALID_PARAMS,\n `Prompt \"${name}\" requires the \"${argument.name}\" argument.`,\n );\n }\n }\n\n const built = await prompt.get(args, context);\n const messages: McpPromptMessage[] =\n typeof built === \"string\"\n ? [{ role: \"user\", content: { type: \"text\", text: built } }]\n : built;\n\n return compact({ description: prompt.description, messages });\n }\n\n /** Build a JSON-RPC success response. */\n private ok(id: JsonRpcRequest[\"id\"], result: unknown): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, result };\n }\n\n /** Build a JSON-RPC error response. */\n private error(\n id: JsonRpcRequest[\"id\"],\n code: number,\n message: string,\n data?: unknown,\n ): JsonRpcResponse {\n const error = data === undefined ? { code, message } : { code, message, data };\n\n return { jsonrpc: JSONRPC_VERSION, id, error };\n }\n}\n\n/** JSON-RPC \"Invalid Request\" — the line is JSON but not a request object. */\nconst JSON_RPC_INVALID_REQUEST = -32600;\n\n/** JSON-RPC \"Internal error\" — answering the request failed unexpectedly. */\nconst JSON_RPC_INTERNAL_ERROR = -32603;\n\n/**\n * Turn one stdio line into the line to write back, or `undefined` when\n * nothing is answered: blank or non-JSON lines and notifications (no `id`).\n * A JSON value that is not a request object (`null`, a number, an array) is\n * answered with a JSON-RPC \"Invalid Request\" error whose `id` is `null`.\n * Never rejects: a dispatch failure is answered as an internal error.\n */\nexport async function answerStdioLine(\n line: string,\n dispatch: (request: JsonRpcRequest) => Promise<JsonRpcResponse>,\n): Promise<string | undefined> {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return undefined;\n }\n\n let message: unknown;\n\n try {\n message = JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n\n if (typeof message !== \"object\" || message === null || Array.isArray(message)) {\n const invalid = {\n jsonrpc: JSONRPC_VERSION,\n id: null,\n error: { code: JSON_RPC_INVALID_REQUEST, message: \"Invalid request: not a JSON-RPC object.\" },\n };\n\n return `${JSON.stringify(invalid)}\\n`;\n }\n\n const request = message as JsonRpcRequest;\n const id: unknown = request.id;\n\n if (id === undefined || id === null) {\n // A notification (e.g. notifications/initialized) — nothing to answer.\n return undefined;\n }\n\n try {\n const response = await dispatch(request);\n\n return `${JSON.stringify(response)}\\n`;\n } catch {\n const failed = {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: { code: JSON_RPC_INTERNAL_ERROR, message: \"Internal error.\" },\n };\n\n return `${JSON.stringify(failed)}\\n`;\n }\n}\n\n/** Read an unknown value as a plain record (non-objects read as empty). */\nfunction readRecord(value: unknown): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return {};\n }\n\n return value as Record<string, unknown>;\n}\n\n/** Drop `undefined` members so optional wire fields are omitted, not `null`. */\nfunction compact(value: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, member] of Object.entries(value)) {\n if (member !== undefined) {\n result[key] = member;\n }\n }\n\n return result;\n}\n\n/**\n * Read and validate the `tools/call` params into `{ name, args }`. Throws\n * a typed {@link McpTransportError} when `name` is missing — mapped to a\n * JSON-RPC `invalid params` error by the handler.\n */\nfunction readCallParams(params: unknown): { name: string; args: unknown } {\n if (typeof params !== \"object\" || params === null) {\n throw new McpTransportError(\"tools/call params must be an object.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const record = params as { name?: unknown; arguments?: unknown };\n\n if (typeof record.name !== \"string\") {\n throw new McpTransportError(\"tools/call requires a string `name`.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n return { name: record.name, args: record.arguments ?? {} };\n}\n\n/**\n * Serialize a tool's `data` for an MCP text content block — a string is\n * passed verbatim, everything else is JSON-stringified so structured\n * output crosses the wire as text the consuming client can re-parse.\n */\nfunction serializeData(data: unknown): string {\n if (typeof data === \"string\") {\n return data;\n }\n\n if (data === undefined) {\n return \"\";\n }\n\n return JSON.stringify(data);\n}\n\n/**\n * The pure protocol handler: one JSON-RPC request in, one response out.\n */\nexport interface McpServeRequestHandler {\n /**\n * Map one request to its response.\n *\n * @param request - The JSON-RPC request.\n * @param context - Optional per-call context (inbound HTTP request, abort signal).\n */\n handle(request: JsonRpcRequest, context?: McpServeCallContext): Promise<JsonRpcResponse>;\n}\n\n/**\n * Build the pure protocol handler for a serve source. Exposed (alongside\n * {@link serve}) so callers and tests can drive the MCP protocol without an\n * actual transport — feed it a JSON-RPC request, get the response.\n *\n * Throws {@link McpServeError} on duplicate tool names / resource URIs /\n * prompt names, or `prefixToolNames` on an ungrouped source.\n *\n * @param source - The tools to expose (one tool set or `{ groups }`).\n * @param options - Serve options (name / version / schema target / resources / prompts).\n * @returns An object whose `handle(request, context?)` maps a request to a response.\n */\nexport function createServeHandler(\n source: McpServeSource,\n options: McpServeOptions,\n): McpServeRequestHandler {\n return new McpServeHandler(source, options);\n}\n\n/**\n * The internal {@link McpServer} — owns a {@link McpServeHandler} and a\n * transport pump. For `stdio` it reads newline-delimited JSON-RPC requests\n * from `process.stdin` and writes responses to `process.stdout`; for\n * `http` it binds a Streamable HTTP endpoint (see `./serve-http`).\n *\n * Constructed via {@link serve}; the class itself is internal.\n */\nclass McpServerImpl implements McpServer {\n /** The pure protocol handler. */\n private readonly handler: McpServeHandler;\n\n /** Serve options (transport selection lives here). */\n private readonly options: McpServeOptions;\n\n /** The running handle (stdio or http), while started or starting. */\n private running: Promise<McpServeHandle> | undefined;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.handler = new McpServeHandler(source, options);\n this.options = options;\n }\n\n public start(): Promise<McpServeHandle> {\n if (!this.running) {\n const transport = this.options.transport ?? { type: \"stdio\" };\n const starting =\n transport.type === \"http\" ? this.startHttp(transport) : Promise.resolve(this.startStdio());\n\n // A failed start leaves the server startable again.\n this.running = starting.catch((error: unknown) => {\n this.running = undefined;\n throw error;\n });\n }\n\n return this.running;\n }\n\n /** Bind the HTTP endpoint; its `close()` also makes this server startable again. */\n private async startHttp(transport: McpHttpServeTransport): Promise<McpHttpServeHandle> {\n const http = await startHttpServer(transport, (request, context) =>\n this.handler.handle(request, context),\n );\n\n return {\n url: http.url,\n close: async () => {\n await http.close();\n this.running = undefined;\n },\n };\n }\n\n /** Wire the stdin line reader and answer each request on stdout. */\n private startStdio(): McpServeHandle {\n const reader: Interface = createInterface({ input: process.stdin });\n\n reader.on(\"line\", (line) => {\n // A failure answering one line must never become an unhandled\n // rejection: that would take the whole server process down.\n void this.onLine(line).catch(() => {});\n });\n\n return {\n close: async () => {\n reader.close();\n this.running = undefined;\n },\n };\n }\n\n /** Answer one stdin line on stdout (see {@link answerStdioLine}). */\n private async onLine(line: string): Promise<void> {\n const reply = await answerStdioLine(line, (request) => this.handler.handle(request));\n\n if (reply !== undefined) {\n process.stdout.write(reply);\n }\n }\n\n public async stop(): Promise<void> {\n const running = this.running;\n\n if (!running) {\n return;\n }\n\n let handle: McpServeHandle;\n\n try {\n handle = await running;\n } catch {\n // A start that failed has nothing to close.\n return;\n }\n\n await handle.close();\n }\n}\n\n/**\n * Expose a built agent / supervisor / orchestrator (or a raw\n * `ToolContract[]`, or several named groups) AS an MCP server (Direction B:\n * local primitive → MCP server other clients consume).\n *\n * Enumerates the tools once, here. Every exposed name must be unique — a\n * duplicate throws {@link McpServeError} (`type: \"collision\"`) right away;\n * serve `{ groups: { … } }` with `prefixToolNames: true` to namespace\n * same-named tools from different sets. `tools/list` answers with each\n * tool's `inputSchema` extracted via `extractJsonSchema` at the configured\n * `schemaTarget` (default `draft-2020-12`). `tools/call` routes to the\n * contract's `invoke()` and maps the never-throwing result — `data`\n * becomes a text content block, `error` an `isError: true` result.\n *\n * Transports: `stdio` (default) pumps `process.stdin` / `process.stdout`;\n * `http` binds an MCP Streamable HTTP endpoint on `127.0.0.1` by default,\n * with bearer / custom auth (required off loopback), `Origin` and `Host`\n * validation against DNS rebinding, a body-size cap, and a draining\n * `close()`. `start()` resolves `{ url, close }`.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, schema dialect, resources, prompts.\n * @returns An {@link McpServer} with `start()` / `stop()`.\n *\n * @example\n * const { url, close } = await serve([calc, clock], {\n * name: \"session-tools\",\n * transport: { type: \"http\", auth: { bearer: sessionToken } },\n * }).start();\n */\nexport function serve(\n source: McpServeSource,\n options: McpServeOptions & { transport: McpHttpServeTransport },\n): McpServer<McpHttpServeHandle>;\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer;\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer {\n return new McpServerImpl(source, options);\n}\n"],"mappings":";;;;;;;;AAyBA,MAAM,kBAAkB;;AAGxB,MAAM,wBAAwB;;AAG9B,MAAM,kBAAkB;;AAGxB,MAAM,4BAA4B;AAClC,MAAM,0BAA0B;;AAGhC,MAAM,yBAAyB;;;;;AAc/B,IAAM,aAAN,cAAyB,MAAM;CAK7B,AAAO,YAAY,MAAc,SAAiB,MAAgB;EAChE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;;AAGA,SAAS,QAAQ,QAAuC;CACtD,OAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,OAAO,MAAM;AACvD;;AAGA,SAAS,gBAAgB,QAAwD;CAC/E,OAAO,CAAC,MAAM,QAAQ,MAAM,KAAK,YAAY;AAC/C;;;;;;;AAQA,SAAS,aAAa,QAAwB,SAAyC;CACrF,MAAM,SAAS,QAAQ,mBAAmB;CAC1C,MAAM,UAAmD,CAAC;CAE1D,IAAI,gBAAgB,MAAM,GACxB,KAAK,MAAM,CAAC,OAAO,gBAAgB,OAAO,QAAQ,OAAO,MAAM,GAC7D,KAAK,MAAM,YAAY,QAAQ,WAAW,GAAG;EAC3C,IAAI,OAAO,SAAS;EAEpB,IAAI,WAAW,MACb,OAAO,GAAG,MAAM,GAAG,SAAS;OACvB,IAAI,OAAO,WAAW,YAC3B,OAAO,OAAO,OAAO,SAAS,IAAI;EAGpC,QAAQ,KAAK;GAAE;GAAM;GAAU,QAAQ,UAAU,MAAM;EAAG,CAAC;CAC7D;MAEG;EACL,IAAI,WAAW,OACb,MAAM,IAAI,cACR,kFACA,EAAE,MAAM,SAAS,CACnB;EAGF,KAAK,MAAM,YAAY,QAAQ,MAAM,GACnC,QAAQ,KAAK;GAAE,MAAM,SAAS;GAAM;GAAU,QAAQ;EAAa,CAAC;CAExE;CAEA,MAAM,uBAAO,IAAI,IAAoB;CAErC,KAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,WAAW,KAAK,IAAI,KAAK,IAAI;EAEnC,IAAI,aAAa,QACf,MAAM,IAAI,cACR,kBAAkB,KAAK,KAAK,2BAA2B,SAAS,OAAO,KAAK,OAAO,sEAEnF;GAAE,MAAM;GAAa,SAAS,EAAE,MAAM,KAAK,KAAK;EAAE,CACpD;EAGF,KAAK,IAAI,KAAK,MAAM,KAAK,MAAM;CACjC;CAEA,OAAO,QAAQ,KAAK,EAAE,MAAM,gBAAgB;EAAE;EAAM;CAAS,EAAE;AACjE;;AAGA,SAAS,aACP,SACA,KACA,OACM;CACN,MAAM,uBAAO,IAAI,IAAY;CAE7B,KAAK,MAAM,SAAS,WAAW,CAAC,GAAG;EACjC,MAAM,QAAQ,IAAI,KAAK;EAEvB,IAAI,KAAK,IAAI,KAAK,GAChB,MAAM,IAAI,cAAc,OAAO,MAAM,IAAI,MAAM,uBAAuB;GACpE,MAAM;GACN,SAAS,GAAG,QAAQ,MAAM;EAC5B,CAAC;EAGH,KAAK,IAAI,KAAK;CAChB;AACF;;;;;;;;;;;;;AAcA,IAAM,kBAAN,MAAsB;CAgBpB,AAAO,YAAY,QAAwB,SAA0B;EACnE,KAAK,QAAQ,aAAa,QAAQ,OAAO;EACzC,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,KAAK,SAAS,CAAC,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC;EAE1E,aAAa,QAAQ,YAAY,aAAa,SAAS,KAAK,UAAU;EACtE,aAAa,QAAQ,UAAU,WAAW,OAAO,MAAM,QAAQ;EAE/D,KAAK,YAAY,QAAQ,YACrB,IAAI,IAAI,QAAQ,UAAU,KAAK,aAAa,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,IACrE;EACJ,KAAK,UAAU,QAAQ,UACnB,IAAI,IAAI,QAAQ,QAAQ,KAAK,WAAW,CAAC,OAAO,MAAM,MAAM,CAAC,CAAC,IAC9D;EACJ,KAAK,UAAU;CACjB;;;;;;;;;;CAWA,MAAa,OACX,SACA,UAA+B,CAAC,GACN;EAC1B,IAAI;GACF,OAAO,KAAK,GAAG,QAAQ,IAAI,MAAM,KAAK,SAAS,SAAS,OAAO,CAAC;EAClE,SAAS,OAAO;GACd,IAAI,iBAAiB,YACnB,OAAO,KAAK,MAAM,QAAQ,IAAI,MAAM,MAAM,MAAM,SAAS,MAAM,IAAI;GAGrE,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAErE,OAAO,KAAK,MAAM,QAAQ,IAAI,yBAAyB,OAAO;EAChE;CACF;;CAGA,MAAc,SAAS,SAAyB,SAAgD;EAC9F,QAAQ,QAAQ,QAAhB;GACE,KAAK,cACH,OAAO,KAAK,iBAAiB,QAAQ,MAAM;GAC7C,KAAK,QACH,OAAO,CAAC;GACV,KAAK,cACH,OAAO,EAAE,OAAO,KAAK,UAAU,EAAE;GACnC,KAAK,cACH,OAAO,KAAK,SAAS,QAAQ,QAAQ,OAAO;GAC9C,SACE;EACJ;EAEA,IAAI,KAAK,WACP,QAAQ,QAAQ,QAAhB;GACE,KAAK,kBACH,OAAO,EAAE,WAAW,KAAK,cAAc,KAAK,SAAS,EAAE;GACzD,KAAK,4BACH,OAAO,EAAE,mBAAmB,CAAC,EAAE;GACjC,KAAK,kBACH,OAAO,KAAK,aAAa,KAAK,WAAW,QAAQ,QAAQ,OAAO;GAClE,SACE;EACJ;EAGF,IAAI,KAAK,SACP,QAAQ,QAAQ,QAAhB;GACE,KAAK,gBACH,OAAO,EAAE,SAAS,KAAK,YAAY,KAAK,OAAO,EAAE;GACnD,KAAK,eACH,OAAO,KAAK,UAAU,KAAK,SAAS,QAAQ,QAAQ,OAAO;GAC7D,SACE;EACJ;EAGF,MAAM,IAAI,WACR,2BACA,WAAW,QAAQ,OAAO,uCAC5B;CACF;;;;;;CAOA,AAAQ,iBAAiB,QAA0C;EACjE,MAAM,YAAY,WAAW,MAAM,CAAC,CAAC;EACrC,MAAM,kBACJ,OAAO,cAAc,YAAY,4BAA4B,SAAS,SAAS,IAC3E,YACA;EAEN,MAAM,eAAwC,EAAE,OAAO,CAAC,EAAE;EAE1D,IAAI,KAAK,WACP,aAAa,YAAY,CAAC;EAG5B,IAAI,KAAK,SACP,aAAa,UAAU,CAAC;EAG1B,OAAO;GACL;GACA;GACA,YAAY;IACV,MAAM,KAAK,QAAQ;IACnB,SAAS,KAAK,QAAQ,WAAW;GACnC;EACF;CACF;;;;;;;CAQA,AAAQ,YAAiC;EACvC,MAAM,SAAS,KAAK,QAAQ,gBAAgB;EAE5C,OAAO,KAAK,MAAM,KAAK,EAAE,MAAM,eAAe;GAC5C,MAAM,cAAc,kBAAkB,SAAS,OAAO,EAAE,OAAO,CAAC,KAAK;IACnE,MAAM;IACN,YAAY,CAAC;GACf;GAEA,OAAO;IACL;IACA,aAAa,SAAS;IACtB;GACF;EACF,CAAC;CACH;;;;;;;;;;CAWA,MAAc,SACZ,QACA,SAC4B;EAC5B,MAAM,EAAE,MAAM,SAAS,eAAe,MAAM;EAC5C,MAAM,WAAW,KAAK,OAAO,IAAI,IAAI;EAErC,IAAI,CAAC,UACH,MAAM,IAAI,kBAAkB,iBAAiB,KAAK,KAAK;GACrD,MAAM;GACN,QAAQ;EACV,CAAC;EAGH,MAAM,cAA2B,EAAE,WAAW,CAAC,EAAE;EAEjD,IAAI,QAAQ,QACV,YAAY,SAAS,QAAQ;EAG/B,IAAI,QAAQ,SAAS;GACnB,MAAM,EAAE,SAAS,kBAAkB,QAAQ;GAE3C,YAAY,UAAU,kBAAkB,SAAY,EAAE,QAAQ,IAAI;IAAE;IAAS;GAAc;EAC7F;EAEA,MAAM,SAAS,MAAM,SAAS,OAAO,MAAM,WAAW;EAEtD,IAAI,OAAO,OACT,OAAO;GACL,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,OAAO,MAAM;GAAQ,CAAC;GACtD,SAAS;EACX;EAGF,OAAO;GACL,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM,cAAc,OAAO,IAAI;GAAE,CAAC;GAC5D,SAAS;EACX;CACF;;CAGA,AAAQ,cAAc,WAAqE;EACzF,OAAO,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK,aAClC,QAAQ;GACN,KAAK,SAAS;GACd,MAAM,SAAS;GACf,OAAO,SAAS;GAChB,aAAa,SAAS;GACtB,UAAU,SAAS;EACrB,CAAC,CACH;CACF;;;;;CAMA,MAAc,aACZ,WACA,QACA,SACkC;EAClC,MAAM,MAAM,WAAW,MAAM,CAAC,CAAC;EAE/B,IAAI,OAAO,QAAQ,UACjB,MAAM,IAAI,WAAW,yBAAyB,yCAAyC;EAGzF,MAAM,WAAW,UAAU,IAAI,GAAG;EAElC,IAAI,CAAC,UACH,MAAM,IAAI,WAAW,wBAAwB,sBAAsB,EAAE,IAAI,CAAC;EAG5E,MAAM,OAAO,MAAM,SAAS,KAAK,OAAO;EAMxC,OAAO,EAAE,UAAU,CAAC,QAJlB,OAAO,SAAS,WACZ;GAAE;GAAK,UAAU,SAAS;GAAU,MAAM;EAAK,IAC/C;GAAE;GAAK,UAAU,SAAS;GAAU,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,QAAQ;EAAE,CAEpD,CAAC,EAAE;CACtC;;CAGA,AAAQ,YAAY,SAAiE;EACnF,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,WAChC,QAAQ;GACN,MAAM,OAAO;GACb,OAAO,OAAO;GACd,aAAa,OAAO;GACpB,WAAW,OAAO;EACpB,CAAC,CACH;CACF;;;;;;CAOA,MAAc,UACZ,SACA,QACA,SACkC;EAClC,MAAM,SAAS,WAAW,MAAM;EAChC,MAAM,OAAO,OAAO;EAEpB,IAAI,OAAO,SAAS,UAClB,MAAM,IAAI,WAAW,yBAAyB,uCAAuC;EAGvF,MAAM,SAAS,QAAQ,IAAI,IAAI;EAE/B,IAAI,CAAC,QACH,MAAM,IAAI,WAAW,yBAAyB,mBAAmB,KAAK,GAAG;EAG3E,MAAM,OAA+B,CAAC;EAEtC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,WAAW,OAAO,SAAS,CAAC,GAAG;GACvE,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,WAAW,yBAAyB,oBAAoB,IAAI,oBAAoB;GAG5F,KAAK,OAAO;EACd;EAEA,KAAK,MAAM,YAAY,OAAO,aAAa,CAAC,GAC1C,IAAI,SAAS,YAAY,KAAK,SAAS,UAAU,QAC/C,MAAM,IAAI,WACR,yBACA,WAAW,KAAK,kBAAkB,SAAS,KAAK,YAClD;EAIJ,MAAM,QAAQ,MAAM,OAAO,IAAI,MAAM,OAAO;EAC5C,MAAM,WACJ,OAAO,UAAU,WACb,CAAC;GAAE,MAAM;GAAQ,SAAS;IAAE,MAAM;IAAQ,MAAM;GAAM;EAAE,CAAC,IACzD;EAEN,OAAO,QAAQ;GAAE,aAAa,OAAO;GAAa;EAAS,CAAC;CAC9D;;CAGA,AAAQ,GAAG,IAA0B,QAAkC;EACrE,OAAO;GAAE,SAAS;GAAiB;GAAI;EAAO;CAChD;;CAGA,AAAQ,MACN,IACA,MACA,SACA,MACiB;EAGjB,OAAO;GAAE,SAAS;GAAiB;GAAI,OAFzB,SAAS,SAAY;IAAE;IAAM;GAAQ,IAAI;IAAE;IAAM;IAAS;GAAK;EAEhC;CAC/C;AACF;;AAGA,MAAM,2BAA2B;;AAGjC,MAAM,0BAA0B;;;;;;;;AAShC,eAAsB,gBACpB,MACA,UAC6B;CAC7B,MAAM,UAAU,KAAK,KAAK;CAE1B,IAAI,CAAC,SACH;CAGF,IAAI;CAEJ,IAAI;EACF,UAAU,KAAK,MAAM,OAAO;CAC9B,QAAQ;EACN;CACF;CAEA,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAO1E,OAAO,GAAG,KAAK,UAAU;EALvB,SAAS;EACT,IAAI;EACJ,OAAO;GAAE,MAAM;GAA0B,SAAS;EAA0C;CAG/D,CAAC,EAAE;CAGpC,MAAM,UAAU;CAChB,MAAM,KAAc,QAAQ;CAE5B,IAAI,OAAO,UAAa,OAAO,MAE7B;CAGF,IAAI;EACF,MAAM,WAAW,MAAM,SAAS,OAAO;EAEvC,OAAO,GAAG,KAAK,UAAU,QAAQ,EAAE;CACrC,QAAQ;EAON,OAAO,GAAG,KAAK,UAAU;GALvB,SAAS;GACT;GACA,OAAO;IAAE,MAAM;IAAyB,SAAS;GAAkB;EAGvC,CAAC,EAAE;CACnC;AACF;;AAGA,SAAS,WAAW,OAAyC;CAC3D,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,OAAO,CAAC;CAGV,OAAO;AACT;;AAGA,SAAS,QAAQ,OAAyD;CACxE,MAAM,SAAkC,CAAC;CAEzC,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,KAAK,GAC9C,IAAI,WAAW,QACb,OAAO,OAAO;CAIlB,OAAO;AACT;;;;;;AAOA,SAAS,eAAe,QAAkD;CACxE,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,MAAM,IAAI,kBAAkB,wCAAwC;EAClE,MAAM;EACN,QAAQ;CACV,CAAC;CAGH,MAAM,SAAS;CAEf,IAAI,OAAO,OAAO,SAAS,UACzB,MAAM,IAAI,kBAAkB,wCAAwC;EAClE,MAAM;EACN,QAAQ;CACV,CAAC;CAGH,OAAO;EAAE,MAAM,OAAO;EAAM,MAAM,OAAO,aAAa,CAAC;CAAE;AAC3D;;;;;;AAOA,SAAS,cAAc,MAAuB;CAC5C,IAAI,OAAO,SAAS,UAClB,OAAO;CAGT,IAAI,SAAS,QACX,OAAO;CAGT,OAAO,KAAK,UAAU,IAAI;AAC5B;;;;;;;;;;;;;AA2BA,SAAgB,mBACd,QACA,SACwB;CACxB,OAAO,IAAI,gBAAgB,QAAQ,OAAO;AAC5C;;;;;;;;;AAUA,IAAM,gBAAN,MAAyC;CAUvC,AAAO,YAAY,QAAwB,SAA0B;EACnE,KAAK,UAAU,IAAI,gBAAgB,QAAQ,OAAO;EAClD,KAAK,UAAU;CACjB;CAEA,AAAO,QAAiC;EACtC,IAAI,CAAC,KAAK,SAAS;GACjB,MAAM,YAAY,KAAK,QAAQ,aAAa,EAAE,MAAM,QAAQ;GAC5D,MAAM,WACJ,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,IAAI,QAAQ,QAAQ,KAAK,WAAW,CAAC;GAG3F,KAAK,UAAU,SAAS,OAAO,UAAmB;IAChD,KAAK,UAAU;IACf,MAAM;GACR,CAAC;EACH;EAEA,OAAO,KAAK;CACd;;CAGA,MAAc,UAAU,WAA+D;EACrF,MAAM,OAAO,MAAM,gBAAgB,YAAY,SAAS,YACtD,KAAK,QAAQ,OAAO,SAAS,OAAO,CACtC;EAEA,OAAO;GACL,KAAK,KAAK;GACV,OAAO,YAAY;IACjB,MAAM,KAAK,MAAM;IACjB,KAAK,UAAU;GACjB;EACF;CACF;;CAGA,AAAQ,aAA6B;EACnC,MAAM,SAAoB,gBAAgB,EAAE,OAAO,QAAQ,MAAM,CAAC;EAElE,OAAO,GAAG,SAAS,SAAS;GAG1B,AAAK,KAAK,OAAO,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;EACvC,CAAC;EAED,OAAO,EACL,OAAO,YAAY;GACjB,OAAO,MAAM;GACb,KAAK,UAAU;EACjB,EACF;CACF;;CAGA,MAAc,OAAO,MAA6B;EAChD,MAAM,QAAQ,MAAM,gBAAgB,OAAO,YAAY,KAAK,QAAQ,OAAO,OAAO,CAAC;EAEnF,IAAI,UAAU,QACZ,QAAQ,OAAO,MAAM,KAAK;CAE9B;CAEA,MAAa,OAAsB;EACjC,MAAM,UAAU,KAAK;EAErB,IAAI,CAAC,SACH;EAGF,IAAI;EAEJ,IAAI;GACF,SAAS,MAAM;EACjB,QAAQ;GAEN;EACF;EAEA,MAAM,OAAO,MAAM;CACrB;AACF;AAqCA,SAAgB,MAAM,QAAwB,SAAqC;CACjF,OAAO,IAAI,cAAc,QAAQ,OAAO;AAC1C"}
|
|
1
|
+
{"version":3,"file":"serve.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/serve.ts"],"sourcesContent":["import { createInterface, type Interface } from \"node:readline\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { extractJsonSchema, type ToolContext, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpGroupedToolSource,\n McpHttpServeHandle,\n McpHttpServeTransport,\n McpPromptMessage,\n McpServeCallContext,\n McpServeHandle,\n McpServeOptions,\n McpServePrompt,\n McpServeResource,\n McpServeSource,\n McpServeToolContext,\n McpStdioServeTransport,\n McpServer,\n McpToolCallResult,\n McpToolDescriptor,\n McpToolSource,\n} from \"../contracts\";\nimport { McpServeError, McpTransportError } from \"../errors\";\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nimport { startHttpServer } from \"./serve-http\";\nimport { McpServeRequests } from \"./serve-requests\";\nimport {\n assertMcpResult,\n isMcpResult,\n isRecord,\n looksLikeMcpResult,\n unwrapMcpResult,\n} from \"./result\";\n\n/** JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/** Default JSON-Schema dialect emitted for each tool's `inputSchema`. */\nconst DEFAULT_SCHEMA_TARGET = \"draft-2020-12\";\n\n/** Default advertised server version when the caller omits one. */\nconst DEFAULT_VERSION = \"4.4.0\";\n\n/** JSON-RPC standard error codes we emit. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\nconst JSON_RPC_INVALID_PARAMS = -32602;\n\n/** MCP's \"resource not found\" error code (server/resources, error handling). */\nconst MCP_RESOURCE_NOT_FOUND = -32002;\n\n/** One tool as exposed on the wire: its public name plus the contract behind it. */\ntype ExposedTool = {\n /** The name clients see in `tools/list` and send in `tools/call`. */\n name: string;\n /** The contract that runs. */\n contract: ToolContract;\n};\n\n/**\n * A JSON-RPC failure raised inside a method handler, carrying the exact\n * code to answer with (unknown resource, missing prompt argument, …).\n */\nclass RpcFailure extends Error {\n public readonly code: number;\n\n public readonly data: unknown;\n\n public constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.code = code;\n this.data = data;\n }\n}\n\n/** Enumerate one tool set into a flat contract array. */\nfunction toolsOf(source: McpToolSource): ToolContract[] {\n return Array.isArray(source) ? source : source.tools();\n}\n\n/** Narrow a serve source to its grouped shape. */\nfunction isGroupedSource(source: McpServeSource): source is McpGroupedToolSource {\n return !Array.isArray(source) && \"groups\" in source;\n}\n\n/**\n * Resolve the serve source into the exposed tool list, applying\n * `prefixToolNames` and refusing duplicate names. Throws\n * {@link McpServeError} — `\"config\"` for `prefixToolNames` on an ungrouped\n * source, `\"collision\"` when two tools would share a name.\n */\nfunction resolveTools(source: McpServeSource, options: McpServeOptions): ExposedTool[] {\n const prefix = options.prefixToolNames ?? false;\n const exposed: Array<ExposedTool & { origin: string }> = [];\n\n if (isGroupedSource(source)) {\n for (const [group, groupSource] of Object.entries(source.groups)) {\n for (const contract of toolsOf(groupSource)) {\n let name = contract.name;\n\n if (prefix === true) {\n name = `${group}_${contract.name}`;\n } else if (typeof prefix === \"function\") {\n name = prefix(group, contract.name);\n }\n\n exposed.push({ name, contract, origin: `group \"${group}\"` });\n }\n }\n } else {\n if (prefix !== false) {\n throw new McpServeError(\n \"prefixToolNames needs a grouped source: serve({ groups: { name: tools } }, …).\",\n { type: \"config\" },\n );\n }\n\n for (const contract of toolsOf(source)) {\n exposed.push({ name: contract.name, contract, origin: \"the source\" });\n }\n }\n\n const seen = new Map<string, string>();\n\n for (const tool of exposed) {\n const previous = seen.get(tool.name);\n\n if (previous !== undefined) {\n throw new McpServeError(\n `MCP tool name \"${tool.name}\" is exposed twice (from ${previous} and ${tool.origin}). ` +\n \"Rename one tool, or serve a grouped source with prefixToolNames.\",\n { type: \"collision\", context: { name: tool.name } },\n );\n }\n\n seen.set(tool.name, tool.origin);\n }\n\n return exposed.map(({ name, contract }) => ({ name, contract }));\n}\n\n/** Throw a collision error when two entries share the same key. */\nfunction assertUnique<T>(\n entries: readonly T[] | undefined,\n key: (entry: T) => string,\n label: string,\n): void {\n const seen = new Set<string>();\n\n for (const entry of entries ?? []) {\n const value = key(entry);\n\n if (seen.has(value)) {\n throw new McpServeError(`MCP ${label} \"${value}\" is declared twice.`, {\n type: \"collision\",\n context: { [label]: value },\n });\n }\n\n seen.add(value);\n }\n}\n\n/**\n * The pure protocol core of `serve` — maps one JSON-RPC request to its\n * response, with no I/O. Both the stdio and HTTP serve-transports pump\n * their inbound requests through this, and specs can drive it directly.\n *\n * Handles `initialize`, `ping`, `tools/list`, `tools/call`, and — when\n * configured — `resources/list`, `resources/templates/list`,\n * `resources/read`, `prompts/list` and `prompts/get`. Any other method\n * answers with a JSON-RPC `method not found` error.\n *\n * Constructed via {@link createServeHandler}.\n */\nclass McpServeHandler {\n /** The tools this server exposes (snapshotted at construction). */\n private readonly tools: ExposedTool[];\n\n /** Fast lookup by exposed tool name for `tools/call` dispatch. */\n private readonly byName: Map<string, ToolContract>;\n\n /** Resources by URI, or `undefined` when the capability is off. */\n private readonly resources: Map<string, McpServeResource> | undefined;\n\n /** Prompts by name, or `undefined` when the capability is off. */\n private readonly prompts: Map<string, McpServePrompt> | undefined;\n\n /** Construction-time serve options (name / version / schema target). */\n private readonly options: McpServeOptions;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.tools = resolveTools(source, options);\n this.byName = new Map(this.tools.map((tool) => [tool.name, tool.contract]));\n\n assertUnique(options.resources, (resource) => resource.uri, \"resource\");\n assertUnique(options.prompts, (prompt) => prompt.name, \"prompt\");\n\n this.resources = options.resources\n ? new Map(options.resources.map((resource) => [resource.uri, resource]))\n : undefined;\n this.prompts = options.prompts\n ? new Map(options.prompts.map((prompt) => [prompt.name, prompt]))\n : undefined;\n this.options = options;\n }\n\n /**\n * Dispatch one inbound JSON-RPC request to its handler and produce the\n * response. A handler that throws is mapped to a JSON-RPC error response\n * — the serve loop never crashes on a bad request.\n *\n * @param request - The JSON-RPC request.\n * @param context - Per-call context: the inbound HTTP request (headers\n * reach tools as `ctx.request`) and an abort signal.\n */\n public async handle(\n request: JsonRpcRequest,\n context: McpServeCallContext = {},\n ): Promise<JsonRpcResponse> {\n try {\n return this.ok(\n request.id,\n await this.dispatch(request, { ...context, requestId: request.id }),\n );\n } catch (cause) {\n if (cause instanceof RpcFailure) {\n return this.error(request.id, cause.code, cause.message, cause.data);\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n return this.error(request.id, JSON_RPC_INVALID_PARAMS, message);\n }\n }\n\n /** Route a method to its result builder. */\n private async dispatch(request: JsonRpcRequest, context: McpServeCallContext): Promise<unknown> {\n switch (request.method) {\n case \"initialize\":\n return this.initializeResult(request.params);\n case \"ping\":\n return {};\n case \"tools/list\":\n return { tools: this.listTools() };\n case \"tools/call\":\n return this.callTool(request.params, { ...context, requestId: request.id });\n default:\n break;\n }\n\n if (this.resources) {\n switch (request.method) {\n case \"resources/list\":\n return { resources: this.listResources(this.resources) };\n case \"resources/templates/list\":\n return { resourceTemplates: [] };\n case \"resources/read\":\n return this.readResource(this.resources, request.params, context);\n default:\n break;\n }\n }\n\n if (this.prompts) {\n switch (request.method) {\n case \"prompts/list\":\n return { prompts: this.listPrompts(this.prompts) };\n case \"prompts/get\":\n return this.getPrompt(this.prompts, request.params, context);\n default:\n break;\n }\n }\n\n throw new RpcFailure(\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${request.method}\" is not supported by this MCP server.`,\n );\n }\n\n /**\n * Build the `initialize` result advertising name / version /\n * capabilities, negotiating the protocol version: the client's requested\n * version when supported, otherwise the newest this server speaks.\n */\n private initializeResult(params: unknown): Record<string, unknown> {\n const requested = readRecord(params).protocolVersion;\n const protocolVersion =\n typeof requested === \"string\" && SUPPORTED_PROTOCOL_VERSIONS.includes(requested)\n ? requested\n : LATEST_PROTOCOL_VERSION;\n\n const capabilities: Record<string, unknown> = { tools: {} };\n\n if (this.resources) {\n capabilities.resources = {};\n }\n\n if (this.prompts) {\n capabilities.prompts = {};\n }\n\n return {\n protocolVersion,\n capabilities,\n serverInfo: {\n name: this.options.name,\n version: this.options.version ?? DEFAULT_VERSION,\n },\n };\n }\n\n /**\n * Build the `tools/list` payload: one {@link McpToolDescriptor} per\n * exposed tool, its `inputSchema` extracted via `extractJsonSchema` at\n * the configured dialect (default `draft-2020-12` — overriding\n * `extractJsonSchema`'s own `openai-strict` default to a neutral MCP draft).\n */\n private listTools(): McpToolDescriptor[] {\n const target = this.options.schemaTarget ?? DEFAULT_SCHEMA_TARGET;\n\n return this.tools.map(({ name, contract }) => {\n const inputSchema = extractJsonSchema(contract.input, { target }) ?? {\n type: \"object\",\n properties: {},\n };\n\n return {\n name,\n description: contract.description,\n inputSchema,\n ...(isRecord(contract.meta?.outputSchema)\n ? { outputSchema: contract.meta.outputSchema }\n : {}),\n };\n });\n }\n\n /**\n * Route a `tools/call` to the named contract's `invoke()` and map the\n * never-throwing {@link import(\"@warlock.js/ai\").ToolInvokeResult}: `data`\n * → a text content block (or an opted-in native result), `error` → an\n * `isError: true` result. An unknown tool name throws (mapped to a\n * JSON-RPC error by {@link handle}).\n *\n * The contract receives a `ToolContext` carrying the abort signal and,\n * over HTTP, the inbound request's headers on `ctx.request`.\n */\n private async callTool(\n params: unknown,\n context: McpServeToolContext,\n ): Promise<McpToolCallResult> {\n const { name, args } = readCallParams(params);\n const contract = this.byName.get(name);\n\n if (!contract) {\n throw new McpTransportError(`Unknown tool \"${name}\".`, {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const toolContext: ToolContext = {\n artifacts: {},\n requestId: context.requestId,\n principal: context.principal,\n };\n\n if (context.signal) {\n toolContext.signal = context.signal;\n }\n\n if (context.request) {\n const { headers, remoteAddress } = context.request;\n\n toolContext.request = remoteAddress === undefined ? { headers } : { headers, remoteAddress };\n }\n\n if (this.options.beforeCall) {\n let input = args;\n\n // Validate at the policy boundary even for custom ToolContracts. Keep\n // invoke as the execution boundary so custom accounting/error handling\n // remains intact; invoke still owns its own input validation.\n if (contract.input) {\n let validation: StandardSchemaV1.Result<unknown>;\n\n try {\n validation = await contract.input[\"~standard\"].validate(args);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n return {\n content: [\n { type: \"text\", text: `Schema validation threw for tool \"${name}\": ${message}` },\n ],\n isError: true,\n };\n }\n\n if (validation.issues) {\n return {\n content: [\n {\n type: \"text\",\n text: `Validation failed: ${validation.issues.map((issue) => issue.message).join(\"; \")}`,\n },\n ],\n isError: true,\n };\n }\n\n input = validation.value;\n }\n\n context.signal?.throwIfAborted();\n const refusal = await this.options.beforeCall(context, contract, input);\n\n if (refusal !== undefined) {\n try {\n assertMcpResult(refusal);\n\n if (refusal.isError !== true) {\n throw new Error(\"A refusal must be an error result.\");\n }\n } catch {\n throw new RpcFailure(JSON_RPC_INTERNAL_ERROR, \"Invalid MCP beforeCall refusal.\");\n }\n\n return refusal;\n }\n }\n\n context.signal?.throwIfAborted();\n const result = await contract.invoke(args, toolContext);\n\n if (result.error) {\n return {\n content: [{ type: \"text\", text: result.error.message }],\n isError: true,\n };\n }\n\n const mode = contract.meta?.mcpResults ?? this.options.results ?? \"text\";\n const branded = isMcpResult(result.data);\n\n if (branded || (mode === \"native\" && looksLikeMcpResult(result.data))) {\n const native = isMcpResult(result.data) ? unwrapMcpResult(result.data) : result.data;\n\n try {\n assertMcpResult(native);\n } catch {\n throw new RpcFailure(JSON_RPC_INTERNAL_ERROR, \"Invalid native MCP CallToolResult.\");\n }\n\n return native;\n }\n\n return {\n content: [{ type: \"text\", text: serializeData(result.data) }],\n isError: false,\n };\n }\n\n /** Build the `resources/list` payload (metadata only, no contents). */\n private listResources(resources: Map<string, McpServeResource>): Record<string, unknown>[] {\n return [...resources.values()].map((resource) =>\n compact({\n uri: resource.uri,\n name: resource.name,\n title: resource.title,\n description: resource.description,\n mimeType: resource.mimeType,\n }),\n );\n }\n\n /**\n * Answer `resources/read`: run the resource's `read()` and wrap the\n * result as one text or base64 blob content entry.\n */\n private async readResource(\n resources: Map<string, McpServeResource>,\n params: unknown,\n context: McpServeCallContext,\n ): Promise<Record<string, unknown>> {\n const uri = readRecord(params).uri;\n\n if (typeof uri !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, \"resources/read requires a string `uri`.\");\n }\n\n const resource = resources.get(uri);\n\n if (!resource) {\n throw new RpcFailure(MCP_RESOURCE_NOT_FOUND, \"Resource not found\", { uri });\n }\n\n const body = await resource.read(context);\n const entry =\n typeof body === \"string\"\n ? { uri, mimeType: resource.mimeType, text: body }\n : { uri, mimeType: resource.mimeType, blob: Buffer.from(body).toString(\"base64\") };\n\n return { contents: [compact(entry)] };\n }\n\n /** Build the `prompts/list` payload. */\n private listPrompts(prompts: Map<string, McpServePrompt>): Record<string, unknown>[] {\n return [...prompts.values()].map((prompt) =>\n compact({\n name: prompt.name,\n title: prompt.title,\n description: prompt.description,\n arguments: prompt.arguments,\n }),\n );\n }\n\n /**\n * Answer `prompts/get`: validate the arguments (strings only; required\n * ones present) and build the messages. An unknown prompt or a missing\n * required argument answers invalid-params, per the MCP prompts spec.\n */\n private async getPrompt(\n prompts: Map<string, McpServePrompt>,\n params: unknown,\n context: McpServeCallContext,\n ): Promise<Record<string, unknown>> {\n const record = readRecord(params);\n const name = record.name;\n\n if (typeof name !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, \"prompts/get requires a string `name`.\");\n }\n\n const prompt = prompts.get(name);\n\n if (!prompt) {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, `Unknown prompt \"${name}\".`);\n }\n\n const args: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(readRecord(record.arguments))) {\n if (typeof value !== \"string\") {\n throw new RpcFailure(JSON_RPC_INVALID_PARAMS, `Prompt argument \"${key}\" must be a string.`);\n }\n\n args[key] = value;\n }\n\n for (const argument of prompt.arguments ?? []) {\n if (argument.required && args[argument.name] === undefined) {\n throw new RpcFailure(\n JSON_RPC_INVALID_PARAMS,\n `Prompt \"${name}\" requires the \"${argument.name}\" argument.`,\n );\n }\n }\n\n const built = await prompt.get(args, context);\n const messages: McpPromptMessage[] =\n typeof built === \"string\"\n ? [{ role: \"user\", content: { type: \"text\", text: built } }]\n : built;\n\n return compact({ description: prompt.description, messages });\n }\n\n /** Build a JSON-RPC success response. */\n private ok(id: JsonRpcRequest[\"id\"], result: unknown): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, result };\n }\n\n /** Build a JSON-RPC error response. */\n private error(\n id: JsonRpcRequest[\"id\"],\n code: number,\n message: string,\n data?: unknown,\n ): JsonRpcResponse {\n const error = data === undefined ? { code, message } : { code, message, data };\n\n return { jsonrpc: JSONRPC_VERSION, id, error };\n }\n}\n\n/** JSON-RPC \"Invalid Request\" — the line is JSON but not a request object. */\nconst JSON_RPC_INVALID_REQUEST = -32600;\n\n/** JSON-RPC \"Internal error\" — answering the request failed unexpectedly. */\nconst JSON_RPC_INTERNAL_ERROR = -32603;\n\n/**\n * Turn one stdio line into the line to write back, or `undefined` when\n * nothing is answered: blank or non-JSON lines and notifications (no `id`).\n * A JSON value that is not a request object (`null`, a number, an array) is\n * answered with a JSON-RPC \"Invalid Request\" error whose `id` is `null`.\n * Never rejects: a dispatch failure is answered as an internal error.\n */\nexport async function answerStdioLine(\n line: string,\n dispatch: (request: JsonRpcRequest) => Promise<JsonRpcResponse | undefined>,\n notify?: (message: unknown) => void,\n): Promise<string | undefined> {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return undefined;\n }\n\n let message: unknown;\n\n try {\n message = JSON.parse(trimmed);\n } catch {\n return undefined;\n }\n\n if (typeof message !== \"object\" || message === null || Array.isArray(message)) {\n const invalid = {\n jsonrpc: JSONRPC_VERSION,\n id: null,\n error: { code: JSON_RPC_INVALID_REQUEST, message: \"Invalid request: not a JSON-RPC object.\" },\n };\n\n return `${JSON.stringify(invalid)}\\n`;\n }\n\n const request = message as JsonRpcRequest;\n const id: unknown = request.id;\n\n if (id === undefined || id === null) {\n notify?.(message);\n\n return undefined;\n }\n\n if (typeof id !== \"string\" && typeof id !== \"number\") {\n return `${JSON.stringify({\n jsonrpc: JSONRPC_VERSION,\n id: null,\n error: {\n code: JSON_RPC_INVALID_REQUEST,\n message: \"Invalid request: id must be a string or number.\",\n },\n })}\\n`;\n }\n\n try {\n const response = await dispatch(request);\n\n return response === undefined ? undefined : `${JSON.stringify(response)}\\n`;\n } catch {\n const failed = {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: { code: JSON_RPC_INTERNAL_ERROR, message: \"Internal error.\" },\n };\n\n return `${JSON.stringify(failed)}\\n`;\n }\n}\n\n/** Read an unknown value as a plain record (non-objects read as empty). */\nfunction readRecord(value: unknown): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return {};\n }\n\n return value as Record<string, unknown>;\n}\n\n/** Drop `undefined` members so optional wire fields are omitted, not `null`. */\nfunction compact(value: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, member] of Object.entries(value)) {\n if (member !== undefined) {\n result[key] = member;\n }\n }\n\n return result;\n}\n\n/**\n * Read and validate the `tools/call` params into `{ name, args }`. Throws\n * a typed {@link McpTransportError} when `name` is missing — mapped to a\n * JSON-RPC `invalid params` error by the handler.\n */\nfunction readCallParams(params: unknown): { name: string; args: unknown } {\n if (typeof params !== \"object\" || params === null) {\n throw new McpTransportError(\"tools/call params must be an object.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const record = params as { name?: unknown; arguments?: unknown };\n\n if (typeof record.name !== \"string\") {\n throw new McpTransportError(\"tools/call requires a string `name`.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n return { name: record.name, args: record.arguments ?? {} };\n}\n\n/**\n * Serialize a tool's `data` for an MCP text content block — a string is\n * passed verbatim, everything else is JSON-stringified so structured\n * output crosses the wire as text the consuming client can re-parse.\n */\nfunction serializeData(data: unknown): string {\n if (typeof data === \"string\") {\n return data;\n }\n\n if (data === undefined) {\n return \"\";\n }\n\n return JSON.stringify(data);\n}\n\n/**\n * The pure protocol handler: one JSON-RPC request in, one response out.\n */\nexport interface McpServeRequestHandler {\n /**\n * Map one request to its response.\n *\n * @param request - The JSON-RPC request.\n * @param context - Optional per-call context (inbound HTTP request, abort signal).\n */\n handle(request: JsonRpcRequest, context?: McpServeCallContext): Promise<JsonRpcResponse>;\n}\n\n/**\n * Build the pure protocol handler for a serve source. Exposed (alongside\n * {@link serve}) so callers and tests can drive the MCP protocol without an\n * actual transport — feed it a JSON-RPC request, get the response.\n *\n * Throws {@link McpServeError} on duplicate tool names / resource URIs /\n * prompt names, or `prefixToolNames` on an ungrouped source.\n *\n * @param source - The tools to expose (one tool set or `{ groups }`).\n * @param options - Serve options (name / version / schema target / resources / prompts).\n * @returns An object whose `handle(request, context?)` maps a request to a response.\n */\nexport function createServeHandler(\n source: McpServeSource,\n options: McpServeOptions,\n): McpServeRequestHandler {\n return new McpServeHandler(source, options);\n}\n\n/**\n * The internal {@link McpServer} — owns a {@link McpServeHandler} and a\n * transport pump. For `stdio` it reads newline-delimited JSON-RPC requests\n * from `process.stdin` and writes responses to `process.stdout`; for\n * `http` it binds a Streamable HTTP endpoint (see `./serve-http`).\n *\n * Constructed via {@link serve}; the class itself is internal.\n */\nclass McpServerImpl implements McpServer {\n /** The pure protocol handler. */\n private readonly handler: McpServeHandler;\n\n /** Serve options (transport selection lives here). */\n private readonly options: McpServeOptions;\n\n /** The running handle (stdio or http), while started or starting. */\n private running: Promise<McpServeHandle> | undefined;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.handler = new McpServeHandler(source, options);\n this.options = options;\n }\n\n public start(): Promise<McpServeHandle> {\n if (!this.running) {\n const transport = this.options.transport ?? { type: \"stdio\" };\n const starting =\n transport.type === \"http\"\n ? this.startHttp(transport)\n : Promise.resolve(this.startStdio(transport));\n\n // A failed start leaves the server startable again.\n this.running = starting.catch((error: unknown) => {\n this.running = undefined;\n throw error;\n });\n }\n\n return this.running;\n }\n\n /** Bind the HTTP endpoint; its `close()` also makes this server startable again. */\n private async startHttp(transport: McpHttpServeTransport): Promise<McpHttpServeHandle> {\n const http = await startHttpServer(transport, (request, context) =>\n this.handler.handle(request, context),\n );\n\n return {\n url: http.url,\n close: async () => {\n await http.close();\n this.running = undefined;\n },\n };\n }\n\n /** Wire the stdin line reader and answer each request on stdout. */\n private startStdio(transport: McpStdioServeTransport): McpServeHandle {\n const shutdown = new AbortController();\n const requests = new McpServeRequests();\n const reader: Interface = createInterface({ input: process.stdin });\n reader.once(\"close\", () => shutdown.abort());\n\n reader.on(\"line\", (line) => {\n // A failure answering one line must never become an unhandled\n // rejection: that would take the whole server process down.\n void this.onLine(line, transport, requests, shutdown.signal).catch(() => {});\n });\n\n return {\n close: async () => {\n shutdown.abort();\n reader.close();\n this.running = undefined;\n },\n };\n }\n\n /** Answer one stdin line on stdout (see {@link answerStdioLine}). */\n private async onLine(\n line: string,\n transport: McpStdioServeTransport,\n requests: McpServeRequests,\n signal: AbortSignal,\n ): Promise<void> {\n const reply = await answerStdioLine(\n line,\n (request) =>\n requests.run(request, { signal }, async (context) => {\n const principal = transport.principalProvider\n ? await transport.principalProvider(context)\n : transport.principal;\n\n return this.handler.handle(request, { ...context, principal });\n }),\n (message) => requests.cancel(message),\n );\n\n if (reply !== undefined) {\n process.stdout.write(reply);\n }\n }\n\n public async stop(): Promise<void> {\n const running = this.running;\n\n if (!running) {\n return;\n }\n\n let handle: McpServeHandle;\n\n try {\n handle = await running;\n } catch {\n // A start that failed has nothing to close.\n return;\n }\n\n await handle.close();\n }\n}\n\n/**\n * Expose a built agent / supervisor / orchestrator (or a raw\n * `ToolContract[]`, or several named groups) AS an MCP server (Direction B:\n * local primitive → MCP server other clients consume).\n *\n * Enumerates the tools once, here. Every exposed name must be unique — a\n * duplicate throws {@link McpServeError} (`type: \"collision\"`) right away;\n * serve `{ groups: { … } }` with `prefixToolNames: true` to namespace\n * same-named tools from different sets. `tools/list` answers with each\n * tool's `inputSchema` extracted via `extractJsonSchema` at the configured\n * `schemaTarget` (default `draft-2020-12`). `tools/call` routes to the\n * contract's `invoke()` and maps the never-throwing result — `data`\n * becomes a text content block, `error` an `isError: true` result.\n *\n * Transports: `stdio` (default) pumps `process.stdin` / `process.stdout`;\n * `http` binds an MCP Streamable HTTP endpoint on `127.0.0.1` by default,\n * with bearer / custom auth (required off loopback), `Origin` and `Host`\n * validation against DNS rebinding, a body-size cap, and a draining\n * `close()`. `start()` resolves `{ url, close }`.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, schema dialect, resources, prompts.\n * @returns An {@link McpServer} with `start()` / `stop()`.\n *\n * @example\n * const { url, close } = await serve([calc, clock], {\n * name: \"session-tools\",\n * transport: { type: \"http\", auth: { bearer: sessionToken } },\n * }).start();\n */\nexport function serve(\n source: McpServeSource,\n options: McpServeOptions & { transport: McpHttpServeTransport },\n): McpServer<McpHttpServeHandle>;\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer;\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer {\n return new McpServerImpl(source, options);\n}\n"],"mappings":"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"}
|
package/esm/mcp/transport.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpTransport } from "../contracts/mcp.type.mjs";
|
|
2
|
+
import "../contracts/index.mjs";
|
|
2
3
|
import { McpTransportClient } from "./transport.type.mjs";
|
|
3
|
-
|
|
4
4
|
//#region ../../packages/ai-tools/src/mcp/transport.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Build the concrete {@link McpTransportClient} for an {@link McpTransport}
|
|
@@ -11,7 +11,7 @@ import { McpTransportClient } from "./transport.type.mjs";
|
|
|
11
11
|
* @param transport - The transport config (discriminated by `type`).
|
|
12
12
|
* @returns A transport client paired with its id allocator.
|
|
13
13
|
*/
|
|
14
|
-
declare function createTransport(transport: McpTransport): McpTransportClient & {
|
|
14
|
+
export declare function createTransport(transport: McpTransport): McpTransportClient & {
|
|
15
15
|
allocateId(): number;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
@@ -19,7 +19,7 @@ declare function createTransport(transport: McpTransport): McpTransportClient &
|
|
|
19
19
|
* transport client (tests inject a fake) or an {@link McpTransport} config
|
|
20
20
|
* to spawn/connect a real one.
|
|
21
21
|
*/
|
|
22
|
-
interface JsonRpcClientHandle {
|
|
22
|
+
export interface JsonRpcClientHandle {
|
|
23
23
|
/** Issue a request and resolve with its `result` (throws on error). */
|
|
24
24
|
call<TResult = unknown>(method: string, params?: unknown, options?: {
|
|
25
25
|
signal?: AbortSignal;
|
|
@@ -42,7 +42,6 @@ interface JsonRpcClientHandle {
|
|
|
42
42
|
* @param source - A transport client or an `McpTransport` config.
|
|
43
43
|
* @returns A JSON-RPC client handle.
|
|
44
44
|
*/
|
|
45
|
-
declare function createJsonRpcClient(source: McpTransport | McpTransportClient): JsonRpcClientHandle;
|
|
45
|
+
export declare function createJsonRpcClient(source: McpTransport | McpTransportClient): JsonRpcClientHandle;
|
|
46
46
|
//#endregion
|
|
47
|
-
export { JsonRpcClientHandle, createJsonRpcClient, createTransport };
|
|
48
47
|
//# sourceMappingURL=transport.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"transport.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.ts"],"mappings":";;;;;;;;;;;;;wBAgegB,gBACd,WAAW,eACV;EAAuB;;;;;;;iBAmFT;;EAEf,KAAK,mBACH,gBACA,kBACA;IAAY,SAAS;IAAa;MACjC,QAAQ;;EAEX,OAAO,gBAAgB,mBAAmB;;EAE1C,SAAS;;;;;;;;;;;;;;wBAeK,oBACd,QAAQ,eAAe,qBACtB"}
|
package/esm/mcp/transport.mjs
CHANGED
|
@@ -65,8 +65,10 @@ function armCall(reject, method, options, onSettle) {
|
|
|
65
65
|
}));
|
|
66
66
|
};
|
|
67
67
|
const signal = options?.signal;
|
|
68
|
-
if (signal)
|
|
69
|
-
|
|
68
|
+
if (signal) {
|
|
69
|
+
if (signal.aborted) queueMicrotask(onAbort);
|
|
70
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
71
|
+
}
|
|
70
72
|
function cleanup() {
|
|
71
73
|
clearTimeout(timer);
|
|
72
74
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
@@ -219,8 +221,10 @@ var HttpTransport = class {
|
|
|
219
221
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
220
222
|
const onAbort = () => controller.abort();
|
|
221
223
|
const signal = options?.signal;
|
|
222
|
-
if (signal)
|
|
223
|
-
|
|
224
|
+
if (signal) {
|
|
225
|
+
if (signal.aborted) controller.abort();
|
|
226
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
227
|
+
}
|
|
224
228
|
let response;
|
|
225
229
|
try {
|
|
226
230
|
response = await fetch(this.url, {
|
|
@@ -285,7 +289,8 @@ var HttpTransport = class {
|
|
|
285
289
|
body: JSON.stringify(notification)
|
|
286
290
|
});
|
|
287
291
|
} catch (cause) {
|
|
288
|
-
|
|
292
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
293
|
+
throw new McpTransportError(`MCP http transport notification "${method}" failed: ${message}`, {
|
|
289
294
|
type: "connect",
|
|
290
295
|
method,
|
|
291
296
|
cause
|
|
@@ -388,7 +393,8 @@ var JsonRpcClient = class {
|
|
|
388
393
|
* @returns A JSON-RPC client handle.
|
|
389
394
|
*/
|
|
390
395
|
function createJsonRpcClient(source) {
|
|
391
|
-
|
|
396
|
+
const transport = isTransportConfig(source) ? createTransport(source) : withIdAllocator(source);
|
|
397
|
+
return new JsonRpcClient(transport);
|
|
392
398
|
}
|
|
393
399
|
/**
|
|
394
400
|
* Distinguish an {@link McpTransport} config (a plain object with a `type`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.ts"],"sourcesContent":["import { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { JsonRpcId, JsonRpcRequest, JsonRpcResponse, McpTransport } from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-request wait before a transport call is abandoned. */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\n\n/** The JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/**\n * The only host environment entries a stdio MCP server inherits. This keeps\n * executable lookup and platform runtime behaviour intact without exposing\n * application secrets or arbitrary parent-process configuration to children.\n */\nconst STDIO_ENV_ALLOWLIST = [\n \"PATH\",\n \"Path\",\n \"HOME\",\n \"USERPROFILE\",\n \"HOMEDRIVE\",\n \"HOMEPATH\",\n \"SystemRoot\",\n \"SYSTEMROOT\",\n \"WINDIR\",\n \"ComSpec\",\n \"COMSPEC\",\n \"PATHEXT\",\n] as const;\n\n/**\n * Build the environment passed to a child MCP stdio server. Never forward\n * `process.env` wholesale: only the cross-platform execution essentials are\n * copied, and application-supplied server variables take precedence.\n */\nfunction createStdioEnvironment(\n serverEnvironment?: Record<string, string>,\n): Record<string, string> {\n const environment: Record<string, string> = {};\n\n for (const key of STDIO_ENV_ALLOWLIST) {\n const value = process.env[key];\n\n if (value !== undefined) {\n environment[key] = value;\n }\n }\n\n return { ...environment, ...serverEnvironment };\n}\n\n/**\n * A pending in-flight request awaiting its correlated response, keyed by\n * the JSON-RPC `id`. The stdio transport multiplexes many requests over\n * one line-framed pipe, so each resolve/reject is parked here until the\n * line whose `id` matches arrives.\n */\ninterface PendingCall {\n resolve(response: JsonRpcResponse): void;\n reject(error: McpTransportError): void;\n /** Clears the per-call timeout + abort wiring when the call settles. */\n cleanup(): void;\n}\n\n/**\n * Wire a per-call timeout and an optional caller `AbortSignal` onto a\n * pending request, returning a `cleanup()` that tears both down. The\n * `onSettle` callback removes the pending entry from whatever registry the\n * transport keeps so a late response can't double-settle.\n */\nfunction armCall(\n reject: (error: McpTransportError) => void,\n method: string,\n options: { signal?: AbortSignal; timeoutMs?: number } | undefined,\n onSettle: () => void,\n): () => void {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n const timer = setTimeout(() => {\n onSettle();\n reject(\n new McpTransportError(`MCP request \"${method}\" timed out after ${timeoutMs}ms.`, {\n type: \"timeout\",\n method,\n }),\n );\n }, timeoutMs);\n\n const onAbort = () => {\n cleanup();\n reject(\n new McpTransportError(`MCP request \"${method}\" was aborted.`, {\n type: \"closed\",\n method,\n }),\n );\n };\n\n const signal = options?.signal;\n\n if (signal) {\n if (signal.aborted) {\n // Defer so the caller has the rejection wired before it fires.\n queueMicrotask(onAbort);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n function cleanup(): void {\n clearTimeout(timer);\n\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n return cleanup;\n}\n\n/**\n * The stdio transport — spawns a child process and speaks JSON-RPC over\n * its stdin/stdout, one JSON object per line (newline-delimited framing).\n * Uses only Node built-ins (`node:child_process` + `node:readline`); no\n * dependency.\n *\n * Constructed via {@link createStdioTransport}; the class itself is\n * internal.\n */\nclass StdioTransport implements McpTransportClient {\n /** The spawned server process. */\n private readonly child: ChildProcessWithoutNullStreams;\n\n /** Line reader over the child's stdout — one JSON-RPC message per line. */\n private readonly reader: Interface;\n\n /** In-flight requests awaiting a correlated response, keyed by id. */\n private readonly pending = new Map<JsonRpcId, PendingCall>();\n\n /** Monotonic id source for outbound requests. */\n private nextId = 1;\n\n /** Flipped once {@link close} runs (or the child exits) so reuse rejects. */\n private closed = false;\n\n public constructor(transport: Extract<McpTransport, { type: \"stdio\" }>) {\n let child: ChildProcessWithoutNullStreams;\n\n try {\n child = spawn(transport.command, transport.args ?? [], {\n // process.env is NOT inherited wholesale: only the\n // STDIO_ENV_ALLOWLIST keys are copied, then the caller's explicit\n // `transport.env` entries are layered on top.\n env: createStdioEnvironment(transport.env),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }) as ChildProcessWithoutNullStreams;\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n `MCP stdio transport could not spawn \"${transport.command}\": ${message}`,\n { type: \"connect\", cause },\n );\n }\n\n this.child = child;\n this.reader = createInterface({ input: child.stdout });\n\n this.reader.on(\"line\", (line) => this.onLine(line));\n\n // A child that dies takes every in-flight (and future) call with it.\n child.on(\"exit\", (code) => this.failAll(\"connect\", `child exited with code ${code ?? \"null\"}`));\n child.on(\"error\", (error) => this.failAll(\"connect\", error.message));\n }\n\n /**\n * Parse one stdout line and route it to its pending request. Non-JSON\n * lines (a server logging to stdout) and messages with no matching `id`\n * (notifications, stray responses) are ignored — robustness over strictness.\n */\n private onLine(line: string): void {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return;\n }\n\n let message: JsonRpcResponse;\n\n try {\n message = JSON.parse(trimmed) as JsonRpcResponse;\n } catch {\n // Not a JSON-RPC line (server diagnostics on stdout) — ignore.\n return;\n }\n\n if (message.id === undefined || message.id === null) {\n // A notification or a malformed response — nothing to correlate.\n return;\n }\n\n const call = this.pending.get(message.id);\n\n if (!call) {\n return;\n }\n\n this.pending.delete(message.id);\n call.cleanup();\n call.resolve(message);\n }\n\n /**\n * Reject every pending call (and mark the transport unusable) when the\n * child dies or errors — so a hung server can never leave a caller\n * waiting forever.\n */\n private failAll(type: \"connect\" | \"closed\", reason: string): void {\n this.closed = true;\n\n for (const [id, call] of this.pending) {\n this.pending.delete(id);\n call.cleanup();\n call.reject(new McpTransportError(`MCP stdio transport failed: ${reason}.`, { type }));\n }\n }\n\n public request<TResult = unknown>(\n request: JsonRpcRequest,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<JsonRpcResponse<TResult>> {\n if (this.closed) {\n return Promise.reject(\n new McpTransportError(\"MCP stdio transport is closed.\", {\n type: \"closed\",\n method: request.method,\n }),\n );\n }\n\n const id = request.id;\n\n return new Promise<JsonRpcResponse<TResult>>((resolve, reject) => {\n const cleanup = armCall(reject, request.method, options, () => this.pending.delete(id));\n\n this.pending.set(id, {\n resolve: (response) => resolve(response as JsonRpcResponse<TResult>),\n reject,\n cleanup,\n });\n\n try {\n this.child.stdin.write(`${JSON.stringify(request)}\\n`);\n } catch (cause) {\n this.pending.delete(id);\n cleanup();\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n reject(\n new McpTransportError(\n `MCP stdio transport failed to write request \"${request.method}\": ${message}`,\n { type: \"closed\", method: request.method, cause },\n ),\n );\n }\n });\n }\n\n public async notify(method: string, params?: unknown): Promise<void> {\n if (this.closed) {\n throw new McpTransportError(\"MCP stdio transport is closed.\", {\n type: \"closed\",\n method,\n });\n }\n\n const notification = { jsonrpc: JSONRPC_VERSION, method, params };\n this.child.stdin.write(`${JSON.stringify(notification)}\\n`);\n }\n\n /** Allocate the next outbound request id. */\n public allocateId(): number {\n return this.nextId++;\n }\n\n public async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.reader.close();\n this.failAll(\"closed\", \"transport closed by caller\");\n this.child.kill();\n }\n}\n\n/**\n * The Streamable HTTP transport — POSTs each JSON-RPC request to the\n * server endpoint over the global `fetch` (Node 18+) and reads the single\n * JSON response. No SSE-legacy, no WebSocket. Static `headers` (e.g. auth)\n * are sent with every request.\n *\n * Constructed via {@link createHttpTransport}; the class itself is internal.\n */\nclass HttpTransport implements McpTransportClient {\n /** The server endpoint POST target. */\n private readonly url: string;\n\n /** Static headers merged into every request (auth, etc.). */\n private readonly headers: Record<string, string>;\n\n /** Monotonic id source for outbound requests. */\n private nextId = 1;\n\n public constructor(transport: Extract<McpTransport, { type: \"http\" }>) {\n this.url = transport.url;\n this.headers = {\n \"content-type\": \"application/json\",\n accept: \"application/json, text/event-stream\",\n ...transport.headers,\n };\n }\n\n public async request<TResult = unknown>(\n request: JsonRpcRequest,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<JsonRpcResponse<TResult>> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const onAbort = () => controller.abort();\n const signal = options?.signal;\n\n if (signal) {\n if (signal.aborted) {\n controller.abort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n let response: Response;\n\n try {\n response = await fetch(this.url, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(request),\n signal: controller.signal,\n });\n } catch (cause) {\n const aborted = controller.signal.aborted;\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n aborted\n ? `MCP request \"${request.method}\" timed out or was aborted after ${timeoutMs}ms.`\n : `MCP http transport request \"${request.method}\" failed: ${message}`,\n { type: aborted ? \"timeout\" : \"connect\", method: request.method, cause },\n );\n } finally {\n clearTimeout(timer);\n\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n if (!response.ok) {\n throw new McpTransportError(\n `MCP http transport request \"${request.method}\" returned HTTP ${response.status}.`,\n { type: \"connect\", method: request.method, context: { status: response.status } },\n );\n }\n\n return this.parseBody<TResult>(response, request.method);\n }\n\n /**\n * Parse the HTTP response body into a JSON-RPC response. Streamable HTTP\n * may answer with either `application/json` (a single response object)\n * or `text/event-stream` (SSE frames); we read the body as text and\n * extract the first JSON object, supporting the common `data: {...}`\n * SSE line shape without a streaming parser.\n */\n private async parseBody<TResult>(\n response: Response,\n method: string,\n ): Promise<JsonRpcResponse<TResult>> {\n const raw = await response.text();\n const contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n\n const jsonText = contentType.includes(\"text/event-stream\") ? extractSseData(raw) : raw;\n\n if (!jsonText) {\n throw new McpTransportError(`MCP http transport got an empty response for \"${method}\".`, {\n type: \"protocol\",\n method,\n });\n }\n\n try {\n return JSON.parse(jsonText) as JsonRpcResponse<TResult>;\n } catch (cause) {\n throw new McpTransportError(`MCP http transport got a non-JSON response for \"${method}\".`, {\n type: \"protocol\",\n method,\n cause,\n });\n }\n }\n\n public async notify(method: string, params?: unknown): Promise<void> {\n const notification = { jsonrpc: JSONRPC_VERSION, method, params };\n\n // A notification expects no response; fire-and-forget but surface a\n // connect failure so a dead endpoint is not silently ignored.\n try {\n await fetch(this.url, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(notification),\n });\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n `MCP http transport notification \"${method}\" failed: ${message}`,\n { type: \"connect\", method, cause },\n );\n }\n }\n\n /** Allocate the next outbound request id. */\n public allocateId(): number {\n return this.nextId++;\n }\n\n public async close(): Promise<void> {\n // Streamable HTTP is stateless per request — nothing persistent to\n // release.\n }\n}\n\n/**\n * Pull the first `data:` JSON payload out of an SSE response body. MCP's\n * Streamable HTTP transport answers a single request with one SSE frame\n * carrying the JSON-RPC response; we take the first non-empty `data:`\n * line. Returns an empty string when none is found.\n */\nfunction extractSseData(body: string): string {\n for (const line of body.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n\n if (trimmed.startsWith(\"data:\")) {\n const payload = trimmed.slice(\"data:\".length).trim();\n\n if (payload && payload !== \"[DONE]\") {\n return payload;\n }\n }\n }\n\n return \"\";\n}\n\n/**\n * Build the concrete {@link McpTransportClient} for an {@link McpTransport}\n * config — a {@link StdioTransport} for `type: \"stdio\"`, an\n * {@link HttpTransport} for `type: \"http\"`. The returned client also\n * carries an `allocateId()` for the JSON-RPC client to mint request ids.\n *\n * @param transport - The transport config (discriminated by `type`).\n * @returns A transport client paired with its id allocator.\n */\nexport function createTransport(\n transport: McpTransport,\n): McpTransportClient & { allocateId(): number } {\n if (transport.type === \"stdio\") {\n return new StdioTransport(transport);\n }\n\n return new HttpTransport(transport);\n}\n\n/**\n * A minimal JSON-RPC 2.0 request/response client over any\n * {@link McpTransportClient}. Mints monotonic ids, frames the\n * `{ jsonrpc, id, method, params }` envelope, and unwraps the response —\n * translating a JSON-RPC `error` member into a typed\n * {@link McpTransportError} so callers branch on `error.type` rather than\n * parsing the wire.\n *\n * Constructed via {@link createJsonRpcClient}; the class itself is internal.\n */\nclass JsonRpcClient {\n /** The underlying framing transport. */\n private readonly transport: McpTransportClient & { allocateId(): number };\n\n public constructor(transport: McpTransportClient & { allocateId(): number }) {\n this.transport = transport;\n }\n\n /**\n * Issue a JSON-RPC `method` call and resolve with its `result`,\n * throwing a typed {@link McpTransportError} on a JSON-RPC error member\n * or a malformed response (neither `result` nor `error`).\n */\n public async call<TResult = unknown>(\n method: string,\n params?: unknown,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<TResult> {\n const request: JsonRpcRequest = {\n jsonrpc: JSONRPC_VERSION,\n id: this.transport.allocateId(),\n method,\n params,\n };\n\n const response = await this.transport.request<TResult>(request, options);\n\n if (response.error) {\n throw new McpTransportError(\n `MCP \"${method}\" failed: ${response.error.message} (code ${response.error.code}).`,\n {\n type: \"protocol\",\n method,\n context: { code: response.error.code },\n cause: response.error.data,\n },\n );\n }\n\n if (response.result === undefined) {\n throw new McpTransportError(\n `MCP \"${method}\" returned a response with neither result nor error.`,\n { type: \"protocol\", method },\n );\n }\n\n return response.result;\n }\n\n /** Send a one-way JSON-RPC notification (no response awaited). */\n public notify(method: string, params?: unknown): Promise<void> {\n return this.transport.notify(method, params);\n }\n\n /** Close the underlying transport. */\n public close(): Promise<void> {\n return this.transport.close();\n }\n}\n\n/**\n * A JSON-RPC client over an MCP transport. Either pass an already-built\n * transport client (tests inject a fake) or an {@link McpTransport} config\n * to spawn/connect a real one.\n */\nexport interface JsonRpcClientHandle {\n /** Issue a request and resolve with its `result` (throws on error). */\n call<TResult = unknown>(\n method: string,\n params?: unknown,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<TResult>;\n /** Send a one-way notification. */\n notify(method: string, params?: unknown): Promise<void>;\n /** Close the underlying transport. */\n close(): Promise<void>;\n}\n\n/**\n * Build a {@link JsonRpcClientHandle} over a transport. Accepts either a\n * pre-built {@link McpTransportClient} (the test seam — inject a scripted\n * fake) or an {@link McpTransport} config, in which case the concrete\n * transport is constructed via {@link createTransport}.\n *\n * When a bare {@link McpTransportClient} (without an `allocateId`) is\n * injected, the client supplies its own monotonic id source.\n *\n * @param source - A transport client or an `McpTransport` config.\n * @returns A JSON-RPC client handle.\n */\nexport function createJsonRpcClient(\n source: McpTransport | McpTransportClient,\n): JsonRpcClientHandle {\n const transport: McpTransportClient & { allocateId(): number } = isTransportConfig(source)\n ? createTransport(source)\n : withIdAllocator(source);\n\n return new JsonRpcClient(transport);\n}\n\n/**\n * Distinguish an {@link McpTransport} config (a plain object with a `type`\n * discriminator and no `request` method) from a built\n * {@link McpTransportClient} (which exposes `request`).\n */\nfunction isTransportConfig(source: McpTransport | McpTransportClient): source is McpTransport {\n return typeof (source as McpTransportClient).request !== \"function\";\n}\n\n/**\n * Wrap an injected {@link McpTransportClient} that lacks its own\n * `allocateId` with a monotonic id source, so the JSON-RPC client can mint\n * request ids uniformly regardless of whether the transport was built here\n * or supplied by a test.\n */\nfunction withIdAllocator(\n client: McpTransportClient,\n): McpTransportClient & { allocateId(): number } {\n const candidate = client as McpTransportClient & { allocateId?(): number };\n\n if (typeof candidate.allocateId === \"function\") {\n return candidate as McpTransportClient & { allocateId(): number };\n }\n\n let nextId = 1;\n\n return Object.assign(client, { allocateId: () => nextId++ });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transport.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.ts"],"sourcesContent":["import { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { JsonRpcId, JsonRpcRequest, JsonRpcResponse, McpTransport } from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-request wait before a transport call is abandoned. */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\n\n/** The JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/**\n * The only host environment entries a stdio MCP server inherits. This keeps\n * executable lookup and platform runtime behaviour intact without exposing\n * application secrets or arbitrary parent-process configuration to children.\n */\nconst STDIO_ENV_ALLOWLIST = [\n \"PATH\",\n \"Path\",\n \"HOME\",\n \"USERPROFILE\",\n \"HOMEDRIVE\",\n \"HOMEPATH\",\n \"SystemRoot\",\n \"SYSTEMROOT\",\n \"WINDIR\",\n \"ComSpec\",\n \"COMSPEC\",\n \"PATHEXT\",\n] as const;\n\n/**\n * Build the environment passed to a child MCP stdio server. Never forward\n * `process.env` wholesale: only the cross-platform execution essentials are\n * copied, and application-supplied server variables take precedence.\n */\nfunction createStdioEnvironment(\n serverEnvironment?: Record<string, string>,\n): Record<string, string> {\n const environment: Record<string, string> = {};\n\n for (const key of STDIO_ENV_ALLOWLIST) {\n const value = process.env[key];\n\n if (value !== undefined) {\n environment[key] = value;\n }\n }\n\n return { ...environment, ...serverEnvironment };\n}\n\n/**\n * A pending in-flight request awaiting its correlated response, keyed by\n * the JSON-RPC `id`. The stdio transport multiplexes many requests over\n * one line-framed pipe, so each resolve/reject is parked here until the\n * line whose `id` matches arrives.\n */\ninterface PendingCall {\n resolve(response: JsonRpcResponse): void;\n reject(error: McpTransportError): void;\n /** Clears the per-call timeout + abort wiring when the call settles. */\n cleanup(): void;\n}\n\n/**\n * Wire a per-call timeout and an optional caller `AbortSignal` onto a\n * pending request, returning a `cleanup()` that tears both down. The\n * `onSettle` callback removes the pending entry from whatever registry the\n * transport keeps so a late response can't double-settle.\n */\nfunction armCall(\n reject: (error: McpTransportError) => void,\n method: string,\n options: { signal?: AbortSignal; timeoutMs?: number } | undefined,\n onSettle: () => void,\n): () => void {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n\n const timer = setTimeout(() => {\n onSettle();\n reject(\n new McpTransportError(`MCP request \"${method}\" timed out after ${timeoutMs}ms.`, {\n type: \"timeout\",\n method,\n }),\n );\n }, timeoutMs);\n\n const onAbort = () => {\n cleanup();\n reject(\n new McpTransportError(`MCP request \"${method}\" was aborted.`, {\n type: \"closed\",\n method,\n }),\n );\n };\n\n const signal = options?.signal;\n\n if (signal) {\n if (signal.aborted) {\n // Defer so the caller has the rejection wired before it fires.\n queueMicrotask(onAbort);\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n function cleanup(): void {\n clearTimeout(timer);\n\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n return cleanup;\n}\n\n/**\n * The stdio transport — spawns a child process and speaks JSON-RPC over\n * its stdin/stdout, one JSON object per line (newline-delimited framing).\n * Uses only Node built-ins (`node:child_process` + `node:readline`); no\n * dependency.\n *\n * Constructed via {@link createStdioTransport}; the class itself is\n * internal.\n */\nclass StdioTransport implements McpTransportClient {\n /** The spawned server process. */\n private readonly child: ChildProcessWithoutNullStreams;\n\n /** Line reader over the child's stdout — one JSON-RPC message per line. */\n private readonly reader: Interface;\n\n /** In-flight requests awaiting a correlated response, keyed by id. */\n private readonly pending = new Map<JsonRpcId, PendingCall>();\n\n /** Monotonic id source for outbound requests. */\n private nextId = 1;\n\n /** Flipped once {@link close} runs (or the child exits) so reuse rejects. */\n private closed = false;\n\n public constructor(transport: Extract<McpTransport, { type: \"stdio\" }>) {\n let child: ChildProcessWithoutNullStreams;\n\n try {\n child = spawn(transport.command, transport.args ?? [], {\n // process.env is NOT inherited wholesale: only the\n // STDIO_ENV_ALLOWLIST keys are copied, then the caller's explicit\n // `transport.env` entries are layered on top.\n env: createStdioEnvironment(transport.env),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }) as ChildProcessWithoutNullStreams;\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n `MCP stdio transport could not spawn \"${transport.command}\": ${message}`,\n { type: \"connect\", cause },\n );\n }\n\n this.child = child;\n this.reader = createInterface({ input: child.stdout });\n\n this.reader.on(\"line\", (line) => this.onLine(line));\n\n // A child that dies takes every in-flight (and future) call with it.\n child.on(\"exit\", (code) => this.failAll(\"connect\", `child exited with code ${code ?? \"null\"}`));\n child.on(\"error\", (error) => this.failAll(\"connect\", error.message));\n }\n\n /**\n * Parse one stdout line and route it to its pending request. Non-JSON\n * lines (a server logging to stdout) and messages with no matching `id`\n * (notifications, stray responses) are ignored — robustness over strictness.\n */\n private onLine(line: string): void {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return;\n }\n\n let message: JsonRpcResponse;\n\n try {\n message = JSON.parse(trimmed) as JsonRpcResponse;\n } catch {\n // Not a JSON-RPC line (server diagnostics on stdout) — ignore.\n return;\n }\n\n if (message.id === undefined || message.id === null) {\n // A notification or a malformed response — nothing to correlate.\n return;\n }\n\n const call = this.pending.get(message.id);\n\n if (!call) {\n return;\n }\n\n this.pending.delete(message.id);\n call.cleanup();\n call.resolve(message);\n }\n\n /**\n * Reject every pending call (and mark the transport unusable) when the\n * child dies or errors — so a hung server can never leave a caller\n * waiting forever.\n */\n private failAll(type: \"connect\" | \"closed\", reason: string): void {\n this.closed = true;\n\n for (const [id, call] of this.pending) {\n this.pending.delete(id);\n call.cleanup();\n call.reject(new McpTransportError(`MCP stdio transport failed: ${reason}.`, { type }));\n }\n }\n\n public request<TResult = unknown>(\n request: JsonRpcRequest,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<JsonRpcResponse<TResult>> {\n if (this.closed) {\n return Promise.reject(\n new McpTransportError(\"MCP stdio transport is closed.\", {\n type: \"closed\",\n method: request.method,\n }),\n );\n }\n\n const id = request.id;\n\n return new Promise<JsonRpcResponse<TResult>>((resolve, reject) => {\n const cleanup = armCall(reject, request.method, options, () => this.pending.delete(id));\n\n this.pending.set(id, {\n resolve: (response) => resolve(response as JsonRpcResponse<TResult>),\n reject,\n cleanup,\n });\n\n try {\n this.child.stdin.write(`${JSON.stringify(request)}\\n`);\n } catch (cause) {\n this.pending.delete(id);\n cleanup();\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n reject(\n new McpTransportError(\n `MCP stdio transport failed to write request \"${request.method}\": ${message}`,\n { type: \"closed\", method: request.method, cause },\n ),\n );\n }\n });\n }\n\n public async notify(method: string, params?: unknown): Promise<void> {\n if (this.closed) {\n throw new McpTransportError(\"MCP stdio transport is closed.\", {\n type: \"closed\",\n method,\n });\n }\n\n const notification = { jsonrpc: JSONRPC_VERSION, method, params };\n this.child.stdin.write(`${JSON.stringify(notification)}\\n`);\n }\n\n /** Allocate the next outbound request id. */\n public allocateId(): number {\n return this.nextId++;\n }\n\n public async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.reader.close();\n this.failAll(\"closed\", \"transport closed by caller\");\n this.child.kill();\n }\n}\n\n/**\n * The Streamable HTTP transport — POSTs each JSON-RPC request to the\n * server endpoint over the global `fetch` (Node 18+) and reads the single\n * JSON response. No SSE-legacy, no WebSocket. Static `headers` (e.g. auth)\n * are sent with every request.\n *\n * Constructed via {@link createHttpTransport}; the class itself is internal.\n */\nclass HttpTransport implements McpTransportClient {\n /** The server endpoint POST target. */\n private readonly url: string;\n\n /** Static headers merged into every request (auth, etc.). */\n private readonly headers: Record<string, string>;\n\n /** Monotonic id source for outbound requests. */\n private nextId = 1;\n\n public constructor(transport: Extract<McpTransport, { type: \"http\" }>) {\n this.url = transport.url;\n this.headers = {\n \"content-type\": \"application/json\",\n accept: \"application/json, text/event-stream\",\n ...transport.headers,\n };\n }\n\n public async request<TResult = unknown>(\n request: JsonRpcRequest,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<JsonRpcResponse<TResult>> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const onAbort = () => controller.abort();\n const signal = options?.signal;\n\n if (signal) {\n if (signal.aborted) {\n controller.abort();\n } else {\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n let response: Response;\n\n try {\n response = await fetch(this.url, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(request),\n signal: controller.signal,\n });\n } catch (cause) {\n const aborted = controller.signal.aborted;\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n aborted\n ? `MCP request \"${request.method}\" timed out or was aborted after ${timeoutMs}ms.`\n : `MCP http transport request \"${request.method}\" failed: ${message}`,\n { type: aborted ? \"timeout\" : \"connect\", method: request.method, cause },\n );\n } finally {\n clearTimeout(timer);\n\n if (signal) {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n if (!response.ok) {\n throw new McpTransportError(\n `MCP http transport request \"${request.method}\" returned HTTP ${response.status}.`,\n { type: \"connect\", method: request.method, context: { status: response.status } },\n );\n }\n\n return this.parseBody<TResult>(response, request.method);\n }\n\n /**\n * Parse the HTTP response body into a JSON-RPC response. Streamable HTTP\n * may answer with either `application/json` (a single response object)\n * or `text/event-stream` (SSE frames); we read the body as text and\n * extract the first JSON object, supporting the common `data: {...}`\n * SSE line shape without a streaming parser.\n */\n private async parseBody<TResult>(\n response: Response,\n method: string,\n ): Promise<JsonRpcResponse<TResult>> {\n const raw = await response.text();\n const contentType = response.headers.get(\"content-type\")?.toLowerCase() ?? \"\";\n\n const jsonText = contentType.includes(\"text/event-stream\") ? extractSseData(raw) : raw;\n\n if (!jsonText) {\n throw new McpTransportError(`MCP http transport got an empty response for \"${method}\".`, {\n type: \"protocol\",\n method,\n });\n }\n\n try {\n return JSON.parse(jsonText) as JsonRpcResponse<TResult>;\n } catch (cause) {\n throw new McpTransportError(`MCP http transport got a non-JSON response for \"${method}\".`, {\n type: \"protocol\",\n method,\n cause,\n });\n }\n }\n\n public async notify(method: string, params?: unknown): Promise<void> {\n const notification = { jsonrpc: JSONRPC_VERSION, method, params };\n\n // A notification expects no response; fire-and-forget but surface a\n // connect failure so a dead endpoint is not silently ignored.\n try {\n await fetch(this.url, {\n method: \"POST\",\n headers: this.headers,\n body: JSON.stringify(notification),\n });\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(\n `MCP http transport notification \"${method}\" failed: ${message}`,\n { type: \"connect\", method, cause },\n );\n }\n }\n\n /** Allocate the next outbound request id. */\n public allocateId(): number {\n return this.nextId++;\n }\n\n public async close(): Promise<void> {\n // Streamable HTTP is stateless per request — nothing persistent to\n // release.\n }\n}\n\n/**\n * Pull the first `data:` JSON payload out of an SSE response body. MCP's\n * Streamable HTTP transport answers a single request with one SSE frame\n * carrying the JSON-RPC response; we take the first non-empty `data:`\n * line. Returns an empty string when none is found.\n */\nfunction extractSseData(body: string): string {\n for (const line of body.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n\n if (trimmed.startsWith(\"data:\")) {\n const payload = trimmed.slice(\"data:\".length).trim();\n\n if (payload && payload !== \"[DONE]\") {\n return payload;\n }\n }\n }\n\n return \"\";\n}\n\n/**\n * Build the concrete {@link McpTransportClient} for an {@link McpTransport}\n * config — a {@link StdioTransport} for `type: \"stdio\"`, an\n * {@link HttpTransport} for `type: \"http\"`. The returned client also\n * carries an `allocateId()` for the JSON-RPC client to mint request ids.\n *\n * @param transport - The transport config (discriminated by `type`).\n * @returns A transport client paired with its id allocator.\n */\nexport function createTransport(\n transport: McpTransport,\n): McpTransportClient & { allocateId(): number } {\n if (transport.type === \"stdio\") {\n return new StdioTransport(transport);\n }\n\n return new HttpTransport(transport);\n}\n\n/**\n * A minimal JSON-RPC 2.0 request/response client over any\n * {@link McpTransportClient}. Mints monotonic ids, frames the\n * `{ jsonrpc, id, method, params }` envelope, and unwraps the response —\n * translating a JSON-RPC `error` member into a typed\n * {@link McpTransportError} so callers branch on `error.type` rather than\n * parsing the wire.\n *\n * Constructed via {@link createJsonRpcClient}; the class itself is internal.\n */\nclass JsonRpcClient {\n /** The underlying framing transport. */\n private readonly transport: McpTransportClient & { allocateId(): number };\n\n public constructor(transport: McpTransportClient & { allocateId(): number }) {\n this.transport = transport;\n }\n\n /**\n * Issue a JSON-RPC `method` call and resolve with its `result`,\n * throwing a typed {@link McpTransportError} on a JSON-RPC error member\n * or a malformed response (neither `result` nor `error`).\n */\n public async call<TResult = unknown>(\n method: string,\n params?: unknown,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<TResult> {\n const request: JsonRpcRequest = {\n jsonrpc: JSONRPC_VERSION,\n id: this.transport.allocateId(),\n method,\n params,\n };\n\n const response = await this.transport.request<TResult>(request, options);\n\n if (response.error) {\n throw new McpTransportError(\n `MCP \"${method}\" failed: ${response.error.message} (code ${response.error.code}).`,\n {\n type: \"protocol\",\n method,\n context: { code: response.error.code },\n cause: response.error.data,\n },\n );\n }\n\n if (response.result === undefined) {\n throw new McpTransportError(\n `MCP \"${method}\" returned a response with neither result nor error.`,\n { type: \"protocol\", method },\n );\n }\n\n return response.result;\n }\n\n /** Send a one-way JSON-RPC notification (no response awaited). */\n public notify(method: string, params?: unknown): Promise<void> {\n return this.transport.notify(method, params);\n }\n\n /** Close the underlying transport. */\n public close(): Promise<void> {\n return this.transport.close();\n }\n}\n\n/**\n * A JSON-RPC client over an MCP transport. Either pass an already-built\n * transport client (tests inject a fake) or an {@link McpTransport} config\n * to spawn/connect a real one.\n */\nexport interface JsonRpcClientHandle {\n /** Issue a request and resolve with its `result` (throws on error). */\n call<TResult = unknown>(\n method: string,\n params?: unknown,\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<TResult>;\n /** Send a one-way notification. */\n notify(method: string, params?: unknown): Promise<void>;\n /** Close the underlying transport. */\n close(): Promise<void>;\n}\n\n/**\n * Build a {@link JsonRpcClientHandle} over a transport. Accepts either a\n * pre-built {@link McpTransportClient} (the test seam — inject a scripted\n * fake) or an {@link McpTransport} config, in which case the concrete\n * transport is constructed via {@link createTransport}.\n *\n * When a bare {@link McpTransportClient} (without an `allocateId`) is\n * injected, the client supplies its own monotonic id source.\n *\n * @param source - A transport client or an `McpTransport` config.\n * @returns A JSON-RPC client handle.\n */\nexport function createJsonRpcClient(\n source: McpTransport | McpTransportClient,\n): JsonRpcClientHandle {\n const transport: McpTransportClient & { allocateId(): number } = isTransportConfig(source)\n ? createTransport(source)\n : withIdAllocator(source);\n\n return new JsonRpcClient(transport);\n}\n\n/**\n * Distinguish an {@link McpTransport} config (a plain object with a `type`\n * discriminator and no `request` method) from a built\n * {@link McpTransportClient} (which exposes `request`).\n */\nfunction isTransportConfig(source: McpTransport | McpTransportClient): source is McpTransport {\n return typeof (source as McpTransportClient).request !== \"function\";\n}\n\n/**\n * Wrap an injected {@link McpTransportClient} that lacks its own\n * `allocateId` with a monotonic id source, so the JSON-RPC client can mint\n * request ids uniformly regardless of whether the transport was built here\n * or supplied by a test.\n */\nfunction withIdAllocator(\n client: McpTransportClient,\n): McpTransportClient & { allocateId(): number } {\n const candidate = client as McpTransportClient & { allocateId?(): number };\n\n if (typeof candidate.allocateId === \"function\") {\n return candidate as McpTransportClient & { allocateId(): number };\n }\n\n let nextId = 1;\n\n return Object.assign(client, { allocateId: () => nextId++ });\n}\n"],"mappings":"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"}
|