@warlock.js/ai-tools 5.30.0 → 5.32.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 +53 -1
- package/README.md +25 -13
- package/cjs/index.cjs +968 -122
- 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 +3 -1
- package/esm/contracts/mcp-serve.type.d.mts +285 -0
- package/esm/contracts/mcp-serve.type.d.mts.map +1 -0
- package/esm/contracts/mcp.type.d.mts +44 -77
- 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 +62 -22
- package/esm/errors.d.mts.map +1 -1
- package/esm/errors.mjs +20 -1
- package/esm/errors.mjs.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 +10 -4
- package/esm/index.mjs +4 -2
- package/esm/mcp/client.mjs +37 -6
- package/esm/mcp/client.mjs.map +1 -1
- package/esm/mcp/index.d.mts +17 -9
- package/esm/mcp/index.d.mts.map +1 -1
- package/esm/mcp/index.mjs +3 -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 +15 -0
- package/esm/mcp/protocol.d.mts.map +1 -0
- package/esm/mcp/protocol.mjs +23 -0
- package/esm/mcp/protocol.mjs.map +1 -0
- 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 +444 -0
- package/esm/mcp/serve-http.mjs.map +1 -0
- package/esm/mcp/serve.d.mts +46 -27
- package/esm/mcp/serve.d.mts.map +1 -1
- package/esm/mcp/serve.mjs +339 -96
- 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 +15 -5
- package/esm/register.d.mts.map +1 -1
- package/esm/register.mjs +3 -2
- package/esm/register.mjs.map +1 -1
- package/esm/schema.mjs.map +1 -1
- package/esm/utility/calculator.d.mts +1 -3
- package/esm/utility/calculator.d.mts.map +1 -1
- package/esm/utility/calculator.mjs.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/utility/schema.mjs.map +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/schema.mjs.map +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 +245 -56
- package/llms.txt +3 -3
- package/package.json +2 -6
- package/skills/connect-mcp-server/SKILL.md +28 -6
- package/skills/expose-as-mcp-server/SKILL.md +168 -35
- package/skills/index.md +5 -2
- package/skills/use-web-and-http-tools/SKILL.md +49 -15
package/esm/mcp/client.mjs
CHANGED
|
@@ -20,8 +20,24 @@ const PROTOCOL_VERSION = "2025-06-18";
|
|
|
20
20
|
var McpClientImpl = class {
|
|
21
21
|
constructor(source, options = {}) {
|
|
22
22
|
this.initialized = false;
|
|
23
|
+
this.onAbort = () => {
|
|
24
|
+
this.close().catch(() => void 0);
|
|
25
|
+
};
|
|
23
26
|
this.rpc = createJsonRpcClient(source);
|
|
24
27
|
this.options = options;
|
|
28
|
+
const signal = options.signal;
|
|
29
|
+
if (signal?.aborted) this.onAbort();
|
|
30
|
+
else signal?.addEventListener("abort", this.onAbort, { once: true });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reject with a typed `"closed"` error once the client was closed or its
|
|
34
|
+
* `signal` aborted, so no request is sent on a dead transport.
|
|
35
|
+
*/
|
|
36
|
+
assertOpen(method) {
|
|
37
|
+
if (this.closing || this.options.signal?.aborted) throw new McpTransportError(`MCP client is closed; "${method}" was not sent.`, {
|
|
38
|
+
type: "closed",
|
|
39
|
+
method
|
|
40
|
+
});
|
|
25
41
|
}
|
|
26
42
|
/**
|
|
27
43
|
* Run the MCP `initialize` handshake exactly once, then send the
|
|
@@ -31,6 +47,7 @@ var McpClientImpl = class {
|
|
|
31
47
|
*/
|
|
32
48
|
async handshake() {
|
|
33
49
|
if (this.initialized) return;
|
|
50
|
+
this.assertOpen("initialize");
|
|
34
51
|
try {
|
|
35
52
|
await this.rpc.call("initialize", {
|
|
36
53
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -39,11 +56,12 @@ var McpClientImpl = class {
|
|
|
39
56
|
name: "@warlock.js/ai-tools",
|
|
40
57
|
version: "4.4.0"
|
|
41
58
|
}
|
|
42
|
-
});
|
|
59
|
+
}, { signal: this.options.signal });
|
|
43
60
|
await this.rpc.notify("notifications/initialized");
|
|
44
61
|
} catch (cause) {
|
|
45
62
|
if (cause instanceof McpTransportError) throw cause;
|
|
46
|
-
|
|
63
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
64
|
+
throw new McpTransportError(`MCP initialize handshake failed: ${message}`, {
|
|
47
65
|
type: "connect",
|
|
48
66
|
method: "initialize",
|
|
49
67
|
cause
|
|
@@ -68,7 +86,8 @@ var McpClientImpl = class {
|
|
|
68
86
|
*/
|
|
69
87
|
async listAndAdapt() {
|
|
70
88
|
await this.handshake();
|
|
71
|
-
|
|
89
|
+
this.assertOpen("tools/list");
|
|
90
|
+
const descriptors = (await this.rpc.call("tools/list", void 0, { signal: this.options.signal })).tools ?? [];
|
|
72
91
|
const filter = this.options.filter;
|
|
73
92
|
return (filter ? descriptors.filter((d) => filter(d.name)) : descriptors).map((descriptor) => this.adapt(descriptor));
|
|
74
93
|
}
|
|
@@ -89,11 +108,12 @@ var McpClientImpl = class {
|
|
|
89
108
|
description: descriptor.description ?? `Invoke the remote MCP tool "${remoteName}".`,
|
|
90
109
|
input,
|
|
91
110
|
execute: async (args, ctx) => {
|
|
111
|
+
this.assertOpen("tools/call");
|
|
92
112
|
const result = await this.rpc.call("tools/call", {
|
|
93
113
|
name: remoteName,
|
|
94
114
|
arguments: args ?? {}
|
|
95
115
|
}, {
|
|
96
|
-
signal: ctx?.signal,
|
|
116
|
+
signal: combineSignals(ctx?.signal, this.options.signal),
|
|
97
117
|
timeoutMs
|
|
98
118
|
});
|
|
99
119
|
if (result.isError) throw new McpTransportError(`MCP tool "${remoteName}" returned an error: ${unwrapContent(result.content)}`, {
|
|
@@ -105,10 +125,20 @@ var McpClientImpl = class {
|
|
|
105
125
|
});
|
|
106
126
|
}
|
|
107
127
|
close() {
|
|
108
|
-
|
|
128
|
+
this.options.signal?.removeEventListener("abort", this.onAbort);
|
|
129
|
+
this.closing ??= this.rpc.close();
|
|
130
|
+
return this.closing;
|
|
109
131
|
}
|
|
110
132
|
};
|
|
111
133
|
/**
|
|
134
|
+
* The signal a `tools/call` honors: the per-call `ctx.signal`, the
|
|
135
|
+
* client-level `options.signal`, both (whichever aborts first), or none.
|
|
136
|
+
*/
|
|
137
|
+
function combineSignals(callSignal, clientSignal) {
|
|
138
|
+
if (!callSignal || !clientSignal) return callSignal ?? clientSignal;
|
|
139
|
+
return AbortSignal.any([callSignal, clientSignal]);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
112
142
|
* Flatten an MCP `tools/call` result's content blocks into the value a
|
|
113
143
|
* tool returns. Text blocks are concatenated; a single block whose text is
|
|
114
144
|
* valid JSON is parsed so structured tool output flows back as an object
|
|
@@ -180,7 +210,8 @@ function tryParseJson(text) {
|
|
|
180
210
|
* @param server - The transport config (`{ type: "stdio" }` /
|
|
181
211
|
* `{ type: "http" }`). A pre-built transport client may be injected for
|
|
182
212
|
* testing.
|
|
183
|
-
* @param options - Prefix / filter / per-call timeout
|
|
213
|
+
* @param options - Prefix / filter / per-call timeout, and a `signal` that
|
|
214
|
+
* closes the client when it aborts.
|
|
184
215
|
* @returns An {@link McpClient} handle.
|
|
185
216
|
*
|
|
186
217
|
* @example
|
package/esm/mcp/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/client.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n McpClient,\n McpClientOptions,\n McpContentBlock,\n McpToolCallResult,\n McpToolDescriptor,\n McpTransport,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nimport { createJsonRpcClient, type JsonRpcClientHandle } from \"./transport\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-call timeout for `tools/call`. */\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\n\n/** The MCP protocol version this client advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** The `tools/list` response slice we read. */\ninterface ToolsListResult {\n tools?: McpToolDescriptor[];\n}\n\n/**\n * The internal {@link McpClient} implementation. Owns one JSON-RPC client\n * over a transport, runs the `initialize` handshake on first use, lists\n * the server's tools, and adapts each into a {@link ToolContract} whose\n * `execute` issues `tools/call`. The adapted contracts are cached after\n * the first `tools()` so repeat calls don't re-handshake.\n *\n * Constructed via {@link mcp}; the class itself is internal.\n */\nclass McpClientImpl implements McpClient {\n /** The JSON-RPC client over the transport. */\n private readonly rpc: JsonRpcClientHandle;\n\n /** Construction-time options (prefix / filter / timeout). */\n private readonly options: McpClientOptions;\n\n /** Resolved + cached adapted tools, set after the first `tools()`. */\n private cached: ToolContract[] | undefined;\n\n /** In-flight `tools()` so concurrent callers share one handshake. */\n private pending: Promise<ToolContract[]> | undefined;\n\n /** Flipped once the handshake completes so we only do it once. */\n private initialized = false;\n\n public constructor(\n source: McpTransport | McpTransportClient,\n options: McpClientOptions = {},\n ) {\n this.rpc = createJsonRpcClient(source);\n this.options = options;\n }\n\n /**\n * Run the MCP `initialize` handshake exactly once, then send the\n * `notifications/initialized` notification the protocol requires before\n * any other request. Wraps a handshake failure as a typed\n * {@link McpTransportError} of type `\"connect\"`.\n */\n private async handshake(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n try {\n await this.rpc.call(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"@warlock.js/ai-tools\", version: \"4.4.0\" },\n });\n\n await this.rpc.notify(\"notifications/initialized\");\n } catch (cause) {\n if (cause instanceof McpTransportError) {\n throw cause;\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(`MCP initialize handshake failed: ${message}`, {\n type: \"connect\",\n method: \"initialize\",\n cause,\n });\n }\n\n this.initialized = true;\n }\n\n public tools(): Promise<ToolContract[]> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.pending) {\n return this.pending;\n }\n\n this.pending = this.listAndAdapt()\n .then((tools) => {\n this.cached = tools;\n\n return tools;\n })\n .finally(() => {\n this.pending = undefined;\n });\n\n return this.pending;\n }\n\n /**\n * Handshake, `tools/list`, and adapt each descriptor into a\n * {@link ToolContract}, applying the `filter` and `namePrefix` options.\n */\n private async listAndAdapt(): Promise<ToolContract[]> {\n await this.handshake();\n\n const result = await this.rpc.call<ToolsListResult>(\"tools/list\");\n const descriptors = result.tools ?? [];\n\n const filter = this.options.filter;\n const selected = filter ? descriptors.filter((d) => filter(d.name)) : descriptors;\n\n return selected.map((descriptor) => this.adapt(descriptor));\n }\n\n /**\n * Adapt one remote tool descriptor into a {@link ToolContract}: build\n * the input schema from its JSON Schema via {@link jsonSchemaToStandard},\n * prefix the name, and route `execute` through a `tools/call` that\n * honors `ctx.signal`, unwraps the content blocks, and throws on an\n * `isError` result so `tool()` wraps it.\n */\n private adapt(descriptor: McpToolDescriptor): ToolContract {\n const prefixedName = `${this.options.namePrefix ?? \"\"}${descriptor.name}`;\n const remoteName = descriptor.name;\n const timeoutMs = this.options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const input = jsonSchemaToStandard(descriptor.inputSchema);\n\n return tool<unknown, unknown>({\n name: prefixedName,\n description:\n descriptor.description ?? `Invoke the remote MCP tool \"${remoteName}\".`,\n input,\n execute: async (args, ctx) => {\n const result = await this.rpc.call<McpToolCallResult>(\n \"tools/call\",\n { name: remoteName, arguments: args ?? {} },\n { signal: ctx?.signal, timeoutMs },\n );\n\n // An `isError` result is a tool-level failure — throw it so the\n // surrounding `tool()` wraps it as a `ToolExecutionError` and the\n // agent reads it as `{ error }` data and self-corrects.\n if (result.isError) {\n throw new McpTransportError(\n `MCP tool \"${remoteName}\" returned an error: ${unwrapContent(result.content)}`,\n { type: \"protocol\", method: \"tools/call\" },\n );\n }\n\n return unwrapResult(result.content);\n },\n });\n }\n\n public close(): Promise<void> {\n return this.rpc.close();\n }\n}\n\n/**\n * Flatten an MCP `tools/call` result's content blocks into the value a\n * tool returns. Text blocks are concatenated; a single block whose text is\n * valid JSON is parsed so structured tool output flows back as an object\n * rather than a string. Non-text blocks are preserved as `type`-tagged\n * objects (MCP's wire `type` is kept; any inbound `kind` is normalized to\n * `type`).\n */\nfunction unwrapResult(content: McpContentBlock[] | undefined): unknown {\n const blocks = content ?? [];\n\n // The overwhelmingly common case: a single text block. Parse JSON when\n // it is one, so structured results come back typed; otherwise the string.\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n const text = blocks[0].text ?? \"\";\n\n return tryParseJson(text);\n }\n\n // Multiple / mixed blocks: return a normalized array, each tagged by\n // `type` (never `kind`).\n return blocks.map((block) => normalizeBlock(block));\n}\n\n/**\n * Render content blocks to a short human string for error messages — the\n * concatenated text of every text block.\n */\nfunction unwrapContent(content: McpContentBlock[] | undefined): string {\n return (content ?? [])\n .filter((block) => block.type === \"text\" && typeof block.text === \"string\")\n .map((block) => block.text)\n .join(\" \")\n .trim();\n}\n\n/**\n * Normalize one content block onto our `type`-only shape: translate an\n * inbound `kind` discriminator to `type` (and strip `kind`) so the value a\n * tool returns never carries MCP's `kind` vocabulary.\n */\nfunction normalizeBlock(block: McpContentBlock): Record<string, unknown> {\n const { kind, ...rest } = block as McpContentBlock & { kind?: string };\n const type = block.type ?? kind ?? \"unknown\";\n\n return { ...rest, type };\n}\n\n/**\n * Parse a string as JSON, returning the parsed value on success or the\n * original string when it is not JSON — so a plain-text tool result stays\n * a string while a JSON tool result becomes an object.\n */\nfunction tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n\n if (!trimmed) {\n return text;\n }\n\n const first = trimmed[0];\n\n // Only attempt a parse for plausibly-structured payloads, so a bare\n // sentence isn't mangled by a lenient parse.\n if (first !== \"{\" && first !== \"[\") {\n return text;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return text;\n }\n}\n\n/**\n * Connect to an external MCP server and adapt its tools as agent tools\n * (Direction A: server → local agent tools).\n *\n * Opens the transport lazily and exposes {@link McpClient.tools}, which on\n * first call runs the `initialize` handshake, lists the server's tools via\n * `tools/list`, and maps each into a {@link ToolContract}:\n * - **input schema** — the remote tool's JSON Schema is wrapped as a\n * Standard Schema via {@link jsonSchemaToStandard} (Ajv-backed, an\n * optional peer);\n * - **execute** — issues `tools/call` honoring `ctx.signal` and the\n * configured `timeoutMs`, unwraps the result content, and throws on an\n * `isError` result so the `tool()` wrapper surfaces it as `{ error }`;\n * - **name** — prefixed with `options.namePrefix` to avoid local\n * collisions; only tools passing `options.filter` are adapted.\n *\n * The adapted contracts are cached after the first `tools()` call, so the\n * handshake + list happen exactly once. The returned contracts drop\n * straight into `ai.agent({ tools: [...] })`.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` /\n * `{ type: \"http\" }`). A pre-built transport client may be injected for\n * testing.\n * @param options - Prefix / filter / per-call timeout.\n * @returns An {@link McpClient} handle.\n *\n * @example\n * const github = mcp(\n * { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n * { namePrefix: \"github.\" },\n * );\n * const dev = ai.agent({ model, tools: [...(await github.tools())] });\n */\nexport function mcp(\n server: McpTransport | McpTransportClient,\n options?: McpClientOptions,\n): McpClient {\n return new McpClientImpl(server, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/client.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n McpClient,\n McpClientOptions,\n McpContentBlock,\n McpToolCallResult,\n McpToolDescriptor,\n McpTransport,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nimport { createJsonRpcClient, type JsonRpcClientHandle } from \"./transport\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-call timeout for `tools/call`. */\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\n\n/** The MCP protocol version this client advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** The `tools/list` response slice we read. */\ninterface ToolsListResult {\n tools?: McpToolDescriptor[];\n}\n\n/**\n * The internal {@link McpClient} implementation. Owns one JSON-RPC client\n * over a transport, runs the `initialize` handshake on first use, lists\n * the server's tools, and adapts each into a {@link ToolContract} whose\n * `execute` issues `tools/call`. The adapted contracts are cached after\n * the first `tools()` so repeat calls don't re-handshake.\n *\n * Constructed via {@link mcp}; the class itself is internal.\n */\nclass McpClientImpl implements McpClient {\n /** The JSON-RPC client over the transport. */\n private readonly rpc: JsonRpcClientHandle;\n\n /** Construction-time options (prefix / filter / timeout). */\n private readonly options: McpClientOptions;\n\n /** Resolved + cached adapted tools, set after the first `tools()`. */\n private cached: ToolContract[] | undefined;\n\n /** In-flight `tools()` so concurrent callers share one handshake. */\n private pending: Promise<ToolContract[]> | undefined;\n\n /** Flipped once the handshake completes so we only do it once. */\n private initialized = false;\n\n /** The in-flight / settled `close()`, so closing is idempotent. */\n private closing: Promise<void> | undefined;\n\n /** Closes the client when `options.signal` aborts. */\n private readonly onAbort = (): void => {\n void this.close().catch(() => undefined);\n };\n\n public constructor(source: McpTransport | McpTransportClient, options: McpClientOptions = {}) {\n this.rpc = createJsonRpcClient(source);\n this.options = options;\n\n const signal = options.signal;\n\n if (signal?.aborted) {\n this.onAbort();\n } else {\n signal?.addEventListener(\"abort\", this.onAbort, { once: true });\n }\n }\n\n /**\n * Reject with a typed `\"closed\"` error once the client was closed or its\n * `signal` aborted, so no request is sent on a dead transport.\n */\n private assertOpen(method: string): void {\n if (this.closing || this.options.signal?.aborted) {\n throw new McpTransportError(`MCP client is closed; \"${method}\" was not sent.`, {\n type: \"closed\",\n method,\n });\n }\n }\n\n /**\n * Run the MCP `initialize` handshake exactly once, then send the\n * `notifications/initialized` notification the protocol requires before\n * any other request. Wraps a handshake failure as a typed\n * {@link McpTransportError} of type `\"connect\"`.\n */\n private async handshake(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n this.assertOpen(\"initialize\");\n\n try {\n await this.rpc.call(\n \"initialize\",\n {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"@warlock.js/ai-tools\", version: \"4.4.0\" },\n },\n { signal: this.options.signal },\n );\n\n await this.rpc.notify(\"notifications/initialized\");\n } catch (cause) {\n if (cause instanceof McpTransportError) {\n throw cause;\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(`MCP initialize handshake failed: ${message}`, {\n type: \"connect\",\n method: \"initialize\",\n cause,\n });\n }\n\n this.initialized = true;\n }\n\n public tools(): Promise<ToolContract[]> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.pending) {\n return this.pending;\n }\n\n this.pending = this.listAndAdapt()\n .then((tools) => {\n this.cached = tools;\n\n return tools;\n })\n .finally(() => {\n this.pending = undefined;\n });\n\n return this.pending;\n }\n\n /**\n * Handshake, `tools/list`, and adapt each descriptor into a\n * {@link ToolContract}, applying the `filter` and `namePrefix` options.\n */\n private async listAndAdapt(): Promise<ToolContract[]> {\n await this.handshake();\n\n this.assertOpen(\"tools/list\");\n\n const result = await this.rpc.call<ToolsListResult>(\"tools/list\", undefined, {\n signal: this.options.signal,\n });\n const descriptors = result.tools ?? [];\n\n const filter = this.options.filter;\n const selected = filter ? descriptors.filter((d) => filter(d.name)) : descriptors;\n\n return selected.map((descriptor) => this.adapt(descriptor));\n }\n\n /**\n * Adapt one remote tool descriptor into a {@link ToolContract}: build\n * the input schema from its JSON Schema via {@link jsonSchemaToStandard},\n * prefix the name, and route `execute` through a `tools/call` that\n * honors `ctx.signal`, unwraps the content blocks, and throws on an\n * `isError` result so `tool()` wraps it.\n */\n private adapt(descriptor: McpToolDescriptor): ToolContract {\n const prefixedName = `${this.options.namePrefix ?? \"\"}${descriptor.name}`;\n const remoteName = descriptor.name;\n const timeoutMs = this.options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const input = jsonSchemaToStandard(descriptor.inputSchema);\n\n return tool<unknown, unknown>({\n name: prefixedName,\n description: descriptor.description ?? `Invoke the remote MCP tool \"${remoteName}\".`,\n input,\n execute: async (args, ctx) => {\n this.assertOpen(\"tools/call\");\n\n const result = await this.rpc.call<McpToolCallResult>(\n \"tools/call\",\n { name: remoteName, arguments: args ?? {} },\n { signal: combineSignals(ctx?.signal, this.options.signal), timeoutMs },\n );\n\n // An `isError` result is a tool-level failure — throw it so the\n // surrounding `tool()` wraps it as a `ToolExecutionError` and the\n // agent reads it as `{ error }` data and self-corrects.\n if (result.isError) {\n throw new McpTransportError(\n `MCP tool \"${remoteName}\" returned an error: ${unwrapContent(result.content)}`,\n { type: \"protocol\", method: \"tools/call\" },\n );\n }\n\n return unwrapResult(result.content);\n },\n });\n }\n\n public close(): Promise<void> {\n this.options.signal?.removeEventListener(\"abort\", this.onAbort);\n this.closing ??= this.rpc.close();\n\n return this.closing;\n }\n}\n\n/**\n * The signal a `tools/call` honors: the per-call `ctx.signal`, the\n * client-level `options.signal`, both (whichever aborts first), or none.\n */\nfunction combineSignals(\n callSignal: AbortSignal | undefined,\n clientSignal: AbortSignal | undefined,\n): AbortSignal | undefined {\n if (!callSignal || !clientSignal) {\n return callSignal ?? clientSignal;\n }\n\n return AbortSignal.any([callSignal, clientSignal]);\n}\n\n/**\n * Flatten an MCP `tools/call` result's content blocks into the value a\n * tool returns. Text blocks are concatenated; a single block whose text is\n * valid JSON is parsed so structured tool output flows back as an object\n * rather than a string. Non-text blocks are preserved as `type`-tagged\n * objects (MCP's wire `type` is kept; any inbound `kind` is normalized to\n * `type`).\n */\nfunction unwrapResult(content: McpContentBlock[] | undefined): unknown {\n const blocks = content ?? [];\n\n // The overwhelmingly common case: a single text block. Parse JSON when\n // it is one, so structured results come back typed; otherwise the string.\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n const text = blocks[0].text ?? \"\";\n\n return tryParseJson(text);\n }\n\n // Multiple / mixed blocks: return a normalized array, each tagged by\n // `type` (never `kind`).\n return blocks.map((block) => normalizeBlock(block));\n}\n\n/**\n * Render content blocks to a short human string for error messages — the\n * concatenated text of every text block.\n */\nfunction unwrapContent(content: McpContentBlock[] | undefined): string {\n return (content ?? [])\n .filter((block) => block.type === \"text\" && typeof block.text === \"string\")\n .map((block) => block.text)\n .join(\" \")\n .trim();\n}\n\n/**\n * Normalize one content block onto our `type`-only shape: translate an\n * inbound `kind` discriminator to `type` (and strip `kind`) so the value a\n * tool returns never carries MCP's `kind` vocabulary.\n */\nfunction normalizeBlock(block: McpContentBlock): Record<string, unknown> {\n const { kind, ...rest } = block as McpContentBlock & { kind?: string };\n const type = block.type ?? kind ?? \"unknown\";\n\n return { ...rest, type };\n}\n\n/**\n * Parse a string as JSON, returning the parsed value on success or the\n * original string when it is not JSON — so a plain-text tool result stays\n * a string while a JSON tool result becomes an object.\n */\nfunction tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n\n if (!trimmed) {\n return text;\n }\n\n const first = trimmed[0];\n\n // Only attempt a parse for plausibly-structured payloads, so a bare\n // sentence isn't mangled by a lenient parse.\n if (first !== \"{\" && first !== \"[\") {\n return text;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return text;\n }\n}\n\n/**\n * Connect to an external MCP server and adapt its tools as agent tools\n * (Direction A: server → local agent tools).\n *\n * Opens the transport lazily and exposes {@link McpClient.tools}, which on\n * first call runs the `initialize` handshake, lists the server's tools via\n * `tools/list`, and maps each into a {@link ToolContract}:\n * - **input schema** — the remote tool's JSON Schema is wrapped as a\n * Standard Schema via {@link jsonSchemaToStandard} (Ajv-backed, an\n * optional peer);\n * - **execute** — issues `tools/call` honoring `ctx.signal` and the\n * configured `timeoutMs`, unwraps the result content, and throws on an\n * `isError` result so the `tool()` wrapper surfaces it as `{ error }`;\n * - **name** — prefixed with `options.namePrefix` to avoid local\n * collisions; only tools passing `options.filter` are adapted.\n *\n * The adapted contracts are cached after the first `tools()` call, so the\n * handshake + list happen exactly once. The returned contracts drop\n * straight into `ai.agent({ tools: [...] })`.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` /\n * `{ type: \"http\" }`). A pre-built transport client may be injected for\n * testing.\n * @param options - Prefix / filter / per-call timeout, and a `signal` that\n * closes the client when it aborts.\n * @returns An {@link McpClient} handle.\n *\n * @example\n * const github = mcp(\n * { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n * { namePrefix: \"github.\" },\n * );\n * const dev = ai.agent({ model, tools: [...(await github.tools())] });\n */\nexport function mcp(\n server: McpTransport | McpTransportClient,\n options?: McpClientOptions,\n): McpClient {\n return new McpClientImpl(server, options);\n}\n"],"mappings":"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"}
|
package/esm/mcp/index.d.mts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { McpClient, McpClientOptions,
|
|
2
|
-
import {
|
|
1
|
+
import { McpClient, McpClientOptions, McpTransport } from "../contracts/mcp.type.mjs";
|
|
2
|
+
import { McpHttpServeHandle, McpHttpServeTransport, McpServeOptions, McpServeSource, McpServer } from "../contracts/mcp-serve.type.mjs";
|
|
3
|
+
import "../contracts/index.mjs";
|
|
4
|
+
import { McpServeRequestHandler, createServeHandler, serve } from "./serve.mjs";
|
|
5
|
+
import { SUPPORTED_PROTOCOL_VERSIONS } from "./protocol.mjs";
|
|
6
|
+
import { McpNativeResult, mcpResult } from "./result.mjs";
|
|
3
7
|
import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
|
|
4
8
|
import { McpTransportClient } from "./transport.type.mjs";
|
|
5
9
|
import { JsonRpcClientHandle, createJsonRpcClient, createTransport } from "./transport.mjs";
|
|
6
|
-
|
|
7
10
|
//#region ../../packages/ai-tools/src/mcp/index.d.ts
|
|
8
11
|
/**
|
|
9
12
|
* The callable `ai.mcp` surface — a factory that connects to an external
|
|
@@ -12,21 +15,26 @@ import { JsonRpcClientHandle, createJsonRpcClient, createTransport } from "./tra
|
|
|
12
15
|
* attached `serve` property, mirroring how `ai.mcp(server).tools()` and
|
|
13
16
|
* `ai.mcp.serve(source, options).start()` read in the design.
|
|
14
17
|
*/
|
|
15
|
-
interface McpFactory {
|
|
18
|
+
export interface McpFactory {
|
|
16
19
|
/**
|
|
17
20
|
* Connect to an external MCP server and adapt its tools as agent tools.
|
|
18
21
|
*
|
|
19
22
|
* @param server - The transport config (`{ type: "stdio" }` / `{ type: "http" }`).
|
|
20
|
-
* @param options - Prefix / filter / per-call timeout
|
|
23
|
+
* @param options - Prefix / filter / per-call timeout, and a `signal`
|
|
24
|
+
* that closes the client (and fails its calls) when it aborts.
|
|
21
25
|
*/
|
|
22
26
|
(server: McpTransport, options?: McpClientOptions): McpClient;
|
|
23
27
|
/**
|
|
24
28
|
* Expose a built agent / supervisor / orchestrator (or a literal
|
|
25
|
-
* `ToolContract[]`) AS an MCP server.
|
|
29
|
+
* `ToolContract[]`, or named `{ groups }`) AS an MCP server. Over
|
|
30
|
+
* `{ type: "http" }`, `start()` resolves `{ url, close }`.
|
|
26
31
|
*
|
|
27
32
|
* @param source - The tools to expose.
|
|
28
|
-
* @param options - Server name, version, transport,
|
|
33
|
+
* @param options - Server name, version, transport, schema dialect, resources, prompts.
|
|
29
34
|
*/
|
|
35
|
+
serve(source: McpServeSource, options: McpServeOptions & {
|
|
36
|
+
transport: McpHttpServeTransport;
|
|
37
|
+
}): McpServer<McpHttpServeHandle>;
|
|
30
38
|
serve(source: McpServeSource, options: McpServeOptions): McpServer;
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
@@ -37,7 +45,7 @@ interface McpFactory {
|
|
|
37
45
|
* registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a
|
|
38
46
|
* pure value/type module the registrar consumes.
|
|
39
47
|
*/
|
|
40
|
-
declare const mcp: McpFactory;
|
|
48
|
+
export declare const mcp: McpFactory;
|
|
41
49
|
//#endregion
|
|
42
|
-
export {
|
|
50
|
+
export { type McpNativeResult, type McpServeRequestHandler, type McpTransportClient, SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcpResult, serve };
|
|
43
51
|
//# sourceMappingURL=index.d.mts.map
|
package/esm/mcp/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"mappings":";;;;;;;;;;;;;;;;;iBA8BiB;;;;;;;;GAQd,QAAQ,cAAc,UAAU,mBAAmB;;;;;;;;;EASpD,MACE,QAAQ,gBACR,SAAS;IAAoB,WAAW;MACvC,UAAU;EACb,MAAM,QAAQ,gBAAgB,SAAS,kBAAkB;;;;;;;;;;qBAW9C,KAAK"}
|
package/esm/mcp/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
|
|
2
2
|
import { createJsonRpcClient, createTransport } from "./transport.mjs";
|
|
3
3
|
import { mcp as mcp$1 } from "./client.mjs";
|
|
4
|
+
import { SUPPORTED_PROTOCOL_VERSIONS } from "./protocol.mjs";
|
|
5
|
+
import { mcpResult } from "./result.mjs";
|
|
4
6
|
import { createServeHandler, serve } from "./serve.mjs";
|
|
5
7
|
|
|
6
8
|
//#region ../../packages/ai-tools/src/mcp/index.ts
|
|
@@ -15,5 +17,5 @@ import { createServeHandler, serve } from "./serve.mjs";
|
|
|
15
17
|
const mcp = Object.assign((server, options) => mcp$1(server, options), { serve });
|
|
16
18
|
|
|
17
19
|
//#endregion
|
|
18
|
-
export { mcp };
|
|
20
|
+
export { SUPPORTED_PROTOCOL_VERSIONS, createServeHandler, mcp, mcpResult, serve };
|
|
19
21
|
//# sourceMappingURL=index.mjs.map
|
package/esm/mcp/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`) AS an MCP server
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../../packages/ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpHttpServeHandle,\n McpHttpServeTransport,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport type { McpServeRequestHandler } from \"./serve\";\nexport { SUPPORTED_PROTOCOL_VERSIONS } from \"./protocol\";\nexport { mcpResult } from \"./result\";\nexport type { McpNativeResult } from \"./result\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout, and a `signal`\n * that closes the client (and fails its calls) when it aborts.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`, or named `{ groups }`) AS an MCP server. Over\n * `{ type: \"http\" }`, `start()` resolves `{ url, close }`.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, schema dialect, resources, prompts.\n */\n serve(\n source: McpServeSource,\n options: McpServeOptions & { transport: McpHttpServeTransport },\n ): McpServer<McpHttpServeHandle>;\n serve(source: McpServeSource, options: McpServeOptions): McpServer;\n}\n\n/**\n * The `ai.mcp` factory value: the client factory with `.serve` attached.\n * `Object.assign` keeps `mcp` callable (Direction A) while widening it with\n * the `serve` member (Direction B) — one object, both directions. The\n * `declare module \"@warlock.js/ai\"` augmentation and the runtime\n * registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a\n * pure value/type module the registrar consumes.\n */\nexport const mcp: McpFactory = Object.assign(\n (server: McpTransport, options?: McpClientOptions): McpClient => mcpClient(server, options),\n { serve },\n);\n"],"mappings":";;;;;;;;;;;;;;;;AA8DA,MAAa,MAAkB,OAAO,QACnC,QAAsB,YAA0CA,MAAU,QAAQ,OAAO,GAC1F,EAAE,MAAM,CACV"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/ai-tools/src/mcp/json-schema-to-standard.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Wrap a raw JSON Schema as a {@link StandardSchemaV1} whose
|
|
@@ -28,7 +27,6 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
|
28
27
|
* required: ["q"],
|
|
29
28
|
* });
|
|
30
29
|
*/
|
|
31
|
-
declare function jsonSchemaToStandard<TInput = unknown>(schema: Record<string, unknown> | undefined): StandardSchemaV1<TInput>;
|
|
30
|
+
export declare function jsonSchemaToStandard<TInput = unknown>(schema: Record<string, unknown> | undefined): StandardSchemaV1<TInput>;
|
|
32
31
|
//#endregion
|
|
33
|
-
export { jsonSchemaToStandard };
|
|
34
32
|
//# sourceMappingURL=json-schema-to-standard.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-schema-to-standard.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/json-schema-to-standard.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"json-schema-to-standard.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/json-schema-to-standard.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+JgB,qBAAqB,kBACnC,QAAQ,sCACP,iBAAiB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region ../../packages/ai-tools/src/mcp/protocol.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* MCP protocol versions the `ai.mcp.serve` server speaks, newest first.
|
|
4
|
+
* `initialize` echoes the client's requested version when it is listed
|
|
5
|
+
* here and otherwise answers with the newest one (lifecycle, version
|
|
6
|
+
* negotiation). The HTTP transport rejects any other value in the
|
|
7
|
+
* `MCP-Protocol-Version` header with 400.
|
|
8
|
+
*
|
|
9
|
+
* Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle
|
|
10
|
+
* and https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
|
|
11
|
+
* (checked 2026-10-17).
|
|
12
|
+
*/
|
|
13
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
|
|
14
|
+
//#endregion
|
|
15
|
+
//# sourceMappingURL=protocol.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/protocol.ts"],"mappings":";;;;;;;;;;;;qBAWa"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region ../../packages/ai-tools/src/mcp/protocol.ts
|
|
2
|
+
/**
|
|
3
|
+
* MCP protocol versions the `ai.mcp.serve` server speaks, newest first.
|
|
4
|
+
* `initialize` echoes the client's requested version when it is listed
|
|
5
|
+
* here and otherwise answers with the newest one (lifecycle, version
|
|
6
|
+
* negotiation). The HTTP transport rejects any other value in the
|
|
7
|
+
* `MCP-Protocol-Version` header with 400.
|
|
8
|
+
*
|
|
9
|
+
* Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle
|
|
10
|
+
* and https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
|
|
11
|
+
* (checked 2026-10-17).
|
|
12
|
+
*/
|
|
13
|
+
const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
14
|
+
"2025-11-25",
|
|
15
|
+
"2025-06-18",
|
|
16
|
+
"2025-03-26"
|
|
17
|
+
];
|
|
18
|
+
/** The newest MCP protocol version the server speaks. */
|
|
19
|
+
const LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS };
|
|
23
|
+
//# sourceMappingURL=protocol.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/protocol.ts"],"sourcesContent":["/**\n * MCP protocol versions the `ai.mcp.serve` server speaks, newest first.\n * `initialize` echoes the client's requested version when it is listed\n * here and otherwise answers with the newest one (lifecycle, version\n * negotiation). The HTTP transport rejects any other value in the\n * `MCP-Protocol-Version` header with 400.\n *\n * Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle\n * and https://modelcontextprotocol.io/specification/2025-11-25/basic/transports\n * (checked 2026-10-17).\n */\nexport const SUPPORTED_PROTOCOL_VERSIONS: readonly string[] = [\n \"2025-11-25\",\n \"2025-06-18\",\n \"2025-03-26\",\n];\n\n/** The newest MCP protocol version the server speaks. */\nexport const LATEST_PROTOCOL_VERSION = \"2025-11-25\";\n"],"mappings":";;;;;;;;;;;;AAWA,MAAa,8BAAiD;CAC5D;CACA;CACA;AACF;;AAGA,MAAa,0BAA0B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { McpToolCallResult } from "../contracts/mcp.type.mjs";
|
|
2
|
+
import "../contracts/index.mjs";
|
|
3
|
+
//#region ../../packages/ai-tools/src/mcp/result.d.ts
|
|
4
|
+
declare const NATIVE_RESULT: unique symbol;
|
|
5
|
+
/** An explicit native MCP result, unwrapped only by the serving boundary. */
|
|
6
|
+
export type McpNativeResult<T extends McpToolCallResult = McpToolCallResult> = {
|
|
7
|
+
readonly [NATIVE_RESULT]: T;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Opt in to native MCP content, error status, structured output and metadata.
|
|
11
|
+
* Validation happens after invoke(), so malformed payloads become JSON-RPC
|
|
12
|
+
* errors rather than being caught as tool execution errors.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mcpResult<T extends McpToolCallResult>(result: T): McpNativeResult<T>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=result.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/result.ts"],"mappings":";;;cAEM;;YAGM,gBAAgB,UAAU,oBAAoB;YAC9C,gBAAgB;;;;;;;wBAQZ,UAAU,UAAU,mBAAmB,QAAQ,IAAI,gBAAgB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//#region ../../packages/ai-tools/src/mcp/result.ts
|
|
2
|
+
const NATIVE_RESULT = Symbol("mcpResult");
|
|
3
|
+
/**
|
|
4
|
+
* Opt in to native MCP content, error status, structured output and metadata.
|
|
5
|
+
* Validation happens after invoke(), so malformed payloads become JSON-RPC
|
|
6
|
+
* errors rather than being caught as tool execution errors.
|
|
7
|
+
*/
|
|
8
|
+
function mcpResult(result) {
|
|
9
|
+
return { [NATIVE_RESULT]: result };
|
|
10
|
+
}
|
|
11
|
+
/** Read a branded value without accepting lookalike application objects. */
|
|
12
|
+
function isMcpResult(value) {
|
|
13
|
+
return isRecord(value) && NATIVE_RESULT in value;
|
|
14
|
+
}
|
|
15
|
+
/** Get the original payload; no cloning, normalization or field stripping. */
|
|
16
|
+
function unwrapMcpResult(value) {
|
|
17
|
+
return value[NATIVE_RESULT];
|
|
18
|
+
}
|
|
19
|
+
/** Objects with result fields are candidates only when native mode is enabled. */
|
|
20
|
+
function looksLikeMcpResult(value) {
|
|
21
|
+
return isRecord(value) && [
|
|
22
|
+
"content",
|
|
23
|
+
"isError",
|
|
24
|
+
"structuredContent",
|
|
25
|
+
"_meta"
|
|
26
|
+
].some((key) => key in value);
|
|
27
|
+
}
|
|
28
|
+
/** Non-array object used by the MCP JSON object fields. */
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
function optionalString(value) {
|
|
33
|
+
return value === void 0 || typeof value === "string";
|
|
34
|
+
}
|
|
35
|
+
function validAnnotations(value) {
|
|
36
|
+
if (value === void 0) return true;
|
|
37
|
+
if (!isRecord(value)) return false;
|
|
38
|
+
return (value.audience === void 0 || Array.isArray(value.audience) && value.audience.every((role) => role === "user" || role === "assistant")) && (value.priority === void 0 || typeof value.priority === "number" && value.priority >= 0 && value.priority <= 1) && optionalString(value.lastModified);
|
|
39
|
+
}
|
|
40
|
+
function validMetadata(value) {
|
|
41
|
+
return (value["_meta"] === void 0 || isRecord(value["_meta"])) && validAnnotations(value.annotations);
|
|
42
|
+
}
|
|
43
|
+
function validIcon(value) {
|
|
44
|
+
return isRecord(value) && typeof value.src === "string" && optionalString(value.mimeType) && (value.sizes === void 0 || Array.isArray(value.sizes) && value.sizes.every((size) => typeof size === "string")) && (value.theme === void 0 || value.theme === "light" || value.theme === "dark");
|
|
45
|
+
}
|
|
46
|
+
function validContent(value) {
|
|
47
|
+
if (!isRecord(value) || !validMetadata(value)) return false;
|
|
48
|
+
switch (value.type) {
|
|
49
|
+
case "text": return typeof value.text === "string";
|
|
50
|
+
case "image":
|
|
51
|
+
case "audio": return typeof value.data === "string" && typeof value.mimeType === "string";
|
|
52
|
+
case "resource_link": return typeof value.uri === "string" && typeof value.name === "string" && optionalString(value.title) && optionalString(value.description) && optionalString(value.mimeType) && (value.size === void 0 || typeof value.size === "number") && (value.icons === void 0 || Array.isArray(value.icons) && value.icons.every(validIcon));
|
|
53
|
+
case "resource": {
|
|
54
|
+
const resource = value.resource;
|
|
55
|
+
return isRecord(resource) && typeof resource.uri === "string" && optionalString(resource.mimeType) && validMetadata(resource) && (typeof resource.text === "string" || typeof resource.blob === "string");
|
|
56
|
+
}
|
|
57
|
+
default: return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Reject values JSON would drop, coerce or fail to encode (including cycles). */
|
|
61
|
+
function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
62
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
63
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
64
|
+
if (typeof value !== "object" || ancestors.has(value)) return false;
|
|
65
|
+
const prototype = Object.getPrototypeOf(value);
|
|
66
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) return false;
|
|
67
|
+
ancestors.add(value);
|
|
68
|
+
const valid = Array.isArray(value) ? Array.from(value).every((entry) => isJsonValue(entry, ancestors)) : Object.values(value).every((entry) => entry === void 0 || isJsonValue(entry, ancestors));
|
|
69
|
+
ancestors.delete(value);
|
|
70
|
+
return valid;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* MCP 2025-11-25 CallToolResult and ContentBlock fields:
|
|
74
|
+
* https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-result
|
|
75
|
+
* https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts
|
|
76
|
+
* structuredContent is preserved even when tools/list declares outputSchema.
|
|
77
|
+
* Tool authors remain responsible for conformity to that application schema.
|
|
78
|
+
*/
|
|
79
|
+
function assertMcpResult(value) {
|
|
80
|
+
if (!isRecord(value) || !Array.isArray(value.content) || !Array.from(value.content).every(validContent) || value.isError !== void 0 && typeof value.isError !== "boolean" || value.structuredContent !== void 0 && !isRecord(value.structuredContent) || value["_meta"] !== void 0 && !isRecord(value["_meta"]) || !isJsonValue(value)) throw new TypeError("Invalid native MCP CallToolResult.");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { assertMcpResult, isMcpResult, isRecord, looksLikeMcpResult, mcpResult, unwrapMcpResult };
|
|
85
|
+
//# sourceMappingURL=result.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.mjs","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/result.ts"],"sourcesContent":["import type { McpToolCallResult } from \"../contracts\";\n\nconst NATIVE_RESULT = Symbol(\"mcpResult\");\n\n/** An explicit native MCP result, unwrapped only by the serving boundary. */\nexport type McpNativeResult<T extends McpToolCallResult = McpToolCallResult> = {\n readonly [NATIVE_RESULT]: T;\n};\n\n/**\n * Opt in to native MCP content, error status, structured output and metadata.\n * Validation happens after invoke(), so malformed payloads become JSON-RPC\n * errors rather than being caught as tool execution errors.\n */\nexport function mcpResult<T extends McpToolCallResult>(result: T): McpNativeResult<T> {\n return { [NATIVE_RESULT]: result };\n}\n\n/** Read a branded value without accepting lookalike application objects. */\nexport function isMcpResult(value: unknown): value is McpNativeResult {\n return isRecord(value) && NATIVE_RESULT in value;\n}\n\n/** Get the original payload; no cloning, normalization or field stripping. */\nexport function unwrapMcpResult(value: McpNativeResult): McpToolCallResult {\n return value[NATIVE_RESULT];\n}\n\n/** Objects with result fields are candidates only when native mode is enabled. */\nexport function looksLikeMcpResult(value: unknown): boolean {\n return (\n isRecord(value) &&\n [\"content\", \"isError\", \"structuredContent\", \"_meta\"].some((key) => key in value)\n );\n}\n\n/** Non-array object used by the MCP JSON object fields. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction optionalString(value: unknown): boolean {\n return value === undefined || typeof value === \"string\";\n}\n\nfunction validAnnotations(value: unknown): boolean {\n if (value === undefined) return true;\n if (!isRecord(value)) return false;\n\n return (\n (value.audience === undefined ||\n (Array.isArray(value.audience) &&\n value.audience.every((role) => role === \"user\" || role === \"assistant\"))) &&\n (value.priority === undefined ||\n (typeof value.priority === \"number\" && value.priority >= 0 && value.priority <= 1)) &&\n optionalString(value.lastModified)\n );\n}\n\nfunction validMetadata(value: Record<string, unknown>): boolean {\n return (\n (value[\"_meta\"] === undefined || isRecord(value[\"_meta\"])) &&\n validAnnotations(value.annotations)\n );\n}\n\nfunction validIcon(value: unknown): boolean {\n return (\n isRecord(value) &&\n typeof value.src === \"string\" &&\n optionalString(value.mimeType) &&\n (value.sizes === undefined ||\n (Array.isArray(value.sizes) && value.sizes.every((size) => typeof size === \"string\"))) &&\n (value.theme === undefined || value.theme === \"light\" || value.theme === \"dark\")\n );\n}\n\nfunction validContent(value: unknown): boolean {\n if (!isRecord(value) || !validMetadata(value)) return false;\n\n switch (value.type) {\n case \"text\":\n return typeof value.text === \"string\";\n case \"image\":\n case \"audio\":\n return typeof value.data === \"string\" && typeof value.mimeType === \"string\";\n case \"resource_link\":\n return (\n typeof value.uri === \"string\" &&\n typeof value.name === \"string\" &&\n optionalString(value.title) &&\n optionalString(value.description) &&\n optionalString(value.mimeType) &&\n (value.size === undefined || typeof value.size === \"number\") &&\n (value.icons === undefined || (Array.isArray(value.icons) && value.icons.every(validIcon)))\n );\n case \"resource\": {\n const resource = value.resource;\n\n return (\n isRecord(resource) &&\n typeof resource.uri === \"string\" &&\n optionalString(resource.mimeType) &&\n validMetadata(resource) &&\n (typeof resource.text === \"string\" || typeof resource.blob === \"string\")\n );\n }\n default:\n return false;\n }\n}\n\n/** Reject values JSON would drop, coerce or fail to encode (including cycles). */\nfunction isJsonValue(value: unknown, ancestors = new Set<object>()): boolean {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return true;\n if (typeof value === \"number\") return Number.isFinite(value);\n if (typeof value !== \"object\" || ancestors.has(value)) return false;\n\n const prototype: unknown = Object.getPrototypeOf(value);\n if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) return false;\n\n ancestors.add(value);\n const valid = Array.isArray(value)\n ? Array.from(value).every((entry) => isJsonValue(entry, ancestors))\n : Object.values(value).every((entry) => entry === undefined || isJsonValue(entry, ancestors));\n ancestors.delete(value);\n return valid;\n}\n\n/**\n * MCP 2025-11-25 CallToolResult and ContentBlock fields:\n * https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-result\n * https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts\n * structuredContent is preserved even when tools/list declares outputSchema.\n * Tool authors remain responsible for conformity to that application schema.\n */\nexport function assertMcpResult(value: unknown): asserts value is McpToolCallResult {\n if (\n !isRecord(value) ||\n !Array.isArray(value.content) ||\n !Array.from(value.content).every(validContent) ||\n (value.isError !== undefined && typeof value.isError !== \"boolean\") ||\n (value.structuredContent !== undefined && !isRecord(value.structuredContent)) ||\n (value[\"_meta\"] !== undefined && !isRecord(value[\"_meta\"])) ||\n !isJsonValue(value)\n ) {\n throw new TypeError(\"Invalid native MCP CallToolResult.\");\n }\n}\n"],"mappings":"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"}
|