@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/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 ToolContract } from \"@warlock.js/ai\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpToolCallResult,\n McpToolDescriptor,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\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/** The MCP protocol version this server advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** JSON-RPC standard error codes we emit. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\nconst JSON_RPC_INVALID_PARAMS = -32602;\n\n/**\n * Resolve the {@link McpServeSource} (either an object exposing `tools()`\n * or a literal `ToolContract[]`) into a flat contract array.\n */\nfunction resolveTools(source: McpServeSource): ToolContract[] {\n if (Array.isArray(source)) {\n return source;\n }\n\n return source.tools();\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 exactly the MCP slice this package serves: `initialize`,\n * `tools/list`, and `tools/call`. Any other method answers with a\n * 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: ToolContract[];\n\n /** Fast lookup by tool name for `tools/call` dispatch. */\n private readonly byName: Map<string, ToolContract>;\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);\n this.byName = new Map(this.tools.map((contract) => [contract.name, contract]));\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 public async handle(request: JsonRpcRequest): Promise<JsonRpcResponse> {\n try {\n switch (request.method) {\n case \"initialize\":\n return this.ok(request.id, this.initializeResult());\n case \"tools/list\":\n return this.ok(request.id, { tools: this.listTools() });\n case \"tools/call\":\n return this.ok(request.id, await this.callTool(request.params));\n default:\n return this.error(\n request.id,\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${request.method}\" is not supported by this MCP server.`,\n );\n }\n } catch (cause) {\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 /** Build the `initialize` result advertising name / version / capabilities. */\n private initializeResult(): Record<string, unknown> {\n return {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\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 * contract, its `inputSchema` extracted via `extractJsonSchema` at the\n * 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((contract) => {\n const inputSchema = extractJsonSchema(contract.input, { target }) ?? {\n type: \"object\",\n properties: {},\n };\n\n return {\n name: contract.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 private async callTool(params: unknown): 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 result = await contract.invoke(args);\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 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(id: JsonRpcRequest[\"id\"], code: number, message: string): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, error: { code, message } };\n }\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 * 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 * @param source - The tools to expose (an object with `tools()` or a literal array).\n * @param options - Serve options (name / version / schema target).\n * @returns An object whose `handle(request)` maps a request to a response.\n */\nexport function createServeHandler(\n source: McpServeSource,\n options: McpServeOptions,\n): { handle(request: JsonRpcRequest): Promise<JsonRpcResponse> } {\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`; the\n * `http` transport is accepted but listening is deferred to the host\n * (a serve-over-HTTP needs a server the caller owns).\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 stdin line reader while serving over stdio. */\n private reader: Interface | undefined;\n\n /** Flipped while the server is actively reading the transport. */\n private running = false;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.handler = new McpServeHandler(source, options);\n this.options = options;\n }\n\n public async start(): Promise<void> {\n if (this.running) {\n return;\n }\n\n const transport = this.options.transport ?? { type: \"stdio\" };\n\n if (transport.type !== \"stdio\") {\n throw new McpTransportError(\n \"serve() over http requires a host-provided server; only stdio is auto-pumped.\",\n { type: \"connect\" },\n );\n }\n\n this.running = true;\n this.reader = createInterface({ input: process.stdin });\n\n this.reader.on(\"line\", (line) => {\n void this.onLine(line);\n });\n }\n\n /**\n * Parse one stdin line as a JSON-RPC request, dispatch it through the\n * handler, and write the response as a single line to stdout. Non-JSON\n * lines and notifications (no `id`) are ignored.\n */\n private async onLine(line: string): Promise<void> {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return;\n }\n\n let request: JsonRpcRequest;\n\n try {\n request = JSON.parse(trimmed) as JsonRpcRequest;\n } catch {\n return;\n }\n\n if (request.id === undefined || request.id === null) {\n // A notification (e.g. notifications/initialized) — nothing to answer.\n return;\n }\n\n const response = await this.handler.handle(request);\n process.stdout.write(`${JSON.stringify(response)}\\n`);\n }\n\n public async stop(): Promise<void> {\n this.running = false;\n this.reader?.close();\n this.reader = undefined;\n }\n}\n\n/**\n * Expose a built agent / supervisor / orchestrator (or a raw\n * `ToolContract[]`) AS an MCP server (Direction B: local primitive → MCP\n * server other clients consume).\n *\n * Enumerates `source.tools()` (or the literal array) once at construction.\n * `tools/list` answers with each tool's `inputSchema` extracted via\n * `extractJsonSchema` at the configured `schemaTarget` (default\n * `draft-2020-12`). `tools/call` routes to the named contract's\n * `invoke()` and maps the never-throwing result — `data` becomes a text\n * content block, `error` becomes an `isError: true` result — so a failing\n * tool surfaces as a normal MCP tool error rather than crashing the server.\n *\n * The default transport is `stdio`, pumped over `process.stdin` /\n * `process.stdout`. Serving over HTTP is left to a host-owned server;\n * `start()` rejects an `http` transport (the protocol core is available\n * via {@link createServeHandler} for a caller's own HTTP wiring).\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, and schema dialect.\n * @returns An {@link McpServer} with `start()` / `stop()`.\n *\n * @example\n * serve(\n * { tools: () => ws.allTools() },\n * { name: \"warlock-workspace\", transport: { type: \"stdio\" } },\n * ).start();\n */\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer {\n return new McpServerImpl(source, options);\n}\n"],"mappings":"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"}
|
|
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\";\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(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 ...(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: 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 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>,\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":"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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 {\n JsonRpcId,\n JsonRpcRequest,\n JsonRpcResponse,\n McpTransport,\n} 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(\n `MCP request \"${method}\" timed out after ${timeoutMs}ms.`,\n { type: \"timeout\", 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(\n new McpTransportError(`MCP stdio transport failed: ${reason}.`, { type }),\n );\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, () =>\n this.pending.delete(id),\n );\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\")\n ? extractSseData(raw)\n : raw;\n\n if (!jsonText) {\n throw new McpTransportError(\n `MCP http transport got an empty response for \"${method}\".`,\n { type: \"protocol\", method },\n );\n }\n\n try {\n return JSON.parse(jsonText) as JsonRpcResponse<TResult>;\n } catch (cause) {\n throw new McpTransportError(\n `MCP http transport got a non-JSON response for \"${method}\".`,\n { type: \"protocol\", method, 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 { type: \"protocol\", method, context: { code: response.error.code }, cause: response.error.data },\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(\n source: McpTransport | McpTransportClient,\n): 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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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JsonRpcRequest, JsonRpcResponse } from "../contracts/mcp.type.mjs";
|
|
2
|
+
import "../contracts/index.mjs";
|
|
2
3
|
//#region ../../packages/ai-tools/src/mcp/transport.type.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Internal contract every concrete MCP transport (stdio / Streamable HTTP)
|
|
@@ -14,7 +15,7 @@ import { JsonRpcRequest, JsonRpcResponse } from "../contracts/mcp.type.mjs";
|
|
|
14
15
|
* **`type`, never `kind`.** This package's discriminators are all `type`;
|
|
15
16
|
* any inbound MCP `kind` is translated at the client boundary.
|
|
16
17
|
*/
|
|
17
|
-
interface McpTransportClient {
|
|
18
|
+
export interface McpTransportClient {
|
|
18
19
|
/**
|
|
19
20
|
* Send a JSON-RPC request and resolve with its matching response.
|
|
20
21
|
*
|
|
@@ -47,5 +48,4 @@ interface McpTransportClient {
|
|
|
47
48
|
close(): Promise<void>;
|
|
48
49
|
}
|
|
49
50
|
//#endregion
|
|
50
|
-
export { McpTransportClient };
|
|
51
51
|
//# sourceMappingURL=transport.type.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.type.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"transport.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/mcp/transport.type.ts"],"mappings":";;;;;;;;;;;;;;;;;iBAgBiB;;;;;;;;;;;;;EAaf,QAAQ,mBACN,SAAS,gBACT;IAAY,SAAS;IAAa;MACjC,QAAQ,gBAAgB;;;;;;;;EAS3B,OAAO,gBAAgB,mBAAmB;;;;;;EAO1C,SAAS"}
|
package/esm/register.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FetchUrlInput, FetchUrlOptions, FetchUrlResult, WebSearchInput, WebSearchOptions, WebSearchResult } from "./contracts/web.type.mjs";
|
|
2
2
|
import { HttpRequestInput, HttpRequestOptions, HttpRequestResult } from "./contracts/http.type.mjs";
|
|
3
3
|
import { CalculatorInput, CalculatorOptions, CalculatorResult, DateTimeInput, DateTimeOptions, DateTimeResult } from "./contracts/utility.type.mjs";
|
|
4
|
+
import "./contracts/index.mjs";
|
|
4
5
|
import { McpFactory } from "./mcp/index.mjs";
|
|
5
|
-
import { ToolContract } from "@warlock.js/ai";
|
|
6
|
-
|
|
6
|
+
import { ToolContract, measureTools } from "@warlock.js/ai";
|
|
7
7
|
//#region ../../packages/ai-tools/src/register.d.ts
|
|
8
8
|
/**
|
|
9
9
|
* The `ai.tools.*` namespace — the five ready-made agent tools this
|
|
@@ -13,7 +13,7 @@ import { ToolContract } from "@warlock.js/ai";
|
|
|
13
13
|
* across the tool sub-barrels, mirroring the `ai.workspace` augmentation in
|
|
14
14
|
* `@warlock.js/ai-workspace`.
|
|
15
15
|
*/
|
|
16
|
-
interface AiToolsNamespace {
|
|
16
|
+
export interface AiToolsNamespace {
|
|
17
17
|
/** Search the web via a chosen provider; returns ranked LLM-ready hits. */
|
|
18
18
|
webSearch(options: WebSearchOptions): ToolContract<WebSearchInput, WebSearchResult>;
|
|
19
19
|
/** Fetch a URL (host-allowlisted, byte-capped) and return its content. */
|
|
@@ -24,6 +24,17 @@ interface AiToolsNamespace {
|
|
|
24
24
|
calculator(options?: CalculatorOptions): ToolContract<CalculatorInput, CalculatorResult>;
|
|
25
25
|
/** Clock/calendar ops — now / add / diff / format over ISO instants. */
|
|
26
26
|
dateTime(options?: DateTimeOptions): ToolContract<DateTimeInput, DateTimeResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Measure the input tokens a tool list adds to every call — natively
|
|
29
|
+
* through the model's count endpoint when it has one, estimated
|
|
30
|
+
* otherwise (`source` says which), per tool and in total, in `"full"`
|
|
31
|
+
* or `"compact"` schema mode. Re-exports `measureTools` from
|
|
32
|
+
* `@warlock.js/ai`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const { tokens, perTool, source } = await ai.tools.measure(tools, { model, mode: "compact" });
|
|
36
|
+
*/
|
|
37
|
+
measure: typeof measureTools;
|
|
27
38
|
}
|
|
28
39
|
/**
|
|
29
40
|
* Attach the `tools` namespace and the `mcp` factory to the `ai` namespace
|
|
@@ -49,7 +60,6 @@ declare module "@warlock.js/ai" {
|
|
|
49
60
|
*/
|
|
50
61
|
mcp: McpFactory;
|
|
51
62
|
}
|
|
52
|
-
}
|
|
63
|
+
}
|
|
53
64
|
//#endregion
|
|
54
|
-
export { AiToolsNamespace };
|
|
55
65
|
//# sourceMappingURL=register.d.mts.map
|
package/esm/register.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.mts","names":[],"sources":["../../../../../../../packages/ai-tools/src/register.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"register.d.mts","names":[],"sources":["../../../../../../../packages/ai-tools/src/register.ts"],"mappings":";;;;;;;;;;;;;;;iBAiCiB;;EAEf,UAAU,SAAS,mBAAmB,aAAa,gBAAgB;;EAEnE,SAAS,UAAU,kBAAkB,aAAa,eAAe;;EAEjE,KAAK,UAAU,qBAAqB,aAAa,kBAAkB;;EAEnE,WAAW,UAAU,oBAAoB,aAAa,iBAAiB;;EAEvE,SAAS,UAAU,kBAAkB,aAAa,eAAe;;;;;;;;;;;EAWjE,gBAAgB;;;;;;;;;;;YAYN;;;;;;IAMR,OAAO;;;;;;;IAOP,KAAK"}
|
package/esm/register.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { calculatorTool } from "./utility/calculator.mjs";
|
|
|
4
4
|
import { dateTimeTool } from "./utility/date-time.mjs";
|
|
5
5
|
import { fetchUrlTool } from "./web/fetch-url.mjs";
|
|
6
6
|
import { webSearchTool } from "./web/web-search.mjs";
|
|
7
|
-
import { ai } from "@warlock.js/ai";
|
|
7
|
+
import { ai, measureTools } from "@warlock.js/ai";
|
|
8
8
|
|
|
9
9
|
//#region ../../packages/ai-tools/src/register.ts
|
|
10
10
|
ai.tools = {
|
|
@@ -12,7 +12,8 @@ ai.tools = {
|
|
|
12
12
|
fetchUrl: fetchUrlTool,
|
|
13
13
|
http: httpRequestTool,
|
|
14
14
|
calculator: calculatorTool,
|
|
15
|
-
dateTime: (options) => dateTimeTool(options)
|
|
15
|
+
dateTime: (options) => dateTimeTool(options),
|
|
16
|
+
measure: measureTools
|
|
16
17
|
};
|
|
17
18
|
ai.mcp = mcp;
|
|
18
19
|
|
package/esm/register.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.mjs","names":[],"sources":["../../../../../../../packages/ai-tools/src/register.ts"],"sourcesContent":["import { ai, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n CalculatorInput,\n CalculatorOptions,\n CalculatorResult,\n DateTimeInput,\n DateTimeOptions,\n DateTimeResult,\n FetchUrlInput,\n FetchUrlOptions,\n FetchUrlResult,\n HttpRequestInput,\n HttpRequestOptions,\n HttpRequestResult,\n WebSearchInput,\n WebSearchOptions,\n WebSearchResult,\n} from \"./contracts\";\nimport { httpRequestTool } from \"./http/http-request\";\nimport { type McpFactory, mcp } from \"./mcp\";\nimport { calculatorTool } from \"./utility/calculator\";\nimport { dateTimeTool } from \"./utility/date-time\";\nimport { fetchUrlTool } from \"./web/fetch-url\";\nimport { webSearchTool } from \"./web/web-search\";\n\n/**\n * The `ai.tools.*` namespace — the five ready-made agent tools this\n * package vends. Each member is a factory returning a {@link ToolContract}\n * that drops straight into `ai.agent({ tools: [...] })`. Declared inline on\n * the `Ai` interface below (one block, one shape) rather than accreted\n * across the tool sub-barrels, mirroring the `ai.workspace` augmentation in\n * `@warlock.js/ai-workspace`.\n */\nexport interface AiToolsNamespace {\n /** Search the web via a chosen provider; returns ranked LLM-ready hits. */\n webSearch(options: WebSearchOptions): ToolContract<WebSearchInput, WebSearchResult>;\n /** Fetch a URL (host-allowlisted, byte-capped) and return its content. */\n fetchUrl(options?: FetchUrlOptions): ToolContract<FetchUrlInput, FetchUrlResult>;\n /** A guarded HTTP/REST client — method + host allowlists, byte/timeout caps. */\n http(options?: HttpRequestOptions): ToolContract<HttpRequestInput, HttpRequestResult>;\n /** A SAFE arithmetic-expression evaluator (no `eval`/`Function`). */\n calculator(options?: CalculatorOptions): ToolContract<CalculatorInput, CalculatorResult>;\n /** Clock/calendar ops — now / add / diff / format over ISO instants. */\n dateTime(options?: DateTimeOptions): ToolContract<DateTimeInput, DateTimeResult>;\n}\n\n/**\n * Attach the `tools` namespace and the `mcp` factory to the `ai` namespace\n * via module augmentation, per the `ai.`-namespace convention.\n * `@warlock.js/ai` now exposes a named `Ai` interface for exactly this, so\n * after a bare `import \"@warlock.js/ai-tools\"`, `ai.tools.webSearch(...)`,\n * `ai.tools.http(...)`, `ai.mcp(server)`, and `ai.mcp.serve(source)` are all\n * globally typed — no view/cast needed.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /**\n * Ready-made agent tools from `@warlock.js/ai-tools` — web search,\n * fetch/scrape, HTTP/REST, calculator, and date-time. Each returns a\n * `ToolContract` that slots into `ai.agent({ tools: [...] })`.\n */\n tools: AiToolsNamespace;\n /**\n * The Model Context Protocol surface — `ai.mcp(server)` connects to an\n * external MCP server and adapts its tools as agent tools (Direction A),\n * while `ai.mcp.serve(source, options)` exposes a local primitive AS an\n * MCP server other clients can consume (Direction B).\n */\n mcp: McpFactory;\n }\n}\n\n// Runtime registration: attach `tools` and `mcp` onto the shared `ai`\n// object the moment this module is imported (the augmentation above types\n// them). The `dateTime` factory's optional injectable-clock `deps` is a\n// test/advanced seam, omitted from the agent-facing signature — call\n// `dateTimeTool` directly to use it.\nai.tools = {\n webSearch: webSearchTool,\n fetchUrl: fetchUrlTool,\n http: httpRequestTool,\n calculator: calculatorTool,\n dateTime: (options?: DateTimeOptions) => dateTimeTool(options),\n};\n\nai.mcp = mcp;\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"register.mjs","names":[],"sources":["../../../../../../../packages/ai-tools/src/register.ts"],"sourcesContent":["import { ai, measureTools, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n CalculatorInput,\n CalculatorOptions,\n CalculatorResult,\n DateTimeInput,\n DateTimeOptions,\n DateTimeResult,\n FetchUrlInput,\n FetchUrlOptions,\n FetchUrlResult,\n HttpRequestInput,\n HttpRequestOptions,\n HttpRequestResult,\n WebSearchInput,\n WebSearchOptions,\n WebSearchResult,\n} from \"./contracts\";\nimport { httpRequestTool } from \"./http/http-request\";\nimport { type McpFactory, mcp } from \"./mcp\";\nimport { calculatorTool } from \"./utility/calculator\";\nimport { dateTimeTool } from \"./utility/date-time\";\nimport { fetchUrlTool } from \"./web/fetch-url\";\nimport { webSearchTool } from \"./web/web-search\";\n\n/**\n * The `ai.tools.*` namespace — the five ready-made agent tools this\n * package vends. Each member is a factory returning a {@link ToolContract}\n * that drops straight into `ai.agent({ tools: [...] })`. Declared inline on\n * the `Ai` interface below (one block, one shape) rather than accreted\n * across the tool sub-barrels, mirroring the `ai.workspace` augmentation in\n * `@warlock.js/ai-workspace`.\n */\nexport interface AiToolsNamespace {\n /** Search the web via a chosen provider; returns ranked LLM-ready hits. */\n webSearch(options: WebSearchOptions): ToolContract<WebSearchInput, WebSearchResult>;\n /** Fetch a URL (host-allowlisted, byte-capped) and return its content. */\n fetchUrl(options?: FetchUrlOptions): ToolContract<FetchUrlInput, FetchUrlResult>;\n /** A guarded HTTP/REST client — method + host allowlists, byte/timeout caps. */\n http(options?: HttpRequestOptions): ToolContract<HttpRequestInput, HttpRequestResult>;\n /** A SAFE arithmetic-expression evaluator (no `eval`/`Function`). */\n calculator(options?: CalculatorOptions): ToolContract<CalculatorInput, CalculatorResult>;\n /** Clock/calendar ops — now / add / diff / format over ISO instants. */\n dateTime(options?: DateTimeOptions): ToolContract<DateTimeInput, DateTimeResult>;\n /**\n * Measure the input tokens a tool list adds to every call — natively\n * through the model's count endpoint when it has one, estimated\n * otherwise (`source` says which), per tool and in total, in `\"full\"`\n * or `\"compact\"` schema mode. Re-exports `measureTools` from\n * `@warlock.js/ai`.\n *\n * @example\n * const { tokens, perTool, source } = await ai.tools.measure(tools, { model, mode: \"compact\" });\n */\n measure: typeof measureTools;\n}\n\n/**\n * Attach the `tools` namespace and the `mcp` factory to the `ai` namespace\n * via module augmentation, per the `ai.`-namespace convention.\n * `@warlock.js/ai` now exposes a named `Ai` interface for exactly this, so\n * after a bare `import \"@warlock.js/ai-tools\"`, `ai.tools.webSearch(...)`,\n * `ai.tools.http(...)`, `ai.mcp(server)`, and `ai.mcp.serve(source)` are all\n * globally typed — no view/cast needed.\n */\ndeclare module \"@warlock.js/ai\" {\n interface Ai {\n /**\n * Ready-made agent tools from `@warlock.js/ai-tools` — web search,\n * fetch/scrape, HTTP/REST, calculator, and date-time. Each returns a\n * `ToolContract` that slots into `ai.agent({ tools: [...] })`.\n */\n tools: AiToolsNamespace;\n /**\n * The Model Context Protocol surface — `ai.mcp(server)` connects to an\n * external MCP server and adapts its tools as agent tools (Direction A),\n * while `ai.mcp.serve(source, options)` exposes a local primitive AS an\n * MCP server other clients can consume (Direction B).\n */\n mcp: McpFactory;\n }\n}\n\n// Runtime registration: attach `tools` and `mcp` onto the shared `ai`\n// object the moment this module is imported (the augmentation above types\n// them). The `dateTime` factory's optional injectable-clock `deps` is a\n// test/advanced seam, omitted from the agent-facing signature — call\n// `dateTimeTool` directly to use it.\nai.tools = {\n webSearch: webSearchTool,\n fetchUrl: fetchUrlTool,\n http: httpRequestTool,\n calculator: calculatorTool,\n dateTime: (options?: DateTimeOptions) => dateTimeTool(options),\n measure: measureTools,\n};\n\nai.mcp = mcp;\n"],"mappings":";;;;;;;;;AAwFA,GAAG,QAAQ;CACT,WAAW;CACX,UAAU;CACV,MAAM;CACN,YAAY;CACZ,WAAW,YAA8B,aAAa,OAAO;CAC7D,SAAS;AACX;AAEA,GAAG,MAAM"}
|
package/esm/schema.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../../../../packages/ai-tools/src/schema.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Tiny, dependency-free [Standard Schema](https://standardschema.dev)\n * builders for the tools' input validation. The package pins only\n * `@warlock.js/ai` as a required runtime peer and keeps every heavy\n * dependency optional, so rather than pull in a schema library we\n * hand-roll the few shapes the tools need — exactly the pattern\n * `@warlock.js/ai`'s own `tool()` tests use. Each builder returns a\n * `StandardSchemaV1`, which is what `tool({ input })` validates against\n * before calling `execute`.\n *\n * These intentionally cover only the primitive cases the bundled tools\n * require (`string`, an optional string drawn from a fixed set, an\n * optional string-to-string record, and an opaque passthrough value),\n * plus an `object` of fields. They are not a general-purpose validator.\n */\n\n/** The vendor tag stamped on every issue these builders produce. */\nconst VENDOR = \"ai-tools\";\n\n/**\n * A single field validator inside {@link objectSchema}: given a value,\n * return either the coerced value or a list of issues. Field validators\n * receive the raw property and the property name (for issue messages).\n */\nexport type FieldValidator<T> = (\n value: unknown,\n key: string,\n) => { value: T } | { issues: StandardSchemaV1.Issue[] };\n\n/** Required string field — rejects anything that is not a string. */\nexport function stringField(): FieldValidator<string> {\n return (value, key) => {\n if (typeof value === \"string\") {\n return { value };\n }\n\n return { issues: [{ message: `\"${key}\" must be a string`, path: [key] }] };\n };\n}\n\n/**\n * Optional string field constrained to a fixed set of allowed values.\n * Accepts `undefined` (the property absent or explicitly undefined) or\n * one of `allowed`; rejects any other string and every non-string type.\n * Used for `method` so an out-of-vocabulary verb is a clean validation\n * error rather than reaching the handler.\n *\n * @param allowed - The exhaustive set of permitted string literals.\n */\nexport function optionalStringEnumField<T extends string>(\n allowed: readonly T[],\n): FieldValidator<T | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"string\" && (allowed as readonly string[]).includes(value)) {\n return { value: value as T };\n }\n\n return {\n issues: [\n {\n message: `\"${key}\" must be one of: ${allowed.join(\", \")}`,\n path: [key],\n },\n ],\n };\n };\n}\n\n/**\n * Optional string-to-string record field — accepts `undefined` or a flat\n * object whose every value is a string, and rejects arrays, non-objects,\n * and any record with a non-string value. Used for per-call `headers`.\n */\nexport function optionalStringRecordField(): FieldValidator
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../../../../../../../packages/ai-tools/src/schema.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Tiny, dependency-free [Standard Schema](https://standardschema.dev)\n * builders for the tools' input validation. The package pins only\n * `@warlock.js/ai` as a required runtime peer and keeps every heavy\n * dependency optional, so rather than pull in a schema library we\n * hand-roll the few shapes the tools need — exactly the pattern\n * `@warlock.js/ai`'s own `tool()` tests use. Each builder returns a\n * `StandardSchemaV1`, which is what `tool({ input })` validates against\n * before calling `execute`.\n *\n * These intentionally cover only the primitive cases the bundled tools\n * require (`string`, an optional string drawn from a fixed set, an\n * optional string-to-string record, and an opaque passthrough value),\n * plus an `object` of fields. They are not a general-purpose validator.\n */\n\n/** The vendor tag stamped on every issue these builders produce. */\nconst VENDOR = \"ai-tools\";\n\n/**\n * A single field validator inside {@link objectSchema}: given a value,\n * return either the coerced value or a list of issues. Field validators\n * receive the raw property and the property name (for issue messages).\n */\nexport type FieldValidator<T> = (\n value: unknown,\n key: string,\n) => { value: T } | { issues: StandardSchemaV1.Issue[] };\n\n/** Required string field — rejects anything that is not a string. */\nexport function stringField(): FieldValidator<string> {\n return (value, key) => {\n if (typeof value === \"string\") {\n return { value };\n }\n\n return { issues: [{ message: `\"${key}\" must be a string`, path: [key] }] };\n };\n}\n\n/**\n * Optional string field constrained to a fixed set of allowed values.\n * Accepts `undefined` (the property absent or explicitly undefined) or\n * one of `allowed`; rejects any other string and every non-string type.\n * Used for `method` so an out-of-vocabulary verb is a clean validation\n * error rather than reaching the handler.\n *\n * @param allowed - The exhaustive set of permitted string literals.\n */\nexport function optionalStringEnumField<T extends string>(\n allowed: readonly T[],\n): FieldValidator<T | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value === \"string\" && (allowed as readonly string[]).includes(value)) {\n return { value: value as T };\n }\n\n return {\n issues: [\n {\n message: `\"${key}\" must be one of: ${allowed.join(\", \")}`,\n path: [key],\n },\n ],\n };\n };\n}\n\n/**\n * Optional string-to-string record field — accepts `undefined` or a flat\n * object whose every value is a string, and rejects arrays, non-objects,\n * and any record with a non-string value. Used for per-call `headers`.\n */\nexport function optionalStringRecordField(): FieldValidator<Record<string, string> | undefined> {\n return (value, key) => {\n if (value === undefined) {\n return { value: undefined };\n }\n\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return {\n issues: [\n { message: `\"${key}\" must be an object of string values when provided`, path: [key] },\n ],\n };\n }\n\n const source = value as Record<string, unknown>;\n const result: Record<string, string> = {};\n\n for (const recordKey of Object.keys(source)) {\n const entry = source[recordKey];\n\n if (typeof entry !== \"string\") {\n return {\n issues: [\n {\n message: `\"${key}.${recordKey}\" must be a string`,\n path: [key, recordKey],\n },\n ],\n };\n }\n\n result[recordKey] = entry;\n }\n\n return { value: result };\n };\n}\n\n/**\n * Opaque passthrough field — accepts any value (including `undefined`)\n * verbatim, performing no validation. Used for a request `body` whose\n * shape the tool deliberately does not constrain: an object is later\n * JSON-serialized, a string is sent as-is.\n */\nexport function passthroughField(): FieldValidator<unknown> {\n return (value) => ({ value });\n}\n\n/** The per-key field validator map describing an object schema's shape. */\ntype ObjectShape<T> = {\n [K in keyof T]-?: FieldValidator<T[K]>;\n};\n\n/**\n * Build a {@link StandardSchemaV1} for a flat object whose every property\n * is validated by a {@link FieldValidator}. The input must be a non-null\n * object; each declared field is validated and the (possibly coerced)\n * values are collected into the typed result. All field issues are merged\n * so the caller sees every problem at once.\n *\n * `T` is constrained to `object` rather than `Record<string, unknown>` so\n * the tool IO `interface`s (which carry no implicit string index\n * signature) satisfy it directly — only the declared keys in `shape` are\n * ever read, so a string index signature is never required.\n *\n * @example\n * const schema = objectSchema<{ url: string; method?: HttpMethod }>({\n * url: stringField(),\n * method: optionalStringEnumField([\"GET\", \"POST\"]),\n * });\n */\nexport function objectSchema<T extends object>(shape: ObjectShape<T>): StandardSchemaV1<T> {\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n validate(input) {\n if (typeof input !== \"object\" || input === null || Array.isArray(input)) {\n return { issues: [{ message: \"input must be an object\" }] };\n }\n\n const source = input as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n const result: Record<string, unknown> = {};\n\n for (const key of Object.keys(shape) as (keyof T)[]) {\n const field = shape[key];\n const present = (key as string) in source;\n const outcome = field(source[key as string], key as string);\n\n if (\"issues\" in outcome) {\n issues.push(...outcome.issues);\n\n continue;\n }\n\n // Carry through any key that resolved to a defined value. A\n // passthrough field can legitimately resolve to `undefined`\n // even when the property was present (e.g. an explicit\n // `body: undefined`); only suppress keys that were absent so\n // optional-absent fields stay absent.\n if (outcome.value !== undefined || present) {\n result[key as string] = outcome.value;\n }\n }\n\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value: result as T };\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,MAAM,SAAS;;AAaf,SAAgB,cAAsC;CACpD,QAAQ,OAAO,QAAQ;EACrB,IAAI,OAAO,UAAU,UACnB,OAAO,EAAE,MAAM;EAGjB,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS,IAAI,IAAI;GAAqB,MAAM,CAAC,GAAG;EAAE,CAAC,EAAE;CAC3E;AACF;;;;;;;;;;AAWA,SAAgB,wBACd,SAC+B;CAC/B,QAAQ,OAAO,QAAQ;EACrB,IAAI,UAAU,QACZ,OAAO,EAAE,OAAO,OAAU;EAG5B,IAAI,OAAO,UAAU,YAAa,QAA8B,SAAS,KAAK,GAC5E,OAAO,EAAS,MAAW;EAG7B,OAAO,EACL,QAAQ,CACN;GACE,SAAS,IAAI,IAAI,oBAAoB,QAAQ,KAAK,IAAI;GACtD,MAAM,CAAC,GAAG;EACZ,CACF,EACF;CACF;AACF;;;;;;AAOA,SAAgB,4BAAgF;CAC9F,QAAQ,OAAO,QAAQ;EACrB,IAAI,UAAU,QACZ,OAAO,EAAE,OAAO,OAAU;EAG5B,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,OAAO,EACL,QAAQ,CACN;GAAE,SAAS,IAAI,IAAI;GAAqD,MAAM,CAAC,GAAG;EAAE,CACtF,EACF;EAGF,MAAM,SAAS;EACf,MAAM,SAAiC,CAAC;EAExC,KAAK,MAAM,aAAa,OAAO,KAAK,MAAM,GAAG;GAC3C,MAAM,QAAQ,OAAO;GAErB,IAAI,OAAO,UAAU,UACnB,OAAO,EACL,QAAQ,CACN;IACE,SAAS,IAAI,IAAI,GAAG,UAAU;IAC9B,MAAM,CAAC,KAAK,SAAS;GACvB,CACF,EACF;GAGF,OAAO,aAAa;EACtB;EAEA,OAAO,EAAE,OAAO,OAAO;CACzB;AACF;;;;;;;AAQA,SAAgB,mBAA4C;CAC1D,QAAQ,WAAW,EAAE,MAAM;AAC7B;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,aAA+B,OAA4C;CACzF,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,SAAS,OAAO;GACd,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,0BAA0B,CAAC,EAAE;GAG5D,MAAM,SAAS;GACf,MAAM,SAAmC,CAAC;GAC1C,MAAM,SAAkC,CAAC;GAEzC,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GAAkB;IACnD,MAAM,QAAQ,MAAM;IACpB,MAAM,UAAW,OAAkB;IACnC,MAAM,UAAU,MAAM,OAAO,MAAgB,GAAa;IAE1D,IAAI,YAAY,SAAS;KACvB,OAAO,KAAK,GAAG,QAAQ,MAAM;KAE7B;IACF;IAOA,IAAI,QAAQ,UAAU,UAAa,SACjC,OAAO,OAAiB,QAAQ;GAEpC;GAEA,IAAI,OAAO,SAAS,GAClB,OAAO,EAAE,OAAO;GAGlB,OAAO,EAAE,OAAO,OAAY;EAC9B;CACF,EACF;AACF"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CalculatorInput, CalculatorOptions, CalculatorResult } from "../contracts/utility.type.mjs";
|
|
2
2
|
import { ToolContract } from "@warlock.js/ai";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/ai-tools/src/utility/calculator.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Build the `calculator` tool — a SAFE arithmetic evaluator the agent can
|
|
@@ -29,7 +28,6 @@ import { ToolContract } from "@warlock.js/ai";
|
|
|
29
28
|
* const { data } = await calc.invoke({ expression: "(3 + 4) * 2" });
|
|
30
29
|
* console.log(data?.result); // 14
|
|
31
30
|
*/
|
|
32
|
-
declare function calculatorTool(options?: CalculatorOptions): ToolContract<CalculatorInput, CalculatorResult>;
|
|
31
|
+
export declare function calculatorTool(options?: CalculatorOptions): ToolContract<CalculatorInput, CalculatorResult>;
|
|
33
32
|
//#endregion
|
|
34
|
-
export { calculatorTool };
|
|
35
33
|
//# sourceMappingURL=calculator.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calculator.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/utility/calculator.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"calculator.d.mts","names":[],"sources":["../../../../../../../../packages/ai-tools/src/utility/calculator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAoYgB,eACd,UAAU,oBACT,aAAa,iBAAiB"}
|