@routecraft/ai 0.2.0 → 0.3.0-canary.2
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/README.md +25 -20
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -25
- package/dist/index.d.ts +152 -25
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -17,21 +17,21 @@ pnpm add @routecraft/ai
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
import {
|
|
20
|
+
import { mcp } from '@routecraft/ai';
|
|
21
21
|
import { craft, simple } from '@routecraft/routecraft';
|
|
22
22
|
|
|
23
23
|
craft()
|
|
24
24
|
.from(simple({ query: 'hello' }))
|
|
25
|
-
.to(
|
|
25
|
+
.to(mcp('my-tool'));
|
|
26
26
|
|
|
27
27
|
craft()
|
|
28
|
-
.from(
|
|
28
|
+
.from(mcp('my-tool'))
|
|
29
29
|
.process((body) => body);
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## Features
|
|
33
33
|
|
|
34
|
-
- **
|
|
34
|
+
- **mcp()**: Alias for `direct()` with semantics for AI/MCP—discoverable routes with optional schema and description
|
|
35
35
|
- **Discovery**: Tools register in the context store for querying endpoints, descriptions, and schemas
|
|
36
36
|
- **Schema validation**: Use Zod (or other Standard Schema libs) for body and header validation on tools
|
|
37
37
|
- **Coming soon**: LLM adapters (OpenAI, Gemini), MCP source/destination, agent routing
|
|
@@ -72,26 +72,31 @@ For comprehensive documentation, examples, and guides, visit [routecraft.dev](ht
|
|
|
72
72
|
## Example
|
|
73
73
|
|
|
74
74
|
```typescript
|
|
75
|
-
import {
|
|
75
|
+
import { mcp } from '@routecraft/ai';
|
|
76
76
|
import { craft, context, DirectAdapter } from '@routecraft/routecraft';
|
|
77
77
|
import { z } from 'zod';
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
const ctx = context()
|
|
80
|
+
.routes([
|
|
81
|
+
craft()
|
|
82
|
+
.from(
|
|
83
|
+
mcp('fetch-webpage', {
|
|
84
|
+
description: 'Fetch and return the content of a webpage',
|
|
85
|
+
schema: z.object({
|
|
86
|
+
url: z.string().url(),
|
|
87
|
+
}),
|
|
88
|
+
keywords: ['fetch', 'web', 'http'],
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
.transform(async (body) => {
|
|
92
|
+
const response = await fetch(body.url);
|
|
93
|
+
return { content: await response.text() };
|
|
85
94
|
}),
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return { content: await response.text() };
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// After context.start(), query registered tools
|
|
95
|
+
])
|
|
96
|
+
.build();
|
|
97
|
+
await ctx.start();
|
|
98
|
+
|
|
99
|
+
// Query registered tools after context has started
|
|
95
100
|
const registry = ctx.getStore(DirectAdapter.ADAPTER_DIRECT_REGISTRY);
|
|
96
101
|
const tools = Array.from(registry?.values() ?? []);
|
|
97
102
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var routecraft=require('@routecraft/routecraft');function f(s,t){if(t!==void 0){if(typeof
|
|
2
|
-
exports.MCPServer=
|
|
1
|
+
'use strict';var routecraft=require('@routecraft/routecraft');var c={McpSourceAdapter:Symbol.for("routecraft.ai.McpSourceAdapter"),McpClientAdapter:Symbol.for("routecraft.ai.McpClientAdapter"),MCPAdapter:Symbol.for("routecraft.ai.MCPAdapter")};function S(e,t){return typeof e=="object"&&e!==null&&e[t]===true}function R(e){return S(e,c.McpSourceAdapter)}function T(e){return S(e,c.McpClientAdapter)}function E(e){return S(e,c.MCPAdapter)}function H(e){return R(e)||T(e)||E(e)}var g="routecraft.mcp.client.servers";function q(e,t){if(e.url)return e.url;if(e.serverId&&!t)throw new Error(`MCP client: serverId "${e.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${g}" can be read.`);if(e.serverId&&t){let r=t.getStore(g)?.get(e.serverId);if(!r)throw new Error(`MCP client: serverId "${e.serverId}" not found in context store. Register it with context store key "${g}".`);return typeof r=="string"?r:r.url}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var b=e=>typeof e.body=="object"&&e.body!==null?e.body:{input:e.body},f=class{constructor(t){this.options=t;this[c.McpClientAdapter]=true;}adapterId="routecraft.adapter.mcp.client";async send(t){let o=routecraft.getExchangeContext(t),r=q(this.options,o),n=this.options.tool??(typeof t.body=="object"&&t.body!==null&&"tool"in t.body&&typeof t.body.tool=="string"?t.body.tool:void 0);if(!n)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let i=(this.options.args??b)(t);return await this.callRemoteTool(r,n,i)}async callRemoteTool(t,o,r){let s=(await import('@modelcontextprotocol/sdk/client/index.js')).Client,a=(await import('@modelcontextprotocol/sdk/client/streamableHttp.js')).StreamableHTTPClientTransport,w=new URL(t),l=new a(w),_={name:"routecraft-mcp-client",version:"1.0.0"},u=new s(_,{capabilities:{}});try{await u.connect.call(u,l);let v=await u.callTool.call(u,{name:o,arguments:r}),d=v?.content;if(Array.isArray(d)&&d.length>0){let p=d[0];if(p&&typeof p=="object"&&"text"in p)return p.text;if(p&&typeof p=="object"&&"data"in p)return p.data}return v}finally{let C=u,P=C.close??C.disconnect;if(typeof P=="function")try{await Promise.resolve(P.call(u));}catch{}let v=l,d=v.close??v.destroy;if(typeof d=="function")try{await Promise.resolve(d.call(l));}catch{}}}};var m=class extends routecraft.DirectAdapter{adapterId="routecraft.adapter.mcp.direct";constructor(...t){super(...t),this[c.MCPAdapter]=true;}};var h="routecraft.mcp.plugin.registered";var M=class{constructor(t,o){this.endpoint=t;this.options=o;this[c.McpSourceAdapter]=true;}adapterId="routecraft.adapter.mcp.source";async subscribe(t,o,r,n){if(t.getStore(h)!==true)throw new Error("MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].");return routecraft.direct(this.endpoint,this.options).subscribe(t,o,r,n)}};function k(e,t){if(typeof e=="object"&&e!==null&&("url"in e||"serverId"in e))return new f(e);let o=t===void 0||typeof t=="object"&&t!==null&&!("description"in t);if(typeof e=="string"&&e.includes(":")&&o){let n=e.indexOf(":"),s=e.slice(0,n),i=e.slice(n+1),a={serverId:s,tool:i};return t!==void 0&&typeof t=="object"&&"args"in t&&t.args!==void 0&&(a.args=t.args),new f(a)}let r=e;if(t!==void 0){if(typeof r!="string")throw routecraft.error("RC5010",void 0,{message:"Dynamic endpoints cannot be used as source",suggestion:"Use a static string endpoint for source: .from(mcp('endpoint', options))."});if("url"in t||"serverId"in t)throw routecraft.error("RC5010",void 0,{message:"mcp() with url or serverId must be used as destination: .to(mcp({ url, tool }))",suggestion:"Use .to(mcp({ url: '...', tool: '...' })) to call a remote MCP server."});if("args"in t&&t.args!==void 0&&!("description"in t))throw routecraft.error("RC5010",void 0,{message:"mcp(endpoint, { args }) is for client usage with a 'server:tool' target, not for defining a source",suggestion:"Use .to(mcp('server:tool', { args })) to call a remote tool, or .from(mcp('endpoint', { description: '...' })) to define a source."});return new M(r,t)}return new m(r)}var y=class{context;options;server=null;transport=null;running=false;toolsListLogged=false;constructor(t,o={}){this.context=t,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...o};}async start(){if(this.running){this.context.logger.warn("MCP server already running");return}try{let t=this.options.transport;t==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info(`MCP server started (${this.options.name}@${this.options.version}) on ${t}`),this.logExposedToolsOnce();}catch(t){throw this.context.logger.error(t,"Failed to start MCP server"),t}}async startStdio(){let{Server:t}=await import('@modelcontextprotocol/sdk/server/index.js'),{StdioServerTransport:o}=await import('@modelcontextprotocol/sdk/server/stdio.js');this.server=new t({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers(),this.transport=new o,await this.server.connect(this.transport);}async startHttp(){let t=await import('@modelcontextprotocol/sdk/server/index.js'),{Server:o}=t,r=t.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");this.server=new o({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers();let n=this.options.port,s=this.options.host,i=r;this.transport=new i({port:n,host:s}),await this.server.connect(this.transport),this.context.logger.info(`MCP HTTP server listening on http://${s}:${n}`);}async setupRequestHandlers(){let o=await import('@modelcontextprotocol/sdk/types.js'),r=o.ListToolsRequestSchema,n=o.CallToolRequestSchema;if(!r||!n)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let s=this.server;s.setRequestHandler(r,async()=>{let i=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:i}}),s.setRequestHandler(n,async i=>{let w=i.params;return await this.handleToolCall(w.name||"",w.arguments||{})});}async stop(){if(this.running)try{this.transport&&await this.transport.close(),this.running=!1,this.context.logger.info("MCP server stopped");}catch(t){this.context.logger.error(t,"Error stopping MCP server");}}logExposedToolsOnce(){if(this.toolsListLogged)return;let t=this.getAvailableTools();if(t.length===0)return;let o=t.map(r=>r.name??"?");this.context.logger.info({tools:o},`Exposing ${t.length} MCP tool(s): ${o.join(", ")}`),this.toolsListLogged=true;}getAvailableTools(){let t=this.context.getStore(routecraft.DirectAdapter.ADAPTER_DIRECT_REGISTRY);if(!t)return [];let o=Array.from(t.values()).filter(n=>n.description!==void 0),r=this.options.tools;if(r)if(Array.isArray(r)){let n=new Set(r);o=o.filter(s=>n.has(s.endpoint));}else typeof r=="function"&&(o=o.filter(r));return o.map(n=>this.metadataToMCPTool(n))}metadataToMCPTool(t){return {name:t.endpoint,description:t.description||"",inputSchema:this.schemaToJsonSchema(t.schema)}}schemaToJsonSchema(t){if(!t)return {type:"object"};if(typeof t=="object"&&t!==null&&"_def"in t&&typeof t.toJsonSchema=="function")try{let r=t;return r.toJsonSchema.call(r)}catch(r){return this.context.logger.debug(r,"Failed to convert schema to JSON Schema"),{type:"object"}}return typeof t=="object"&&t!==null&&"~standard"in t?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(t,o){try{let r=this.context.getStore(routecraft.DirectAdapter.ADAPTER_DIRECT_STORE);if(!r){let l=new Error("No direct channels available");return this.context.emit("error",{error:l}),{content:[{type:"text",text:"Error: No direct channels available"}]}}let n=r.get(t);if(!n){let l=new Error(`Tool not found: ${t}`);return this.context.emit("error",{error:l}),{content:[{type:"text",text:`Error: Tool not found: ${t}`}]}}let s=new routecraft.DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":t,"routecraft.mcp.session":`mcp-${Date.now()}`}}),a=await n.send(t,s);return {content:[{type:"text",text:typeof a.body=="string"?a.body:JSON.stringify(a.body)}]}}catch(r){let n=r instanceof Error?r.message:String(r);return this.context.logger.error(r,`Tool call failed: ${t}`),this.context.emit("error",{error:r}),{content:[{type:"text",text:`Error: ${n}`}]}}}};function D(e){if(e.transport==="http"){if(e.port!==void 0){if(typeof e.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(e.port<0||e.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(e.host!==void 0&&typeof e.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}}async function O(e,t){let o=t["~standard"];if(!o?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let r=o.validate(e);if(r instanceof Promise&&(r=await r),r.issues&&r.issues.length>0)throw new Error(`mcpPlugin options validation failed: ${JSON.stringify(r.issues)}`);return r.value}function I(e={}){return D(e),t=>{if(t.setStore(h,true),e.clients&&Object.keys(e.clients).length>0){let r=new Map(Object.entries(e.clients));t.setStore(g,r);}let o=null;t.on("contextStarted",async()=>{o=new y(t,e);try{await o.start();}catch(r){throw t.logger.error(r,"Failed to start MCP server plugin"),r}}),t.on("contextStopping",async()=>{if(o)try{await o.stop();}catch(r){t.logger.error(r,"Error stopping MCP server plugin");}});}}
|
|
2
|
+
exports.ADAPTER_MCP_CLIENT_SERVERS=g;exports.BRAND=c;exports.MCPAdapter=m;exports.MCPServer=y;exports.MCP_PLUGIN_REGISTERED=h;exports.McpClientAdapter=f;exports.defaultArgs=b;exports.isMcpAdapter=H;exports.isMcpClientAdapter=T;exports.isMcpDirectAdapter=E;exports.isMcpSourceAdapter=R;exports.mcp=k;exports.mcpPlugin=I;exports.validateWithSchema=O;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dsl.ts","../src/mcp/server.ts","../src/mcp/plugin.ts"],"names":["tool","endpoint","options","rcError","direct","MCPServer","context","transport","error","Server","StdioServerTransport","serverModule","StreamableHTTPServerTransport","port","host","TransportClass","t","ListToolsRequestSchema","CallToolRequestSchema","srv","request","params","registry","DirectAdapter","tools","toolsFilter","allowed","meta","metadata","schema","schemaWithMethod","toolName","args","channelStore","err","channel","exchange","DefaultExchange","resultExchange","message","plugin","ctx","server"],"mappings":"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","file":"index.cjs","sourcesContent":["import {\n direct,\n error as rcError,\n type DirectDestinationOptions,\n type DirectSourceOptions,\n type Exchange,\n type Source,\n type Destination,\n} from \"@routecraft/routecraft\";\n\n/**\n * Options for tool() when used as a Source in .from().\n * Description is required for AI/MCP discoverability.\n */\nexport interface ToolSourceOptions extends DirectSourceOptions {\n /** Human-readable description (required for tools). */\n description: string;\n}\n\n/**\n * Options for tool() when used as a Destination in .to().\n */\nexport type ToolDestinationOptions = DirectDestinationOptions;\n\nexport type ToolOptions = ToolSourceOptions;\n\n/**\n * Create a tool - a discoverable direct route for AI/MCP integration.\n *\n * `tool()` is an alias for `direct()` with semantics oriented toward AI use cases.\n * Same two-argument pattern: tool(endpoint, options) for source, tool(endpoint) for destination.\n */\nexport function tool<T = unknown>(\n endpoint: string,\n options: ToolSourceOptions,\n): Source<T>;\nexport function tool<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n): Destination<T, T>;\nexport function tool<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n options?: ToolSourceOptions | ToolDestinationOptions,\n): Source<T> | Destination<T, T> {\n if (options !== undefined) {\n if (typeof endpoint !== \"string\") {\n throw rcError(\"RC5010\", undefined, {\n message: \"Dynamic endpoints cannot be used as source\",\n suggestion:\n \"Use a static string endpoint for source: .from(tool('endpoint', options)).\",\n });\n }\n return direct<T>(endpoint, options);\n }\n return direct<T>(endpoint);\n}\n","import type { CraftContext, DirectRouteMetadata } from \"@routecraft/routecraft\";\nimport { DirectAdapter, DefaultExchange } from \"@routecraft/routecraft\";\nimport type { MCPServerOptions } from \"./types.ts\";\n\n/** Resolved options with defaults applied (internal use). */\ntype MCPServerResolvedOptions = Required<\n Pick<MCPServerOptions, \"name\" | \"version\" | \"transport\" | \"port\" | \"host\">\n> &\n Pick<MCPServerOptions, \"tools\">;\n\n/**\n * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.\n * It reads the tool registry lazily (on first tools/list request) to ensure routes have subscribed.\n *\n * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.\n * Supports both stdio and streamable-http transports.\n */\nexport class MCPServer {\n private context: CraftContext;\n private options: MCPServerResolvedOptions;\n private server: unknown = null;\n private transport: unknown = null;\n private running = false;\n\n constructor(context: CraftContext, options: MCPServerOptions = {}) {\n this.context = context;\n this.options = {\n name: \"routecraft\",\n version: \"1.0.0\",\n transport: \"stdio\",\n port: 3001,\n host: \"localhost\",\n ...options,\n };\n }\n\n /**\n * Start the MCP server and listen for connections\n */\n async start(): Promise<void> {\n if (this.running) {\n this.context.logger.warn(\"MCP server already running\");\n return;\n }\n\n try {\n const transport = this.options.transport;\n\n if (transport === \"http\") {\n await this.startHttp();\n } else {\n await this.startStdio();\n }\n\n this.running = true;\n this.context.logger.info(\n `MCP server started (${this.options.name}@${this.options.version}) on ${transport}`,\n );\n } catch (error) {\n this.context.logger.error(error, \"Failed to start MCP server\");\n throw error;\n }\n }\n\n /**\n * Start stdio transport\n */\n private async startStdio(): Promise<void> {\n // Dynamically import SDK to avoid TypeScript compatibility issues\n const { Server } =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { StdioServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n this.transport = new StdioServerTransport();\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n }\n\n /**\n * Start HTTP transport (streamable-http)\n */\n private async startHttp(): Promise<void> {\n // Dynamically import SDK\n const serverModule =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { Server } = serverModule;\n\n // Try to get StreamableHTTPServerTransport from the SDK\n // The MCP SDK exports this from the main server module\n const StreamableHTTPServerTransport: unknown = (\n serverModule as Record<string, unknown>\n )[\"StreamableHTTPServerTransport\"];\n\n if (!StreamableHTTPServerTransport) {\n throw new Error(\n \"StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed\",\n );\n }\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n const port = this.options.port;\n const host = this.options.host;\n\n // Create HTTP transport with port and host\n const TransportClass = StreamableHTTPServerTransport as {\n new (options: { port: number; host?: string }): unknown;\n };\n this.transport = new TransportClass({ port, host });\n\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n\n this.context.logger.info(\n `MCP HTTP server listening on http://${host}:${port}`,\n );\n }\n\n /**\n * Set up request handlers (shared by both transports).\n * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).\n */\n private async setupRequestHandlers(): Promise<void> {\n const typesModule = await import(\"@modelcontextprotocol/sdk/types.js\");\n const t = typesModule as Record<string, unknown>;\n const ListToolsRequestSchema = t[\"ListToolsRequestSchema\"];\n const CallToolRequestSchema = t[\"CallToolRequestSchema\"];\n\n if (!ListToolsRequestSchema || !CallToolRequestSchema) {\n throw new Error(\n \"MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed\",\n );\n }\n\n const srv = this.server as Record<\n string,\n (schema: unknown, handler: (request: unknown) => Promise<unknown>) => void\n >;\n\n srv[\"setRequestHandler\"](ListToolsRequestSchema, async () => {\n return {\n tools: this.getAvailableTools(),\n };\n });\n\n srv[\"setRequestHandler\"](\n CallToolRequestSchema,\n async (request: unknown) => {\n const req = request as Record<string, unknown>;\n const params = req[\"params\"] as Record<string, unknown>;\n return await this.handleToolCall(\n (params[\"name\"] as string) || \"\",\n (params[\"arguments\"] as Record<string, unknown>) || {},\n );\n },\n );\n }\n\n /**\n * Stop the MCP server\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n try {\n if (this.transport) {\n const tr = this.transport as Record<string, () => Promise<void>>;\n await tr[\"close\"]();\n }\n this.running = false;\n this.context.logger.info(\"MCP server stopped\");\n } catch (error) {\n this.context.logger.error(error, \"Error stopping MCP server\");\n }\n }\n\n /**\n * Get list of tools that should be exposed via MCP.\n * Reads the registry lazily - called on every tools/list request and for tests.\n */\n getAvailableTools(): Array<Record<string, unknown>> {\n const registry = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_REGISTRY,\n ) as Map<string, DirectRouteMetadata> | undefined;\n\n if (!registry) {\n return [];\n }\n\n // Get all tool routes (those with description)\n let tools = Array.from(registry.values()).filter(\n (t) => t.description !== undefined,\n );\n\n // Apply user filter if provided\n const toolsFilter = this.options.tools;\n if (toolsFilter) {\n if (Array.isArray(toolsFilter)) {\n const allowed = new Set(toolsFilter);\n tools = tools.filter((t) => allowed.has(t.endpoint));\n } else if (typeof toolsFilter === \"function\") {\n tools = tools.filter(toolsFilter);\n }\n }\n\n // Convert to MCP tool format\n return tools.map((meta) => this.metadataToMCPTool(meta));\n }\n\n /**\n * Convert RouteCraft tool metadata to MCP tool format\n */\n private metadataToMCPTool(\n metadata: DirectRouteMetadata,\n ): Record<string, unknown> {\n return {\n name: metadata.endpoint,\n description: metadata.description || \"\",\n inputSchema: this.schemaToJsonSchema(metadata.schema),\n };\n }\n\n /**\n * Convert StandardSchema to JSON Schema\n */\n private schemaToJsonSchema(schema: unknown): Record<string, unknown> {\n if (!schema) {\n return { type: \"object\" };\n }\n\n // Check for Zod 4 toJsonSchema method\n if (typeof schema === \"object\" && schema !== null && \"_def\" in schema) {\n const schemaObj = schema as Record<string, unknown>;\n if (typeof schemaObj[\"toJsonSchema\"] === \"function\") {\n try {\n const schemaWithMethod = schema as Record<string, unknown>;\n const toJsonSchema = schemaWithMethod[\"toJsonSchema\"] as (\n this: unknown,\n ) => Record<string, unknown>;\n return toJsonSchema.call(schemaWithMethod);\n } catch (error) {\n this.context.logger.debug(\n error,\n \"Failed to convert schema to JSON Schema\",\n );\n return { type: \"object\" };\n }\n }\n }\n\n // Check for standard-schema validate method and try to extract info\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema\n ) {\n // For now, return generic object schema for standard-schema\n // In the future, we could enhance this based on the schema library\n return { type: \"object\", additionalProperties: true };\n }\n\n return { type: \"object\" };\n }\n\n /**\n * Handle a tool call from MCP client\n */\n private async handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n // Get the direct channel store\n const channelStore = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_STORE,\n ) as Map<string, Record<string, unknown>> | undefined;\n\n if (!channelStore) {\n const err = new Error(\"No direct channels available\");\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: No direct channels available` },\n ],\n };\n }\n\n // Get the channel for this tool endpoint\n const channel = channelStore.get(toolName);\n if (!channel) {\n const err = new Error(`Tool not found: ${toolName}`);\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: Tool not found: ${toolName}` },\n ],\n };\n }\n\n // Create an exchange with the tool arguments\n const exchange = new DefaultExchange(this.context, {\n body: args,\n headers: {\n \"routecraft.mcp.tool\": toolName,\n \"routecraft.mcp.session\": `mcp-${Date.now()}`,\n },\n });\n\n // Send the exchange through the direct channel\n const channelTyped = channel as Record<\n string,\n (name: string, ex: unknown) => Promise<unknown>\n >;\n const resultExchange = (await channelTyped[\"send\"](\n toolName,\n exchange,\n )) as Record<string, unknown>;\n\n // Convert result to MCP format\n const resultText =\n typeof resultExchange[\"body\"] === \"string\"\n ? (resultExchange[\"body\"] as string)\n : JSON.stringify(resultExchange[\"body\"]);\n\n return {\n content: [{ type: \"text\", text: resultText }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.context.logger.error(error, `Tool call failed: ${toolName}`);\n this.context.emit(\"error\", { error });\n return {\n content: [{ type: \"text\", text: `Error: ${message}` }],\n };\n }\n }\n}\n","import type { CraftContext, CraftPlugin } from \"@routecraft/routecraft\";\nimport { MCPServer } from \"./server.ts\";\n\nexport function plugin(options: Record<string, unknown> = {}): CraftPlugin {\n return (ctx: CraftContext) => {\n let server: MCPServer | null = null;\n\n ctx.on(\"contextStarted\", async () => {\n server = new MCPServer(ctx, options);\n try {\n await server.start();\n } catch (error) {\n ctx.logger.error(error, \"Failed to start MCP server plugin\");\n throw error;\n }\n });\n\n ctx.on(\"contextStopping\", async () => {\n if (server) {\n try {\n await server.stop();\n } catch (error) {\n ctx.logger.error(error, \"Error stopping MCP server plugin\");\n }\n }\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/brand.ts","../src/mcp/client-adapter.ts","../src/mcp/mcp-adapter.ts","../src/mcp/types.ts","../src/mcp/source-adapter.ts","../src/mcp/mcp.ts","../src/mcp/server.ts","../src/mcp/validate-options.ts","../src/mcp/plugin.ts"],"names":["BRAND","isBranded","obj","key","isMcpSourceAdapter","isMcpClientAdapter","isMcpDirectAdapter","isMcpAdapter","ADAPTER_MCP_CLIENT_SERVERS","resolveUrl","options","context","config","defaultArgs","exchange","McpClientAdapter","getExchangeContext","url","toolName","args","serverUrl","Client","StreamableHTTPClientTransport","transport","clientInfo","client","response","content","first","clientCleanup","closeOrDisconnect","transportCleanup","closeOrDestroy","MCPAdapter","DirectAdapter","MCP_PLUGIN_REGISTERED","McpSourceAdapter","endpoint","handler","abortController","onReady","direct","mcp","endpointOrOptions","isClientColonOptions","colonIndex","serverId","tool","clientOptions","rcError","MCPServer","error","Server","StdioServerTransport","serverModule","StreamableHTTPServerTransport","port","host","TransportClass","t","ListToolsRequestSchema","CallToolRequestSchema","srv","tools","request","params","names","registry","toolsFilter","allowed","meta","metadata","schema","schemaWithMethod","channelStore","err","channel","DefaultExchange","resultExchange","message","validateMcpPluginOptions","validateWithSchema","standard","result","mcpPlugin","ctx","map","server"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Cross-instance identity for @routecraft/ai: Symbol.for() keys and type guards.\n * Shared across all copies of @routecraft/ai (and multiple routecraft versions) in a process.\n */\n\nexport const BRAND = {\n McpSourceAdapter: Symbol.for(\"routecraft.ai.McpSourceAdapter\"),\n McpClientAdapter: Symbol.for(\"routecraft.ai.McpClientAdapter\"),\n MCPAdapter: Symbol.for(\"routecraft.ai.MCPAdapter\"),\n} as const;\n\nfunction isBranded(obj: unknown, key: symbol): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n (obj as Record<symbol, unknown>)[key] === true\n );\n}\n\nexport function isMcpSourceAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.McpSourceAdapter);\n}\n\nexport function isMcpClientAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.McpClientAdapter);\n}\n\nexport function isMcpDirectAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.MCPAdapter);\n}\n\nexport function isMcpAdapter(obj: unknown): boolean {\n return (\n isMcpSourceAdapter(obj) ||\n isMcpClientAdapter(obj) ||\n isMcpDirectAdapter(obj)\n );\n}\n","import type { Exchange } from \"@routecraft/routecraft\";\nimport { getExchangeContext } from \"@routecraft/routecraft\";\nimport type { Destination } from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\nimport type {\n McpArgsExtractor,\n McpClientHttpConfig,\n McpClientOptions,\n} from \"./types.ts\";\n\nconst ADAPTER_MCP_CLIENT_SERVERS = \"routecraft.mcp.client.servers\" as const;\n\ndeclare module \"@routecraft/routecraft\" {\n interface StoreRegistry {\n [ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig | string>;\n }\n}\n\n/**\n * Resolves the URL for the MCP server from options or context (serverId).\n * Store holds McpClientHttpConfig (with url); backward-compat: value may be string.\n */\nfunction resolveUrl(\n options: McpClientOptions,\n context: ReturnType<typeof getExchangeContext>,\n): string {\n if (options.url) return options.url;\n if (options.serverId && !context) {\n throw new Error(\n `MCP client: serverId \"${options.serverId}\" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store \"${ADAPTER_MCP_CLIENT_SERVERS}\" can be read.`,\n );\n }\n if (options.serverId && context) {\n const servers = context.getStore(\n ADAPTER_MCP_CLIENT_SERVERS as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n ) as Map<string, McpClientHttpConfig | string> | undefined;\n const config = servers?.get(options.serverId);\n if (!config) {\n throw new Error(\n `MCP client: serverId \"${options.serverId}\" not found in context store. Register it with context store key \"${ADAPTER_MCP_CLIENT_SERVERS}\".`,\n );\n }\n if (typeof config === \"string\") return config;\n return config.url;\n }\n throw new Error(\n \"MCP client: either url or serverId must be provided in McpClientOptions.\",\n );\n}\n\n/**\n * Default args extractor: use exchange body as tool arguments.\n * If body is a non-null object, use it as the args; otherwise use { input: body }.\n */\nexport const defaultArgs: McpArgsExtractor = (exchange) =>\n typeof exchange.body === \"object\" && exchange.body !== null\n ? (exchange.body as Record<string, unknown>)\n : { input: exchange.body };\n\n/**\n * McpClientAdapter calls a remote MCP server's tool and returns the result as the exchange body.\n * Use .to(mcp({ url, tool })) or .to(mcp({ serverId, tool, args })).\n */\nexport class McpClientAdapter implements Destination<unknown, unknown> {\n readonly adapterId = \"routecraft.adapter.mcp.client\";\n\n constructor(private readonly options: McpClientOptions) {\n (this as unknown as Record<symbol, boolean>)[BRAND.McpClientAdapter] = true;\n }\n\n async send(exchange: Exchange<unknown>): Promise<unknown> {\n const context = getExchangeContext(exchange);\n const url = resolveUrl(this.options, context);\n const toolName =\n this.options.tool ??\n (typeof exchange.body === \"object\" &&\n exchange.body !== null &&\n \"tool\" in exchange.body &&\n typeof (exchange.body as { tool: string }).tool === \"string\"\n ? (exchange.body as { tool: string }).tool\n : undefined);\n if (!toolName) {\n throw new Error(\n \"MCP client: tool name required. Set options.tool or exchange.body.tool.\",\n );\n }\n const argsExtractor = this.options.args ?? defaultArgs;\n const args = argsExtractor(exchange);\n\n const result = await this.callRemoteTool(url, toolName, args);\n return result;\n }\n\n private async callRemoteTool(\n serverUrl: string,\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<unknown> {\n const clientModule =\n await import(\"@modelcontextprotocol/sdk/client/index.js\");\n const Client = clientModule.Client;\n const transportModule =\n await import(\"@modelcontextprotocol/sdk/client/streamableHttp.js\");\n const StreamableHTTPClientTransport =\n transportModule.StreamableHTTPClientTransport as new (\n url: URL,\n options?: { sessionId?: string },\n ) => unknown;\n\n const url = new URL(serverUrl);\n const transport = new StreamableHTTPClientTransport(url);\n const clientInfo = { name: \"routecraft-mcp-client\", version: \"1.0.0\" };\n const client = new (Client as new (\n info: { name: string; version: string },\n options?: { capabilities?: Record<string, unknown> },\n ) => InstanceType<typeof clientModule.Client>)(clientInfo, {\n capabilities: {},\n });\n try {\n const connect = (\n client as unknown as { connect(transport: unknown): Promise<void> }\n ).connect;\n await connect.call(client, transport);\n\n const callTool = (\n client as unknown as {\n callTool(params: {\n name: string;\n arguments?: Record<string, unknown>;\n }): Promise<{ content?: Array<{ type: string; text?: string }> }>;\n }\n ).callTool;\n const response = await callTool.call(client, {\n name: toolName,\n arguments: args,\n });\n\n const content = response?.content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (first && typeof first === \"object\" && \"text\" in first)\n return first.text;\n if (first && typeof first === \"object\" && \"data\" in first)\n return (first as { data?: string }).data;\n }\n return response;\n } finally {\n const clientCleanup = client as unknown as {\n close?: () => void | Promise<void>;\n disconnect?: () => void | Promise<void>;\n };\n const closeOrDisconnect = clientCleanup.close ?? clientCleanup.disconnect;\n if (typeof closeOrDisconnect === \"function\") {\n try {\n await Promise.resolve(closeOrDisconnect.call(client));\n } catch {\n // Ignore cleanup errors so original error propagates\n }\n }\n const transportCleanup = transport as unknown as {\n close?: () => void | Promise<void>;\n destroy?: () => void;\n };\n const closeOrDestroy = transportCleanup.close ?? transportCleanup.destroy;\n if (typeof closeOrDestroy === \"function\") {\n try {\n await Promise.resolve(closeOrDestroy.call(transport));\n } catch {\n // Ignore cleanup errors so original error propagates\n }\n }\n }\n }\n}\n\nexport { ADAPTER_MCP_CLIENT_SERVERS };\n","import { DirectAdapter } from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\n\n/**\n * MCP adapter for local direct endpoints.\n * Extends DirectAdapter with MCP semantics; use via mcp(endpoint) with no options\n * for .to(mcp('endpoint')) or when another route defines the endpoint with options.\n */\nexport class MCPAdapter<T = unknown> extends DirectAdapter<T> {\n override readonly adapterId = \"routecraft.adapter.mcp.direct\";\n\n constructor(...args: ConstructorParameters<typeof DirectAdapter<T>>) {\n super(...args);\n (this as unknown as Record<symbol, boolean>)[BRAND.MCPAdapter] = true;\n }\n}\n","import type {\n DirectRouteMetadata,\n DirectServerOptions,\n Exchange,\n} from \"@routecraft/routecraft\";\n\n/** Store key set by mcpPlugin() when applied; routes using .from(mcp(...)) require it. */\nexport const MCP_PLUGIN_REGISTERED =\n \"routecraft.mcp.plugin.registered\" as const;\n\ndeclare module \"@routecraft/routecraft\" {\n interface StoreRegistry {\n [MCP_PLUGIN_REGISTERED]: boolean;\n }\n}\n\n/**\n * HTTP client config for a remote MCP server (Streamable HTTP).\n * Used in mcpPlugin({ clients: { name: config } }).\n */\nexport interface McpClientHttpConfig {\n transport?: \"streamable-http\";\n url: string;\n}\n\n/**\n * Stdio client config for a remote MCP server (subprocess).\n * Not yet accepted in plugin options; for future use.\n */\nexport interface McpClientStdioConfig {\n transport: \"stdio\";\n command: string;\n args?: string[];\n}\n\n/** Union of client configs. Plugin options use McpClientHttpConfig only for now. */\nexport type McpClientServerConfig = McpClientHttpConfig | McpClientStdioConfig;\n\n/**\n * Options for the MCP plugin (mcpPlugin).\n * One plugin per adapter: this is the single options type for the MCP plugin.\n */\nexport interface McpPluginOptions {\n /** Server name in MCP protocol handshake. Default: \"routecraft\" */\n name?: string;\n\n /** Server version. Default: \"1.0.0\" */\n version?: string;\n\n /** Transport mode for MCP server. Default: \"stdio\" */\n transport?: \"stdio\" | \"http\";\n\n /** Port for the streamable-http MCP server. Default: 3001 (only used with transport: \"http\") */\n port?: number;\n\n /** Host to bind to. Default: \"localhost\" (only used with transport: \"http\") */\n host?: string;\n\n /**\n * Filter which tools to expose. Default: all mcp() routes.\n * Can be an array of endpoint names or a filter function.\n */\n tools?: string[] | ((meta: DirectRouteMetadata) => boolean);\n\n /**\n * Named remote MCP servers for .to(mcp(\"name:tool\")). Keys are server names; values are HTTP config (url).\n * Stdio config not yet supported in plugin options.\n */\n clients?: Record<string, McpClientHttpConfig>;\n}\n\n/** @internal Used by MCPServer implementation; same shape as McpPluginOptions. */\nexport type MCPServerOptions = McpPluginOptions;\n\n/**\n * Options for mcp() when used as a server in .from().\n * Description is required for AI/MCP discoverability.\n */\nexport interface McpServerOptions extends DirectServerOptions {\n /** Human-readable description (required for MCP tools). */\n description: string;\n}\n\nexport type McpOptions = McpServerOptions;\n\n/**\n * Extracts MCP tool arguments from an exchange. Default implementation uses exchange.body.\n */\nexport type McpArgsExtractor = (\n exchange: Exchange<unknown>,\n) => Record<string, unknown>;\n\n/**\n * Options for mcp() when used as a Client in .to() to call a remote MCP server.\n * Provide either url (direct) or serverId (from plugin clients); tool is required.\n */\nexport interface McpClientOptions {\n /** URL of the remote MCP server. Omit when using serverId (from mcpPlugin clients). */\n url?: string;\n /** Tool name to invoke. If omitted, exchange body may specify it or a default applies. */\n tool?: string;\n /** Server id from context store; resolved to URL at runtime. Use when URL is registered via mcpPlugin({ clients }). */\n serverId?: string;\n /**\n * Extract tool arguments from the exchange. Receives the full exchange.\n * Default: body as object → use as args; otherwise { input: body }.\n */\n args?: McpArgsExtractor;\n}\n\n/**\n * Represents a tool exposed via MCP\n */\nexport interface MCPTool {\n name: string;\n description?: string;\n inputSchema: {\n type: \"object\";\n properties?: Record<string, unknown>;\n required?: string[];\n [key: string]: unknown;\n };\n}\n\n/**\n * MCP tool call result\n */\nexport interface MCPToolResult {\n content: Array<{\n type: \"text\" | \"image\" | \"resource\";\n text?: string;\n data?: string;\n mimeType?: string;\n }>;\n isError?: boolean;\n}\n","import {\n direct,\n type CraftContext,\n type Exchange,\n type ExchangeHeaders,\n type Source,\n} from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\nimport type { McpServerOptions } from \"./types.ts\";\nimport { MCP_PLUGIN_REGISTERED } from \"./types.ts\";\n\n/**\n * Source adapter for .from(mcp(endpoint, options)).\n * Delegates to direct() but requires the MCP plugin to be registered; fails at subscribe (route start) if not.\n */\nexport class McpSourceAdapter<T = unknown> implements Source<T> {\n readonly adapterId = \"routecraft.adapter.mcp.source\";\n\n constructor(\n private readonly endpoint: string,\n private readonly options: McpServerOptions,\n ) {\n (this as unknown as Record<symbol, boolean>)[BRAND.McpSourceAdapter] = true;\n }\n\n async subscribe(\n context: CraftContext,\n handler: (message: T, headers?: ExchangeHeaders) => Promise<Exchange>,\n abortController: AbortController,\n onReady?: () => void,\n ): Promise<void> {\n const registered = context.getStore(\n MCP_PLUGIN_REGISTERED as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n ) as boolean | undefined;\n if (registered !== true) {\n throw new Error(\n \"MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].\",\n );\n }\n const directAdapter = direct<T>(this.endpoint, this.options);\n return directAdapter.subscribe(context, handler, abortController, onReady);\n }\n}\n","import {\n error as rcError,\n type Exchange,\n type Source,\n type Destination,\n} from \"@routecraft/routecraft\";\nimport { McpClientAdapter } from \"./client-adapter.ts\";\nimport { MCPAdapter } from \"./mcp-adapter.ts\";\nimport { McpSourceAdapter } from \"./source-adapter.ts\";\nimport type {\n McpArgsExtractor,\n McpClientOptions,\n McpServerOptions,\n} from \"./types.ts\";\n\n/**\n * Create an MCP endpoint - a discoverable direct route for AI/MCP integration.\n *\n * `mcp()` is an alias for `direct()` with semantics oriented toward AI/MCP use cases.\n * - .to(mcp(\"server:tool\", { args? })) — remote tool by name (server and tool from plugin clients).\n * - .to(mcp({ url | serverId, tool, args? })) — remote tool with explicit options.\n * - .from(mcp(endpoint, options)) — source with description.\n * - .to(mcp(endpoint)) — direct destination.\n */\nexport function mcp<T = unknown>(\n endpoint: string,\n options: McpServerOptions,\n): Source<T>;\nexport function mcp(options: McpClientOptions): Destination<unknown, unknown>;\nexport function mcp(\n target: string,\n options?: { args?: McpArgsExtractor },\n): Destination<unknown, unknown>;\nexport function mcp<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n): Destination<T, T>;\nexport function mcp<T = unknown>(\n endpointOrOptions:\n | string\n | ((exchange: Exchange<T>) => string)\n | McpClientOptions,\n options?: McpServerOptions | McpClientOptions | { args?: McpArgsExtractor },\n): Source<T> | Destination<T, T> | Destination<unknown, unknown> {\n // Remote MCP client: .to(mcp({ url, tool })) or .to(mcp({ serverId, tool }))\n if (\n typeof endpointOrOptions === \"object\" &&\n endpointOrOptions !== null &&\n (\"url\" in endpointOrOptions || \"serverId\" in endpointOrOptions)\n ) {\n return new McpClientAdapter(endpointOrOptions as McpClientOptions);\n }\n\n // .to(mcp(\"server:tool\", { args? })) — parse target and create client adapter when options is undefined or lacks description (client-style; description = server/source)\n const isClientColonOptions =\n options === undefined ||\n (typeof options === \"object\" &&\n options !== null &&\n !(\"description\" in options));\n if (\n typeof endpointOrOptions === \"string\" &&\n endpointOrOptions.includes(\":\") &&\n isClientColonOptions\n ) {\n const colonIndex = endpointOrOptions.indexOf(\":\");\n const serverId = endpointOrOptions.slice(0, colonIndex);\n const tool = endpointOrOptions.slice(colonIndex + 1);\n const clientOptions: McpClientOptions = { serverId, tool };\n if (\n options !== undefined &&\n typeof options === \"object\" &&\n \"args\" in options &&\n options.args !== undefined\n ) {\n clientOptions.args = options.args;\n }\n return new McpClientAdapter(clientOptions);\n }\n\n const endpoint = endpointOrOptions as\n | string\n | ((exchange: Exchange<T>) => string);\n if (options !== undefined) {\n if (typeof endpoint !== \"string\") {\n throw rcError(\"RC5010\", undefined, {\n message: \"Dynamic endpoints cannot be used as source\",\n suggestion:\n \"Use a static string endpoint for source: .from(mcp('endpoint', options)).\",\n });\n }\n if (\"url\" in options || \"serverId\" in options) {\n throw rcError(\"RC5010\", undefined, {\n message:\n \"mcp() with url or serverId must be used as destination: .to(mcp({ url, tool }))\",\n suggestion:\n \"Use .to(mcp({ url: '...', tool: '...' })) to call a remote MCP server.\",\n });\n }\n if (\n \"args\" in options &&\n options.args !== undefined &&\n !(\"description\" in options)\n ) {\n throw rcError(\"RC5010\", undefined, {\n message:\n \"mcp(endpoint, { args }) is for client usage with a 'server:tool' target, not for defining a source\",\n suggestion:\n \"Use .to(mcp('server:tool', { args })) to call a remote tool, or .from(mcp('endpoint', { description: '...' })) to define a source.\",\n });\n }\n return new McpSourceAdapter<T>(endpoint, options as McpServerOptions);\n }\n return new MCPAdapter<T>(endpoint);\n}\n","import type { CraftContext, DirectRouteMetadata } from \"@routecraft/routecraft\";\nimport { DirectAdapter, DefaultExchange } from \"@routecraft/routecraft\";\nimport type { MCPServerOptions } from \"./types.ts\";\n\n/** Resolved options with defaults applied (internal use). */\ntype MCPServerResolvedOptions = Required<\n Pick<MCPServerOptions, \"name\" | \"version\" | \"transport\" | \"port\" | \"host\">\n> &\n Pick<MCPServerOptions, \"tools\">;\n\n/**\n * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.\n * It reads the MCP route registry lazily (on first tools/list request) to ensure routes have subscribed.\n *\n * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.\n * Supports both stdio and streamable-http transports.\n */\nexport class MCPServer {\n private context: CraftContext;\n private options: MCPServerResolvedOptions;\n private server: unknown = null;\n private transport: unknown = null;\n private running = false;\n private toolsListLogged = false;\n\n constructor(context: CraftContext, options: MCPServerOptions = {}) {\n this.context = context;\n this.options = {\n name: \"routecraft\",\n version: \"1.0.0\",\n transport: \"stdio\",\n port: 3001,\n host: \"localhost\",\n ...options,\n };\n }\n\n /**\n * Start the MCP server and listen for connections\n */\n async start(): Promise<void> {\n if (this.running) {\n this.context.logger.warn(\"MCP server already running\");\n return;\n }\n\n try {\n const transport = this.options.transport;\n\n if (transport === \"http\") {\n await this.startHttp();\n } else {\n await this.startStdio();\n }\n\n this.running = true;\n this.context.logger.info(\n `MCP server started (${this.options.name}@${this.options.version}) on ${transport}`,\n );\n this.logExposedToolsOnce();\n } catch (error) {\n this.context.logger.error(error, \"Failed to start MCP server\");\n throw error;\n }\n }\n\n /**\n * Start stdio transport\n */\n private async startStdio(): Promise<void> {\n // Dynamically import SDK to avoid TypeScript compatibility issues\n const { Server } =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { StdioServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n this.transport = new StdioServerTransport();\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n }\n\n /**\n * Start HTTP transport (streamable-http)\n */\n private async startHttp(): Promise<void> {\n // Dynamically import SDK\n const serverModule =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { Server } = serverModule;\n\n // Try to get StreamableHTTPServerTransport from the SDK\n // The MCP SDK exports this from the main server module\n const StreamableHTTPServerTransport: unknown = (\n serverModule as Record<string, unknown>\n )[\"StreamableHTTPServerTransport\"];\n\n if (!StreamableHTTPServerTransport) {\n throw new Error(\n \"StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed\",\n );\n }\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n const port = this.options.port;\n const host = this.options.host;\n\n // Create HTTP transport with port and host\n const TransportClass = StreamableHTTPServerTransport as {\n new (options: { port: number; host?: string }): unknown;\n };\n this.transport = new TransportClass({ port, host });\n\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n\n this.context.logger.info(\n `MCP HTTP server listening on http://${host}:${port}`,\n );\n }\n\n /**\n * Set up request handlers (shared by both transports).\n * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).\n */\n private async setupRequestHandlers(): Promise<void> {\n const typesModule = await import(\"@modelcontextprotocol/sdk/types.js\");\n const t = typesModule as Record<string, unknown>;\n const ListToolsRequestSchema = t[\"ListToolsRequestSchema\"];\n const CallToolRequestSchema = t[\"CallToolRequestSchema\"];\n\n if (!ListToolsRequestSchema || !CallToolRequestSchema) {\n throw new Error(\n \"MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed\",\n );\n }\n\n const srv = this.server as Record<\n string,\n (schema: unknown, handler: (request: unknown) => Promise<unknown>) => void\n >;\n\n srv[\"setRequestHandler\"](ListToolsRequestSchema, async () => {\n const tools = this.getAvailableTools();\n this.logExposedToolsOnce();\n return { tools };\n });\n\n srv[\"setRequestHandler\"](\n CallToolRequestSchema,\n async (request: unknown) => {\n const req = request as Record<string, unknown>;\n const params = req[\"params\"] as Record<string, unknown>;\n return await this.handleToolCall(\n (params[\"name\"] as string) || \"\",\n (params[\"arguments\"] as Record<string, unknown>) || {},\n );\n },\n );\n }\n\n /**\n * Stop the MCP server\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n try {\n if (this.transport) {\n const tr = this.transport as Record<string, () => Promise<void>>;\n await tr[\"close\"]();\n }\n this.running = false;\n this.context.logger.info(\"MCP server stopped\");\n } catch (error) {\n this.context.logger.error(error, \"Error stopping MCP server\");\n }\n }\n\n /**\n * Log exposed MCP tool names once (at start or on first tools/list).\n */\n private logExposedToolsOnce(): void {\n if (this.toolsListLogged) return;\n const tools = this.getAvailableTools();\n if (tools.length === 0) return;\n const names = tools.map((t) => (t[\"name\"] as string) ?? \"?\");\n this.context.logger.info(\n { tools: names },\n `Exposing ${tools.length} MCP tool(s): ${names.join(\", \")}`,\n );\n this.toolsListLogged = true;\n }\n\n /**\n * Get list of tools that should be exposed via MCP.\n * Reads the registry lazily - called on every tools/list request and for tests.\n */\n getAvailableTools(): Array<Record<string, unknown>> {\n const registry = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_REGISTRY,\n ) as Map<string, DirectRouteMetadata> | undefined;\n\n if (!registry) {\n return [];\n }\n\n // Get all mcp() routes (those with description)\n let tools = Array.from(registry.values()).filter(\n (t) => t.description !== undefined,\n );\n\n // Apply user filter if provided\n const toolsFilter = this.options.tools;\n if (toolsFilter) {\n if (Array.isArray(toolsFilter)) {\n const allowed = new Set(toolsFilter);\n tools = tools.filter((t) => allowed.has(t.endpoint));\n } else if (typeof toolsFilter === \"function\") {\n tools = tools.filter(toolsFilter);\n }\n }\n\n // Convert to MCP tool format\n return tools.map((meta) => this.metadataToMCPTool(meta));\n }\n\n /**\n * Convert RouteCraft mcp() route metadata to MCP tool format\n */\n private metadataToMCPTool(\n metadata: DirectRouteMetadata,\n ): Record<string, unknown> {\n return {\n name: metadata.endpoint,\n description: metadata.description || \"\",\n inputSchema: this.schemaToJsonSchema(metadata.schema),\n };\n }\n\n /**\n * Convert StandardSchema to JSON Schema\n */\n private schemaToJsonSchema(schema: unknown): Record<string, unknown> {\n if (!schema) {\n return { type: \"object\" };\n }\n\n // Check for Zod 4 toJsonSchema method\n if (typeof schema === \"object\" && schema !== null && \"_def\" in schema) {\n const schemaObj = schema as Record<string, unknown>;\n if (typeof schemaObj[\"toJsonSchema\"] === \"function\") {\n try {\n const schemaWithMethod = schema as Record<string, unknown>;\n const toJsonSchema = schemaWithMethod[\"toJsonSchema\"] as (\n this: unknown,\n ) => Record<string, unknown>;\n return toJsonSchema.call(schemaWithMethod);\n } catch (error) {\n this.context.logger.debug(\n error,\n \"Failed to convert schema to JSON Schema\",\n );\n return { type: \"object\" };\n }\n }\n }\n\n // Check for standard-schema validate method and try to extract info\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema\n ) {\n // For now, return generic object schema for standard-schema\n // In the future, we could enhance this based on the schema library\n return { type: \"object\", additionalProperties: true };\n }\n\n return { type: \"object\" };\n }\n\n /**\n * Handle a tool call from MCP client\n */\n private async handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n // Get the direct channel store\n const channelStore = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_STORE,\n ) as Map<string, Record<string, unknown>> | undefined;\n\n if (!channelStore) {\n const err = new Error(\"No direct channels available\");\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: No direct channels available` },\n ],\n };\n }\n\n // Get the channel for this tool endpoint\n const channel = channelStore.get(toolName);\n if (!channel) {\n const err = new Error(`Tool not found: ${toolName}`);\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: Tool not found: ${toolName}` },\n ],\n };\n }\n\n // Create an exchange with the tool arguments\n const exchange = new DefaultExchange(this.context, {\n body: args,\n headers: {\n \"routecraft.mcp.tool\": toolName,\n \"routecraft.mcp.session\": `mcp-${Date.now()}`,\n },\n });\n\n // Send the exchange through the direct channel\n const channelTyped = channel as Record<\n string,\n (name: string, ex: unknown) => Promise<unknown>\n >;\n const resultExchange = (await channelTyped[\"send\"](\n toolName,\n exchange,\n )) as Record<string, unknown>;\n\n // Convert result to MCP format\n const resultText =\n typeof resultExchange[\"body\"] === \"string\"\n ? (resultExchange[\"body\"] as string)\n : JSON.stringify(resultExchange[\"body\"]);\n\n return {\n content: [{ type: \"text\", text: resultText }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.context.logger.error(error, `Tool call failed: ${toolName}`);\n this.context.emit(\"error\", { error });\n return {\n content: [{ type: \"text\", text: `Error: ${message}` }],\n };\n }\n }\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { McpPluginOptions } from \"./types.ts\";\n\n/** Standard Schema validate result: success has value, failure has issues. */\ntype ValidateResult<T = unknown> =\n | { value: T; issues?: never }\n | { value?: never; issues: readonly unknown[] };\n\n/**\n * Validates MCP plugin options at apply time.\n * For full schema validation (required props, shape), use validateWithSchema() with a\n * StandardSchemaV1 from Zod, Valibot, or ArkType before calling mcpPlugin().\n */\nexport function validateMcpPluginOptions(options: McpPluginOptions): void {\n if (options.transport === \"http\") {\n if (options.port !== undefined) {\n if (typeof options.port !== \"number\") {\n throw new TypeError(\n \"mcpPlugin: when transport is 'http', port must be a number\",\n );\n }\n if (options.port < 0 || options.port > 65535) {\n throw new RangeError(\n \"mcpPlugin: port must be between 0 and 65535 when transport is 'http'\",\n );\n }\n }\n if (options.host !== undefined && typeof options.host !== \"string\") {\n throw new TypeError(\"mcpPlugin: when provided, host must be a string\");\n }\n }\n}\n\n/**\n * Validate plugin options with a StandardSchemaV1 (e.g. from Zod, Valibot, ArkType).\n * Use this when you need required props or full shape validation before mcpPlugin().\n *\n * @example\n * import { z } from \"zod\";\n * const schema = z.object({ transport: z.enum([\"stdio\", \"http\"]), port: z.number().optional() });\n * const validated = await validateWithSchema(options, schema);\n * mcpPlugin(validated);\n */\nexport async function validateWithSchema(\n options: McpPluginOptions,\n schema: StandardSchemaV1,\n): Promise<McpPluginOptions> {\n const standard = (\n schema as {\n \"~standard\"?: {\n validate: (\n v: unknown,\n ) => ValidateResult<unknown> | Promise<ValidateResult<unknown>>;\n };\n }\n )[\"~standard\"];\n if (!standard?.validate) {\n throw new Error(\n \"mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate\",\n );\n }\n let result = standard.validate(options);\n if (result instanceof Promise) {\n result = await result;\n }\n if (result.issues && result.issues.length > 0) {\n throw new Error(\n `mcpPlugin options validation failed: ${JSON.stringify(result.issues)}`,\n );\n }\n return result.value as McpPluginOptions;\n}\n","import type { CraftContext, CraftPlugin } from \"@routecraft/routecraft\";\nimport { ADAPTER_MCP_CLIENT_SERVERS } from \"./client-adapter.ts\";\nimport { MCPServer } from \"./server.ts\";\nimport { MCP_PLUGIN_REGISTERED } from \"./types.ts\";\nimport type { McpPluginOptions } from \"./types.ts\";\nimport { validateMcpPluginOptions } from \"./validate-options.ts\";\n\n/**\n * MCP plugin: one plugin per adapter. Starts the MCP server on context start and exposes mcp() routes to external MCP clients.\n * Optional clients: register named remote MCP servers so routes can use .to(mcp(\"name:tool\")) without passing url.\n * Required when any route uses .from(mcp(...)); the route will fail at start if this plugin is not applied.\n */\nexport function mcpPlugin(options: McpPluginOptions = {}): CraftPlugin {\n validateMcpPluginOptions(options);\n\n return (ctx: CraftContext) => {\n ctx.setStore(\n MCP_PLUGIN_REGISTERED as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n true,\n );\n\n if (options.clients && Object.keys(options.clients).length > 0) {\n const map = new Map(Object.entries(options.clients));\n ctx.setStore(\n ADAPTER_MCP_CLIENT_SERVERS as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n map,\n );\n }\n\n let server: MCPServer | null = null;\n\n ctx.on(\"contextStarted\", async () => {\n server = new MCPServer(ctx, options);\n try {\n await server.start();\n } catch (error) {\n ctx.logger.error(error, \"Failed to start MCP server plugin\");\n throw error;\n }\n });\n\n ctx.on(\"contextStopping\", async () => {\n if (server) {\n try {\n await server.stop();\n } catch (error) {\n ctx.logger.error(error, \"Error stopping MCP server plugin\");\n }\n }\n });\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,34 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DirectRouteMetadata, Exchange, DirectServerOptions, Source, Destination, DirectAdapter, CraftPlugin, CraftContext } from '@routecraft/routecraft';
|
|
2
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Cross-instance identity for @routecraft/ai: Symbol.for() keys and type guards.
|
|
6
|
+
* Shared across all copies of @routecraft/ai (and multiple routecraft versions) in a process.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
declare const BRAND: {
|
|
9
|
+
readonly McpSourceAdapter: symbol;
|
|
10
|
+
readonly McpClientAdapter: symbol;
|
|
11
|
+
readonly MCPAdapter: symbol;
|
|
12
|
+
};
|
|
13
|
+
declare function isMcpSourceAdapter(obj: unknown): boolean;
|
|
14
|
+
declare function isMcpClientAdapter(obj: unknown): boolean;
|
|
15
|
+
declare function isMcpDirectAdapter(obj: unknown): boolean;
|
|
16
|
+
declare function isMcpAdapter(obj: unknown): boolean;
|
|
17
|
+
|
|
18
|
+
/** Store key set by mcpPlugin() when applied; routes using .from(mcp(...)) require it. */
|
|
19
|
+
declare const MCP_PLUGIN_REGISTERED: "routecraft.mcp.plugin.registered";
|
|
20
|
+
declare module "@routecraft/routecraft" {
|
|
21
|
+
interface StoreRegistry {
|
|
22
|
+
[MCP_PLUGIN_REGISTERED]: boolean;
|
|
23
|
+
}
|
|
10
24
|
}
|
|
11
25
|
/**
|
|
12
|
-
*
|
|
26
|
+
* HTTP client config for a remote MCP server (Streamable HTTP).
|
|
27
|
+
* Used in mcpPlugin({ clients: { name: config } }).
|
|
13
28
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
interface McpClientHttpConfig {
|
|
30
|
+
transport?: "streamable-http";
|
|
31
|
+
url: string;
|
|
32
|
+
}
|
|
16
33
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `tool()` is an alias for `direct()` with semantics oriented toward AI use cases.
|
|
20
|
-
* Same two-argument pattern: tool(endpoint, options) for source, tool(endpoint) for destination.
|
|
34
|
+
* Stdio client config for a remote MCP server (subprocess).
|
|
35
|
+
* Not yet accepted in plugin options; for future use.
|
|
21
36
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
interface McpClientStdioConfig {
|
|
38
|
+
transport: "stdio";
|
|
39
|
+
command: string;
|
|
40
|
+
args?: string[];
|
|
41
|
+
}
|
|
42
|
+
/** Union of client configs. Plugin options use McpClientHttpConfig only for now. */
|
|
43
|
+
type McpClientServerConfig = McpClientHttpConfig | McpClientStdioConfig;
|
|
27
44
|
/**
|
|
28
|
-
* Options for
|
|
29
|
-
*
|
|
45
|
+
* Options for the MCP plugin (mcpPlugin).
|
|
46
|
+
* One plugin per adapter: this is the single options type for the MCP plugin.
|
|
30
47
|
*/
|
|
31
|
-
interface
|
|
48
|
+
interface McpPluginOptions {
|
|
32
49
|
/** Server name in MCP protocol handshake. Default: "routecraft" */
|
|
33
50
|
name?: string;
|
|
34
51
|
/** Server version. Default: "1.0.0" */
|
|
@@ -40,10 +57,47 @@ interface MCPServerOptions {
|
|
|
40
57
|
/** Host to bind to. Default: "localhost" (only used with transport: "http") */
|
|
41
58
|
host?: string;
|
|
42
59
|
/**
|
|
43
|
-
* Filter which tools to expose. Default: all
|
|
60
|
+
* Filter which tools to expose. Default: all mcp() routes.
|
|
44
61
|
* Can be an array of endpoint names or a filter function.
|
|
45
62
|
*/
|
|
46
63
|
tools?: string[] | ((meta: DirectRouteMetadata) => boolean);
|
|
64
|
+
/**
|
|
65
|
+
* Named remote MCP servers for .to(mcp("name:tool")). Keys are server names; values are HTTP config (url).
|
|
66
|
+
* Stdio config not yet supported in plugin options.
|
|
67
|
+
*/
|
|
68
|
+
clients?: Record<string, McpClientHttpConfig>;
|
|
69
|
+
}
|
|
70
|
+
/** @internal Used by MCPServer implementation; same shape as McpPluginOptions. */
|
|
71
|
+
type MCPServerOptions = McpPluginOptions;
|
|
72
|
+
/**
|
|
73
|
+
* Options for mcp() when used as a server in .from().
|
|
74
|
+
* Description is required for AI/MCP discoverability.
|
|
75
|
+
*/
|
|
76
|
+
interface McpServerOptions extends DirectServerOptions {
|
|
77
|
+
/** Human-readable description (required for MCP tools). */
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
type McpOptions = McpServerOptions;
|
|
81
|
+
/**
|
|
82
|
+
* Extracts MCP tool arguments from an exchange. Default implementation uses exchange.body.
|
|
83
|
+
*/
|
|
84
|
+
type McpArgsExtractor = (exchange: Exchange<unknown>) => Record<string, unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* Options for mcp() when used as a Client in .to() to call a remote MCP server.
|
|
87
|
+
* Provide either url (direct) or serverId (from plugin clients); tool is required.
|
|
88
|
+
*/
|
|
89
|
+
interface McpClientOptions {
|
|
90
|
+
/** URL of the remote MCP server. Omit when using serverId (from mcpPlugin clients). */
|
|
91
|
+
url?: string;
|
|
92
|
+
/** Tool name to invoke. If omitted, exchange body may specify it or a default applies. */
|
|
93
|
+
tool?: string;
|
|
94
|
+
/** Server id from context store; resolved to URL at runtime. Use when URL is registered via mcpPlugin({ clients }). */
|
|
95
|
+
serverId?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Extract tool arguments from the exchange. Receives the full exchange.
|
|
98
|
+
* Default: body as object → use as args; otherwise { input: body }.
|
|
99
|
+
*/
|
|
100
|
+
args?: McpArgsExtractor;
|
|
47
101
|
}
|
|
48
102
|
/**
|
|
49
103
|
* Represents a tool exposed via MCP
|
|
@@ -71,9 +125,42 @@ interface MCPToolResult {
|
|
|
71
125
|
isError?: boolean;
|
|
72
126
|
}
|
|
73
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Create an MCP endpoint - a discoverable direct route for AI/MCP integration.
|
|
130
|
+
*
|
|
131
|
+
* `mcp()` is an alias for `direct()` with semantics oriented toward AI/MCP use cases.
|
|
132
|
+
* - .to(mcp("server:tool", { args? })) — remote tool by name (server and tool from plugin clients).
|
|
133
|
+
* - .to(mcp({ url | serverId, tool, args? })) — remote tool with explicit options.
|
|
134
|
+
* - .from(mcp(endpoint, options)) — source with description.
|
|
135
|
+
* - .to(mcp(endpoint)) — direct destination.
|
|
136
|
+
*/
|
|
137
|
+
declare function mcp<T = unknown>(endpoint: string, options: McpServerOptions): Source<T>;
|
|
138
|
+
declare function mcp(options: McpClientOptions): Destination<unknown, unknown>;
|
|
139
|
+
declare function mcp(target: string, options?: {
|
|
140
|
+
args?: McpArgsExtractor;
|
|
141
|
+
}): Destination<unknown, unknown>;
|
|
142
|
+
declare function mcp<T = unknown>(endpoint: string | ((exchange: Exchange<T>) => string)): Destination<T, T>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* MCP adapter for local direct endpoints.
|
|
146
|
+
* Extends DirectAdapter with MCP semantics; use via mcp(endpoint) with no options
|
|
147
|
+
* for .to(mcp('endpoint')) or when another route defines the endpoint with options.
|
|
148
|
+
*/
|
|
149
|
+
declare class MCPAdapter<T = unknown> extends DirectAdapter<T> {
|
|
150
|
+
readonly adapterId = "routecraft.adapter.mcp.direct";
|
|
151
|
+
constructor(...args: ConstructorParameters<typeof DirectAdapter<T>>);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* MCP plugin: one plugin per adapter. Starts the MCP server on context start and exposes mcp() routes to external MCP clients.
|
|
156
|
+
* Optional clients: register named remote MCP servers so routes can use .to(mcp("name:tool")) without passing url.
|
|
157
|
+
* Required when any route uses .from(mcp(...)); the route will fail at start if this plugin is not applied.
|
|
158
|
+
*/
|
|
159
|
+
declare function mcpPlugin(options?: McpPluginOptions): CraftPlugin;
|
|
160
|
+
|
|
74
161
|
/**
|
|
75
162
|
* MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.
|
|
76
|
-
* It reads the
|
|
163
|
+
* It reads the MCP route registry lazily (on first tools/list request) to ensure routes have subscribed.
|
|
77
164
|
*
|
|
78
165
|
* Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.
|
|
79
166
|
* Supports both stdio and streamable-http transports.
|
|
@@ -84,6 +171,7 @@ declare class MCPServer {
|
|
|
84
171
|
private server;
|
|
85
172
|
private transport;
|
|
86
173
|
private running;
|
|
174
|
+
private toolsListLogged;
|
|
87
175
|
constructor(context: CraftContext, options?: MCPServerOptions);
|
|
88
176
|
/**
|
|
89
177
|
* Start the MCP server and listen for connections
|
|
@@ -106,13 +194,17 @@ declare class MCPServer {
|
|
|
106
194
|
* Stop the MCP server
|
|
107
195
|
*/
|
|
108
196
|
stop(): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Log exposed MCP tool names once (at start or on first tools/list).
|
|
199
|
+
*/
|
|
200
|
+
private logExposedToolsOnce;
|
|
109
201
|
/**
|
|
110
202
|
* Get list of tools that should be exposed via MCP.
|
|
111
203
|
* Reads the registry lazily - called on every tools/list request and for tests.
|
|
112
204
|
*/
|
|
113
205
|
getAvailableTools(): Array<Record<string, unknown>>;
|
|
114
206
|
/**
|
|
115
|
-
* Convert RouteCraft
|
|
207
|
+
* Convert RouteCraft mcp() route metadata to MCP tool format
|
|
116
208
|
*/
|
|
117
209
|
private metadataToMCPTool;
|
|
118
210
|
/**
|
|
@@ -125,4 +217,39 @@ declare class MCPServer {
|
|
|
125
217
|
private handleToolCall;
|
|
126
218
|
}
|
|
127
219
|
|
|
128
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Validate plugin options with a StandardSchemaV1 (e.g. from Zod, Valibot, ArkType).
|
|
222
|
+
* Use this when you need required props or full shape validation before mcpPlugin().
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* import { z } from "zod";
|
|
226
|
+
* const schema = z.object({ transport: z.enum(["stdio", "http"]), port: z.number().optional() });
|
|
227
|
+
* const validated = await validateWithSchema(options, schema);
|
|
228
|
+
* mcpPlugin(validated);
|
|
229
|
+
*/
|
|
230
|
+
declare function validateWithSchema(options: McpPluginOptions, schema: StandardSchemaV1): Promise<McpPluginOptions>;
|
|
231
|
+
|
|
232
|
+
declare const ADAPTER_MCP_CLIENT_SERVERS: "routecraft.mcp.client.servers";
|
|
233
|
+
declare module "@routecraft/routecraft" {
|
|
234
|
+
interface StoreRegistry {
|
|
235
|
+
[ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig | string>;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Default args extractor: use exchange body as tool arguments.
|
|
240
|
+
* If body is a non-null object, use it as the args; otherwise use { input: body }.
|
|
241
|
+
*/
|
|
242
|
+
declare const defaultArgs: McpArgsExtractor;
|
|
243
|
+
/**
|
|
244
|
+
* McpClientAdapter calls a remote MCP server's tool and returns the result as the exchange body.
|
|
245
|
+
* Use .to(mcp({ url, tool })) or .to(mcp({ serverId, tool, args })).
|
|
246
|
+
*/
|
|
247
|
+
declare class McpClientAdapter implements Destination<unknown, unknown> {
|
|
248
|
+
private readonly options;
|
|
249
|
+
readonly adapterId = "routecraft.adapter.mcp.client";
|
|
250
|
+
constructor(options: McpClientOptions);
|
|
251
|
+
send(exchange: Exchange<unknown>): Promise<unknown>;
|
|
252
|
+
private callRemoteTool;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export { ADAPTER_MCP_CLIENT_SERVERS, BRAND, MCPAdapter, MCPServer, type MCPServerOptions, type MCPTool, type MCPToolResult, MCP_PLUGIN_REGISTERED, type McpArgsExtractor, McpClientAdapter, type McpClientHttpConfig, type McpClientOptions, type McpClientServerConfig, type McpClientStdioConfig, type McpOptions, type McpPluginOptions, type McpServerOptions, defaultArgs, isMcpAdapter, isMcpClientAdapter, isMcpDirectAdapter, isMcpSourceAdapter, mcp, mcpPlugin, validateWithSchema };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,34 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DirectRouteMetadata, Exchange, DirectServerOptions, Source, Destination, DirectAdapter, CraftPlugin, CraftContext } from '@routecraft/routecraft';
|
|
2
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Cross-instance identity for @routecraft/ai: Symbol.for() keys and type guards.
|
|
6
|
+
* Shared across all copies of @routecraft/ai (and multiple routecraft versions) in a process.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
declare const BRAND: {
|
|
9
|
+
readonly McpSourceAdapter: symbol;
|
|
10
|
+
readonly McpClientAdapter: symbol;
|
|
11
|
+
readonly MCPAdapter: symbol;
|
|
12
|
+
};
|
|
13
|
+
declare function isMcpSourceAdapter(obj: unknown): boolean;
|
|
14
|
+
declare function isMcpClientAdapter(obj: unknown): boolean;
|
|
15
|
+
declare function isMcpDirectAdapter(obj: unknown): boolean;
|
|
16
|
+
declare function isMcpAdapter(obj: unknown): boolean;
|
|
17
|
+
|
|
18
|
+
/** Store key set by mcpPlugin() when applied; routes using .from(mcp(...)) require it. */
|
|
19
|
+
declare const MCP_PLUGIN_REGISTERED: "routecraft.mcp.plugin.registered";
|
|
20
|
+
declare module "@routecraft/routecraft" {
|
|
21
|
+
interface StoreRegistry {
|
|
22
|
+
[MCP_PLUGIN_REGISTERED]: boolean;
|
|
23
|
+
}
|
|
10
24
|
}
|
|
11
25
|
/**
|
|
12
|
-
*
|
|
26
|
+
* HTTP client config for a remote MCP server (Streamable HTTP).
|
|
27
|
+
* Used in mcpPlugin({ clients: { name: config } }).
|
|
13
28
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
interface McpClientHttpConfig {
|
|
30
|
+
transport?: "streamable-http";
|
|
31
|
+
url: string;
|
|
32
|
+
}
|
|
16
33
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `tool()` is an alias for `direct()` with semantics oriented toward AI use cases.
|
|
20
|
-
* Same two-argument pattern: tool(endpoint, options) for source, tool(endpoint) for destination.
|
|
34
|
+
* Stdio client config for a remote MCP server (subprocess).
|
|
35
|
+
* Not yet accepted in plugin options; for future use.
|
|
21
36
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
interface McpClientStdioConfig {
|
|
38
|
+
transport: "stdio";
|
|
39
|
+
command: string;
|
|
40
|
+
args?: string[];
|
|
41
|
+
}
|
|
42
|
+
/** Union of client configs. Plugin options use McpClientHttpConfig only for now. */
|
|
43
|
+
type McpClientServerConfig = McpClientHttpConfig | McpClientStdioConfig;
|
|
27
44
|
/**
|
|
28
|
-
* Options for
|
|
29
|
-
*
|
|
45
|
+
* Options for the MCP plugin (mcpPlugin).
|
|
46
|
+
* One plugin per adapter: this is the single options type for the MCP plugin.
|
|
30
47
|
*/
|
|
31
|
-
interface
|
|
48
|
+
interface McpPluginOptions {
|
|
32
49
|
/** Server name in MCP protocol handshake. Default: "routecraft" */
|
|
33
50
|
name?: string;
|
|
34
51
|
/** Server version. Default: "1.0.0" */
|
|
@@ -40,10 +57,47 @@ interface MCPServerOptions {
|
|
|
40
57
|
/** Host to bind to. Default: "localhost" (only used with transport: "http") */
|
|
41
58
|
host?: string;
|
|
42
59
|
/**
|
|
43
|
-
* Filter which tools to expose. Default: all
|
|
60
|
+
* Filter which tools to expose. Default: all mcp() routes.
|
|
44
61
|
* Can be an array of endpoint names or a filter function.
|
|
45
62
|
*/
|
|
46
63
|
tools?: string[] | ((meta: DirectRouteMetadata) => boolean);
|
|
64
|
+
/**
|
|
65
|
+
* Named remote MCP servers for .to(mcp("name:tool")). Keys are server names; values are HTTP config (url).
|
|
66
|
+
* Stdio config not yet supported in plugin options.
|
|
67
|
+
*/
|
|
68
|
+
clients?: Record<string, McpClientHttpConfig>;
|
|
69
|
+
}
|
|
70
|
+
/** @internal Used by MCPServer implementation; same shape as McpPluginOptions. */
|
|
71
|
+
type MCPServerOptions = McpPluginOptions;
|
|
72
|
+
/**
|
|
73
|
+
* Options for mcp() when used as a server in .from().
|
|
74
|
+
* Description is required for AI/MCP discoverability.
|
|
75
|
+
*/
|
|
76
|
+
interface McpServerOptions extends DirectServerOptions {
|
|
77
|
+
/** Human-readable description (required for MCP tools). */
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
type McpOptions = McpServerOptions;
|
|
81
|
+
/**
|
|
82
|
+
* Extracts MCP tool arguments from an exchange. Default implementation uses exchange.body.
|
|
83
|
+
*/
|
|
84
|
+
type McpArgsExtractor = (exchange: Exchange<unknown>) => Record<string, unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* Options for mcp() when used as a Client in .to() to call a remote MCP server.
|
|
87
|
+
* Provide either url (direct) or serverId (from plugin clients); tool is required.
|
|
88
|
+
*/
|
|
89
|
+
interface McpClientOptions {
|
|
90
|
+
/** URL of the remote MCP server. Omit when using serverId (from mcpPlugin clients). */
|
|
91
|
+
url?: string;
|
|
92
|
+
/** Tool name to invoke. If omitted, exchange body may specify it or a default applies. */
|
|
93
|
+
tool?: string;
|
|
94
|
+
/** Server id from context store; resolved to URL at runtime. Use when URL is registered via mcpPlugin({ clients }). */
|
|
95
|
+
serverId?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Extract tool arguments from the exchange. Receives the full exchange.
|
|
98
|
+
* Default: body as object → use as args; otherwise { input: body }.
|
|
99
|
+
*/
|
|
100
|
+
args?: McpArgsExtractor;
|
|
47
101
|
}
|
|
48
102
|
/**
|
|
49
103
|
* Represents a tool exposed via MCP
|
|
@@ -71,9 +125,42 @@ interface MCPToolResult {
|
|
|
71
125
|
isError?: boolean;
|
|
72
126
|
}
|
|
73
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Create an MCP endpoint - a discoverable direct route for AI/MCP integration.
|
|
130
|
+
*
|
|
131
|
+
* `mcp()` is an alias for `direct()` with semantics oriented toward AI/MCP use cases.
|
|
132
|
+
* - .to(mcp("server:tool", { args? })) — remote tool by name (server and tool from plugin clients).
|
|
133
|
+
* - .to(mcp({ url | serverId, tool, args? })) — remote tool with explicit options.
|
|
134
|
+
* - .from(mcp(endpoint, options)) — source with description.
|
|
135
|
+
* - .to(mcp(endpoint)) — direct destination.
|
|
136
|
+
*/
|
|
137
|
+
declare function mcp<T = unknown>(endpoint: string, options: McpServerOptions): Source<T>;
|
|
138
|
+
declare function mcp(options: McpClientOptions): Destination<unknown, unknown>;
|
|
139
|
+
declare function mcp(target: string, options?: {
|
|
140
|
+
args?: McpArgsExtractor;
|
|
141
|
+
}): Destination<unknown, unknown>;
|
|
142
|
+
declare function mcp<T = unknown>(endpoint: string | ((exchange: Exchange<T>) => string)): Destination<T, T>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* MCP adapter for local direct endpoints.
|
|
146
|
+
* Extends DirectAdapter with MCP semantics; use via mcp(endpoint) with no options
|
|
147
|
+
* for .to(mcp('endpoint')) or when another route defines the endpoint with options.
|
|
148
|
+
*/
|
|
149
|
+
declare class MCPAdapter<T = unknown> extends DirectAdapter<T> {
|
|
150
|
+
readonly adapterId = "routecraft.adapter.mcp.direct";
|
|
151
|
+
constructor(...args: ConstructorParameters<typeof DirectAdapter<T>>);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* MCP plugin: one plugin per adapter. Starts the MCP server on context start and exposes mcp() routes to external MCP clients.
|
|
156
|
+
* Optional clients: register named remote MCP servers so routes can use .to(mcp("name:tool")) without passing url.
|
|
157
|
+
* Required when any route uses .from(mcp(...)); the route will fail at start if this plugin is not applied.
|
|
158
|
+
*/
|
|
159
|
+
declare function mcpPlugin(options?: McpPluginOptions): CraftPlugin;
|
|
160
|
+
|
|
74
161
|
/**
|
|
75
162
|
* MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.
|
|
76
|
-
* It reads the
|
|
163
|
+
* It reads the MCP route registry lazily (on first tools/list request) to ensure routes have subscribed.
|
|
77
164
|
*
|
|
78
165
|
* Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.
|
|
79
166
|
* Supports both stdio and streamable-http transports.
|
|
@@ -84,6 +171,7 @@ declare class MCPServer {
|
|
|
84
171
|
private server;
|
|
85
172
|
private transport;
|
|
86
173
|
private running;
|
|
174
|
+
private toolsListLogged;
|
|
87
175
|
constructor(context: CraftContext, options?: MCPServerOptions);
|
|
88
176
|
/**
|
|
89
177
|
* Start the MCP server and listen for connections
|
|
@@ -106,13 +194,17 @@ declare class MCPServer {
|
|
|
106
194
|
* Stop the MCP server
|
|
107
195
|
*/
|
|
108
196
|
stop(): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Log exposed MCP tool names once (at start or on first tools/list).
|
|
199
|
+
*/
|
|
200
|
+
private logExposedToolsOnce;
|
|
109
201
|
/**
|
|
110
202
|
* Get list of tools that should be exposed via MCP.
|
|
111
203
|
* Reads the registry lazily - called on every tools/list request and for tests.
|
|
112
204
|
*/
|
|
113
205
|
getAvailableTools(): Array<Record<string, unknown>>;
|
|
114
206
|
/**
|
|
115
|
-
* Convert RouteCraft
|
|
207
|
+
* Convert RouteCraft mcp() route metadata to MCP tool format
|
|
116
208
|
*/
|
|
117
209
|
private metadataToMCPTool;
|
|
118
210
|
/**
|
|
@@ -125,4 +217,39 @@ declare class MCPServer {
|
|
|
125
217
|
private handleToolCall;
|
|
126
218
|
}
|
|
127
219
|
|
|
128
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Validate plugin options with a StandardSchemaV1 (e.g. from Zod, Valibot, ArkType).
|
|
222
|
+
* Use this when you need required props or full shape validation before mcpPlugin().
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* import { z } from "zod";
|
|
226
|
+
* const schema = z.object({ transport: z.enum(["stdio", "http"]), port: z.number().optional() });
|
|
227
|
+
* const validated = await validateWithSchema(options, schema);
|
|
228
|
+
* mcpPlugin(validated);
|
|
229
|
+
*/
|
|
230
|
+
declare function validateWithSchema(options: McpPluginOptions, schema: StandardSchemaV1): Promise<McpPluginOptions>;
|
|
231
|
+
|
|
232
|
+
declare const ADAPTER_MCP_CLIENT_SERVERS: "routecraft.mcp.client.servers";
|
|
233
|
+
declare module "@routecraft/routecraft" {
|
|
234
|
+
interface StoreRegistry {
|
|
235
|
+
[ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig | string>;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Default args extractor: use exchange body as tool arguments.
|
|
240
|
+
* If body is a non-null object, use it as the args; otherwise use { input: body }.
|
|
241
|
+
*/
|
|
242
|
+
declare const defaultArgs: McpArgsExtractor;
|
|
243
|
+
/**
|
|
244
|
+
* McpClientAdapter calls a remote MCP server's tool and returns the result as the exchange body.
|
|
245
|
+
* Use .to(mcp({ url, tool })) or .to(mcp({ serverId, tool, args })).
|
|
246
|
+
*/
|
|
247
|
+
declare class McpClientAdapter implements Destination<unknown, unknown> {
|
|
248
|
+
private readonly options;
|
|
249
|
+
readonly adapterId = "routecraft.adapter.mcp.client";
|
|
250
|
+
constructor(options: McpClientOptions);
|
|
251
|
+
send(exchange: Exchange<unknown>): Promise<unknown>;
|
|
252
|
+
private callRemoteTool;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export { ADAPTER_MCP_CLIENT_SERVERS, BRAND, MCPAdapter, MCPServer, type MCPServerOptions, type MCPTool, type MCPToolResult, MCP_PLUGIN_REGISTERED, type McpArgsExtractor, McpClientAdapter, type McpClientHttpConfig, type McpClientOptions, type McpClientServerConfig, type McpClientStdioConfig, type McpOptions, type McpPluginOptions, type McpServerOptions, defaultArgs, isMcpAdapter, isMcpClientAdapter, isMcpDirectAdapter, isMcpSourceAdapter, mcp, mcpPlugin, validateWithSchema };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export{
|
|
1
|
+
import {getExchangeContext,DirectAdapter,error,DefaultExchange,direct}from'@routecraft/routecraft';var c={McpSourceAdapter:Symbol.for("routecraft.ai.McpSourceAdapter"),McpClientAdapter:Symbol.for("routecraft.ai.McpClientAdapter"),MCPAdapter:Symbol.for("routecraft.ai.MCPAdapter")};function S(e,t){return typeof e=="object"&&e!==null&&e[t]===true}function R(e){return S(e,c.McpSourceAdapter)}function T(e){return S(e,c.McpClientAdapter)}function E(e){return S(e,c.MCPAdapter)}function H(e){return R(e)||T(e)||E(e)}var g="routecraft.mcp.client.servers";function q(e,t){if(e.url)return e.url;if(e.serverId&&!t)throw new Error(`MCP client: serverId "${e.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${g}" can be read.`);if(e.serverId&&t){let r=t.getStore(g)?.get(e.serverId);if(!r)throw new Error(`MCP client: serverId "${e.serverId}" not found in context store. Register it with context store key "${g}".`);return typeof r=="string"?r:r.url}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var b=e=>typeof e.body=="object"&&e.body!==null?e.body:{input:e.body},f=class{constructor(t){this.options=t;this[c.McpClientAdapter]=true;}adapterId="routecraft.adapter.mcp.client";async send(t){let o=getExchangeContext(t),r=q(this.options,o),n=this.options.tool??(typeof t.body=="object"&&t.body!==null&&"tool"in t.body&&typeof t.body.tool=="string"?t.body.tool:void 0);if(!n)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let i=(this.options.args??b)(t);return await this.callRemoteTool(r,n,i)}async callRemoteTool(t,o,r){let s=(await import('@modelcontextprotocol/sdk/client/index.js')).Client,a=(await import('@modelcontextprotocol/sdk/client/streamableHttp.js')).StreamableHTTPClientTransport,w=new URL(t),l=new a(w),_={name:"routecraft-mcp-client",version:"1.0.0"},u=new s(_,{capabilities:{}});try{await u.connect.call(u,l);let v=await u.callTool.call(u,{name:o,arguments:r}),d=v?.content;if(Array.isArray(d)&&d.length>0){let p=d[0];if(p&&typeof p=="object"&&"text"in p)return p.text;if(p&&typeof p=="object"&&"data"in p)return p.data}return v}finally{let C=u,P=C.close??C.disconnect;if(typeof P=="function")try{await Promise.resolve(P.call(u));}catch{}let v=l,d=v.close??v.destroy;if(typeof d=="function")try{await Promise.resolve(d.call(l));}catch{}}}};var m=class extends DirectAdapter{adapterId="routecraft.adapter.mcp.direct";constructor(...t){super(...t),this[c.MCPAdapter]=true;}};var h="routecraft.mcp.plugin.registered";var M=class{constructor(t,o){this.endpoint=t;this.options=o;this[c.McpSourceAdapter]=true;}adapterId="routecraft.adapter.mcp.source";async subscribe(t,o,r,n){if(t.getStore(h)!==true)throw new Error("MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].");return direct(this.endpoint,this.options).subscribe(t,o,r,n)}};function k(e,t){if(typeof e=="object"&&e!==null&&("url"in e||"serverId"in e))return new f(e);let o=t===void 0||typeof t=="object"&&t!==null&&!("description"in t);if(typeof e=="string"&&e.includes(":")&&o){let n=e.indexOf(":"),s=e.slice(0,n),i=e.slice(n+1),a={serverId:s,tool:i};return t!==void 0&&typeof t=="object"&&"args"in t&&t.args!==void 0&&(a.args=t.args),new f(a)}let r=e;if(t!==void 0){if(typeof r!="string")throw error("RC5010",void 0,{message:"Dynamic endpoints cannot be used as source",suggestion:"Use a static string endpoint for source: .from(mcp('endpoint', options))."});if("url"in t||"serverId"in t)throw error("RC5010",void 0,{message:"mcp() with url or serverId must be used as destination: .to(mcp({ url, tool }))",suggestion:"Use .to(mcp({ url: '...', tool: '...' })) to call a remote MCP server."});if("args"in t&&t.args!==void 0&&!("description"in t))throw error("RC5010",void 0,{message:"mcp(endpoint, { args }) is for client usage with a 'server:tool' target, not for defining a source",suggestion:"Use .to(mcp('server:tool', { args })) to call a remote tool, or .from(mcp('endpoint', { description: '...' })) to define a source."});return new M(r,t)}return new m(r)}var y=class{context;options;server=null;transport=null;running=false;toolsListLogged=false;constructor(t,o={}){this.context=t,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...o};}async start(){if(this.running){this.context.logger.warn("MCP server already running");return}try{let t=this.options.transport;t==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info(`MCP server started (${this.options.name}@${this.options.version}) on ${t}`),this.logExposedToolsOnce();}catch(t){throw this.context.logger.error(t,"Failed to start MCP server"),t}}async startStdio(){let{Server:t}=await import('@modelcontextprotocol/sdk/server/index.js'),{StdioServerTransport:o}=await import('@modelcontextprotocol/sdk/server/stdio.js');this.server=new t({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers(),this.transport=new o,await this.server.connect(this.transport);}async startHttp(){let t=await import('@modelcontextprotocol/sdk/server/index.js'),{Server:o}=t,r=t.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");this.server=new o({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers();let n=this.options.port,s=this.options.host,i=r;this.transport=new i({port:n,host:s}),await this.server.connect(this.transport),this.context.logger.info(`MCP HTTP server listening on http://${s}:${n}`);}async setupRequestHandlers(){let o=await import('@modelcontextprotocol/sdk/types.js'),r=o.ListToolsRequestSchema,n=o.CallToolRequestSchema;if(!r||!n)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let s=this.server;s.setRequestHandler(r,async()=>{let i=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:i}}),s.setRequestHandler(n,async i=>{let w=i.params;return await this.handleToolCall(w.name||"",w.arguments||{})});}async stop(){if(this.running)try{this.transport&&await this.transport.close(),this.running=!1,this.context.logger.info("MCP server stopped");}catch(t){this.context.logger.error(t,"Error stopping MCP server");}}logExposedToolsOnce(){if(this.toolsListLogged)return;let t=this.getAvailableTools();if(t.length===0)return;let o=t.map(r=>r.name??"?");this.context.logger.info({tools:o},`Exposing ${t.length} MCP tool(s): ${o.join(", ")}`),this.toolsListLogged=true;}getAvailableTools(){let t=this.context.getStore(DirectAdapter.ADAPTER_DIRECT_REGISTRY);if(!t)return [];let o=Array.from(t.values()).filter(n=>n.description!==void 0),r=this.options.tools;if(r)if(Array.isArray(r)){let n=new Set(r);o=o.filter(s=>n.has(s.endpoint));}else typeof r=="function"&&(o=o.filter(r));return o.map(n=>this.metadataToMCPTool(n))}metadataToMCPTool(t){return {name:t.endpoint,description:t.description||"",inputSchema:this.schemaToJsonSchema(t.schema)}}schemaToJsonSchema(t){if(!t)return {type:"object"};if(typeof t=="object"&&t!==null&&"_def"in t&&typeof t.toJsonSchema=="function")try{let r=t;return r.toJsonSchema.call(r)}catch(r){return this.context.logger.debug(r,"Failed to convert schema to JSON Schema"),{type:"object"}}return typeof t=="object"&&t!==null&&"~standard"in t?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(t,o){try{let r=this.context.getStore(DirectAdapter.ADAPTER_DIRECT_STORE);if(!r){let l=new Error("No direct channels available");return this.context.emit("error",{error:l}),{content:[{type:"text",text:"Error: No direct channels available"}]}}let n=r.get(t);if(!n){let l=new Error(`Tool not found: ${t}`);return this.context.emit("error",{error:l}),{content:[{type:"text",text:`Error: Tool not found: ${t}`}]}}let s=new DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":t,"routecraft.mcp.session":`mcp-${Date.now()}`}}),a=await n.send(t,s);return {content:[{type:"text",text:typeof a.body=="string"?a.body:JSON.stringify(a.body)}]}}catch(r){let n=r instanceof Error?r.message:String(r);return this.context.logger.error(r,`Tool call failed: ${t}`),this.context.emit("error",{error:r}),{content:[{type:"text",text:`Error: ${n}`}]}}}};function D(e){if(e.transport==="http"){if(e.port!==void 0){if(typeof e.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(e.port<0||e.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(e.host!==void 0&&typeof e.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}}async function O(e,t){let o=t["~standard"];if(!o?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let r=o.validate(e);if(r instanceof Promise&&(r=await r),r.issues&&r.issues.length>0)throw new Error(`mcpPlugin options validation failed: ${JSON.stringify(r.issues)}`);return r.value}function I(e={}){return D(e),t=>{if(t.setStore(h,true),e.clients&&Object.keys(e.clients).length>0){let r=new Map(Object.entries(e.clients));t.setStore(g,r);}let o=null;t.on("contextStarted",async()=>{o=new y(t,e);try{await o.start();}catch(r){throw t.logger.error(r,"Failed to start MCP server plugin"),r}}),t.on("contextStopping",async()=>{if(o)try{await o.stop();}catch(r){t.logger.error(r,"Error stopping MCP server plugin");}});}}
|
|
2
|
+
export{g as ADAPTER_MCP_CLIENT_SERVERS,c as BRAND,m as MCPAdapter,y as MCPServer,h as MCP_PLUGIN_REGISTERED,f as McpClientAdapter,b as defaultArgs,H as isMcpAdapter,T as isMcpClientAdapter,E as isMcpDirectAdapter,R as isMcpSourceAdapter,k as mcp,I as mcpPlugin,O as validateWithSchema};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dsl.ts","../src/mcp/server.ts","../src/mcp/plugin.ts"],"names":["tool","endpoint","options","rcError","direct","MCPServer","context","transport","error","Server","StdioServerTransport","serverModule","StreamableHTTPServerTransport","port","host","TransportClass","t","ListToolsRequestSchema","CallToolRequestSchema","srv","request","params","registry","DirectAdapter","tools","toolsFilter","allowed","meta","metadata","schema","schemaWithMethod","toolName","args","channelStore","err","channel","exchange","DefaultExchange","resultExchange","message","plugin","ctx","server"],"mappings":"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","file":"index.js","sourcesContent":["import {\n direct,\n error as rcError,\n type DirectDestinationOptions,\n type DirectSourceOptions,\n type Exchange,\n type Source,\n type Destination,\n} from \"@routecraft/routecraft\";\n\n/**\n * Options for tool() when used as a Source in .from().\n * Description is required for AI/MCP discoverability.\n */\nexport interface ToolSourceOptions extends DirectSourceOptions {\n /** Human-readable description (required for tools). */\n description: string;\n}\n\n/**\n * Options for tool() when used as a Destination in .to().\n */\nexport type ToolDestinationOptions = DirectDestinationOptions;\n\nexport type ToolOptions = ToolSourceOptions;\n\n/**\n * Create a tool - a discoverable direct route for AI/MCP integration.\n *\n * `tool()` is an alias for `direct()` with semantics oriented toward AI use cases.\n * Same two-argument pattern: tool(endpoint, options) for source, tool(endpoint) for destination.\n */\nexport function tool<T = unknown>(\n endpoint: string,\n options: ToolSourceOptions,\n): Source<T>;\nexport function tool<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n): Destination<T, T>;\nexport function tool<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n options?: ToolSourceOptions | ToolDestinationOptions,\n): Source<T> | Destination<T, T> {\n if (options !== undefined) {\n if (typeof endpoint !== \"string\") {\n throw rcError(\"RC5010\", undefined, {\n message: \"Dynamic endpoints cannot be used as source\",\n suggestion:\n \"Use a static string endpoint for source: .from(tool('endpoint', options)).\",\n });\n }\n return direct<T>(endpoint, options);\n }\n return direct<T>(endpoint);\n}\n","import type { CraftContext, DirectRouteMetadata } from \"@routecraft/routecraft\";\nimport { DirectAdapter, DefaultExchange } from \"@routecraft/routecraft\";\nimport type { MCPServerOptions } from \"./types.ts\";\n\n/** Resolved options with defaults applied (internal use). */\ntype MCPServerResolvedOptions = Required<\n Pick<MCPServerOptions, \"name\" | \"version\" | \"transport\" | \"port\" | \"host\">\n> &\n Pick<MCPServerOptions, \"tools\">;\n\n/**\n * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.\n * It reads the tool registry lazily (on first tools/list request) to ensure routes have subscribed.\n *\n * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.\n * Supports both stdio and streamable-http transports.\n */\nexport class MCPServer {\n private context: CraftContext;\n private options: MCPServerResolvedOptions;\n private server: unknown = null;\n private transport: unknown = null;\n private running = false;\n\n constructor(context: CraftContext, options: MCPServerOptions = {}) {\n this.context = context;\n this.options = {\n name: \"routecraft\",\n version: \"1.0.0\",\n transport: \"stdio\",\n port: 3001,\n host: \"localhost\",\n ...options,\n };\n }\n\n /**\n * Start the MCP server and listen for connections\n */\n async start(): Promise<void> {\n if (this.running) {\n this.context.logger.warn(\"MCP server already running\");\n return;\n }\n\n try {\n const transport = this.options.transport;\n\n if (transport === \"http\") {\n await this.startHttp();\n } else {\n await this.startStdio();\n }\n\n this.running = true;\n this.context.logger.info(\n `MCP server started (${this.options.name}@${this.options.version}) on ${transport}`,\n );\n } catch (error) {\n this.context.logger.error(error, \"Failed to start MCP server\");\n throw error;\n }\n }\n\n /**\n * Start stdio transport\n */\n private async startStdio(): Promise<void> {\n // Dynamically import SDK to avoid TypeScript compatibility issues\n const { Server } =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { StdioServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n this.transport = new StdioServerTransport();\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n }\n\n /**\n * Start HTTP transport (streamable-http)\n */\n private async startHttp(): Promise<void> {\n // Dynamically import SDK\n const serverModule =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { Server } = serverModule;\n\n // Try to get StreamableHTTPServerTransport from the SDK\n // The MCP SDK exports this from the main server module\n const StreamableHTTPServerTransport: unknown = (\n serverModule as Record<string, unknown>\n )[\"StreamableHTTPServerTransport\"];\n\n if (!StreamableHTTPServerTransport) {\n throw new Error(\n \"StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed\",\n );\n }\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n const port = this.options.port;\n const host = this.options.host;\n\n // Create HTTP transport with port and host\n const TransportClass = StreamableHTTPServerTransport as {\n new (options: { port: number; host?: string }): unknown;\n };\n this.transport = new TransportClass({ port, host });\n\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n\n this.context.logger.info(\n `MCP HTTP server listening on http://${host}:${port}`,\n );\n }\n\n /**\n * Set up request handlers (shared by both transports).\n * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).\n */\n private async setupRequestHandlers(): Promise<void> {\n const typesModule = await import(\"@modelcontextprotocol/sdk/types.js\");\n const t = typesModule as Record<string, unknown>;\n const ListToolsRequestSchema = t[\"ListToolsRequestSchema\"];\n const CallToolRequestSchema = t[\"CallToolRequestSchema\"];\n\n if (!ListToolsRequestSchema || !CallToolRequestSchema) {\n throw new Error(\n \"MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed\",\n );\n }\n\n const srv = this.server as Record<\n string,\n (schema: unknown, handler: (request: unknown) => Promise<unknown>) => void\n >;\n\n srv[\"setRequestHandler\"](ListToolsRequestSchema, async () => {\n return {\n tools: this.getAvailableTools(),\n };\n });\n\n srv[\"setRequestHandler\"](\n CallToolRequestSchema,\n async (request: unknown) => {\n const req = request as Record<string, unknown>;\n const params = req[\"params\"] as Record<string, unknown>;\n return await this.handleToolCall(\n (params[\"name\"] as string) || \"\",\n (params[\"arguments\"] as Record<string, unknown>) || {},\n );\n },\n );\n }\n\n /**\n * Stop the MCP server\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n try {\n if (this.transport) {\n const tr = this.transport as Record<string, () => Promise<void>>;\n await tr[\"close\"]();\n }\n this.running = false;\n this.context.logger.info(\"MCP server stopped\");\n } catch (error) {\n this.context.logger.error(error, \"Error stopping MCP server\");\n }\n }\n\n /**\n * Get list of tools that should be exposed via MCP.\n * Reads the registry lazily - called on every tools/list request and for tests.\n */\n getAvailableTools(): Array<Record<string, unknown>> {\n const registry = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_REGISTRY,\n ) as Map<string, DirectRouteMetadata> | undefined;\n\n if (!registry) {\n return [];\n }\n\n // Get all tool routes (those with description)\n let tools = Array.from(registry.values()).filter(\n (t) => t.description !== undefined,\n );\n\n // Apply user filter if provided\n const toolsFilter = this.options.tools;\n if (toolsFilter) {\n if (Array.isArray(toolsFilter)) {\n const allowed = new Set(toolsFilter);\n tools = tools.filter((t) => allowed.has(t.endpoint));\n } else if (typeof toolsFilter === \"function\") {\n tools = tools.filter(toolsFilter);\n }\n }\n\n // Convert to MCP tool format\n return tools.map((meta) => this.metadataToMCPTool(meta));\n }\n\n /**\n * Convert RouteCraft tool metadata to MCP tool format\n */\n private metadataToMCPTool(\n metadata: DirectRouteMetadata,\n ): Record<string, unknown> {\n return {\n name: metadata.endpoint,\n description: metadata.description || \"\",\n inputSchema: this.schemaToJsonSchema(metadata.schema),\n };\n }\n\n /**\n * Convert StandardSchema to JSON Schema\n */\n private schemaToJsonSchema(schema: unknown): Record<string, unknown> {\n if (!schema) {\n return { type: \"object\" };\n }\n\n // Check for Zod 4 toJsonSchema method\n if (typeof schema === \"object\" && schema !== null && \"_def\" in schema) {\n const schemaObj = schema as Record<string, unknown>;\n if (typeof schemaObj[\"toJsonSchema\"] === \"function\") {\n try {\n const schemaWithMethod = schema as Record<string, unknown>;\n const toJsonSchema = schemaWithMethod[\"toJsonSchema\"] as (\n this: unknown,\n ) => Record<string, unknown>;\n return toJsonSchema.call(schemaWithMethod);\n } catch (error) {\n this.context.logger.debug(\n error,\n \"Failed to convert schema to JSON Schema\",\n );\n return { type: \"object\" };\n }\n }\n }\n\n // Check for standard-schema validate method and try to extract info\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema\n ) {\n // For now, return generic object schema for standard-schema\n // In the future, we could enhance this based on the schema library\n return { type: \"object\", additionalProperties: true };\n }\n\n return { type: \"object\" };\n }\n\n /**\n * Handle a tool call from MCP client\n */\n private async handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n // Get the direct channel store\n const channelStore = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_STORE,\n ) as Map<string, Record<string, unknown>> | undefined;\n\n if (!channelStore) {\n const err = new Error(\"No direct channels available\");\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: No direct channels available` },\n ],\n };\n }\n\n // Get the channel for this tool endpoint\n const channel = channelStore.get(toolName);\n if (!channel) {\n const err = new Error(`Tool not found: ${toolName}`);\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: Tool not found: ${toolName}` },\n ],\n };\n }\n\n // Create an exchange with the tool arguments\n const exchange = new DefaultExchange(this.context, {\n body: args,\n headers: {\n \"routecraft.mcp.tool\": toolName,\n \"routecraft.mcp.session\": `mcp-${Date.now()}`,\n },\n });\n\n // Send the exchange through the direct channel\n const channelTyped = channel as Record<\n string,\n (name: string, ex: unknown) => Promise<unknown>\n >;\n const resultExchange = (await channelTyped[\"send\"](\n toolName,\n exchange,\n )) as Record<string, unknown>;\n\n // Convert result to MCP format\n const resultText =\n typeof resultExchange[\"body\"] === \"string\"\n ? (resultExchange[\"body\"] as string)\n : JSON.stringify(resultExchange[\"body\"]);\n\n return {\n content: [{ type: \"text\", text: resultText }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.context.logger.error(error, `Tool call failed: ${toolName}`);\n this.context.emit(\"error\", { error });\n return {\n content: [{ type: \"text\", text: `Error: ${message}` }],\n };\n }\n }\n}\n","import type { CraftContext, CraftPlugin } from \"@routecraft/routecraft\";\nimport { MCPServer } from \"./server.ts\";\n\nexport function plugin(options: Record<string, unknown> = {}): CraftPlugin {\n return (ctx: CraftContext) => {\n let server: MCPServer | null = null;\n\n ctx.on(\"contextStarted\", async () => {\n server = new MCPServer(ctx, options);\n try {\n await server.start();\n } catch (error) {\n ctx.logger.error(error, \"Failed to start MCP server plugin\");\n throw error;\n }\n });\n\n ctx.on(\"contextStopping\", async () => {\n if (server) {\n try {\n await server.stop();\n } catch (error) {\n ctx.logger.error(error, \"Error stopping MCP server plugin\");\n }\n }\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/brand.ts","../src/mcp/client-adapter.ts","../src/mcp/mcp-adapter.ts","../src/mcp/types.ts","../src/mcp/source-adapter.ts","../src/mcp/mcp.ts","../src/mcp/server.ts","../src/mcp/validate-options.ts","../src/mcp/plugin.ts"],"names":["BRAND","isBranded","obj","key","isMcpSourceAdapter","isMcpClientAdapter","isMcpDirectAdapter","isMcpAdapter","ADAPTER_MCP_CLIENT_SERVERS","resolveUrl","options","context","config","defaultArgs","exchange","McpClientAdapter","getExchangeContext","url","toolName","args","serverUrl","Client","StreamableHTTPClientTransport","transport","clientInfo","client","response","content","first","clientCleanup","closeOrDisconnect","transportCleanup","closeOrDestroy","MCPAdapter","DirectAdapter","MCP_PLUGIN_REGISTERED","McpSourceAdapter","endpoint","handler","abortController","onReady","direct","mcp","endpointOrOptions","isClientColonOptions","colonIndex","serverId","tool","clientOptions","rcError","MCPServer","error","Server","StdioServerTransport","serverModule","StreamableHTTPServerTransport","port","host","TransportClass","t","ListToolsRequestSchema","CallToolRequestSchema","srv","tools","request","params","names","registry","toolsFilter","allowed","meta","metadata","schema","schemaWithMethod","channelStore","err","channel","DefaultExchange","resultExchange","message","validateMcpPluginOptions","validateWithSchema","standard","result","mcpPlugin","ctx","map","server"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Cross-instance identity for @routecraft/ai: Symbol.for() keys and type guards.\n * Shared across all copies of @routecraft/ai (and multiple routecraft versions) in a process.\n */\n\nexport const BRAND = {\n McpSourceAdapter: Symbol.for(\"routecraft.ai.McpSourceAdapter\"),\n McpClientAdapter: Symbol.for(\"routecraft.ai.McpClientAdapter\"),\n MCPAdapter: Symbol.for(\"routecraft.ai.MCPAdapter\"),\n} as const;\n\nfunction isBranded(obj: unknown, key: symbol): boolean {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n (obj as Record<symbol, unknown>)[key] === true\n );\n}\n\nexport function isMcpSourceAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.McpSourceAdapter);\n}\n\nexport function isMcpClientAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.McpClientAdapter);\n}\n\nexport function isMcpDirectAdapter(obj: unknown): boolean {\n return isBranded(obj, BRAND.MCPAdapter);\n}\n\nexport function isMcpAdapter(obj: unknown): boolean {\n return (\n isMcpSourceAdapter(obj) ||\n isMcpClientAdapter(obj) ||\n isMcpDirectAdapter(obj)\n );\n}\n","import type { Exchange } from \"@routecraft/routecraft\";\nimport { getExchangeContext } from \"@routecraft/routecraft\";\nimport type { Destination } from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\nimport type {\n McpArgsExtractor,\n McpClientHttpConfig,\n McpClientOptions,\n} from \"./types.ts\";\n\nconst ADAPTER_MCP_CLIENT_SERVERS = \"routecraft.mcp.client.servers\" as const;\n\ndeclare module \"@routecraft/routecraft\" {\n interface StoreRegistry {\n [ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig | string>;\n }\n}\n\n/**\n * Resolves the URL for the MCP server from options or context (serverId).\n * Store holds McpClientHttpConfig (with url); backward-compat: value may be string.\n */\nfunction resolveUrl(\n options: McpClientOptions,\n context: ReturnType<typeof getExchangeContext>,\n): string {\n if (options.url) return options.url;\n if (options.serverId && !context) {\n throw new Error(\n `MCP client: serverId \"${options.serverId}\" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store \"${ADAPTER_MCP_CLIENT_SERVERS}\" can be read.`,\n );\n }\n if (options.serverId && context) {\n const servers = context.getStore(\n ADAPTER_MCP_CLIENT_SERVERS as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n ) as Map<string, McpClientHttpConfig | string> | undefined;\n const config = servers?.get(options.serverId);\n if (!config) {\n throw new Error(\n `MCP client: serverId \"${options.serverId}\" not found in context store. Register it with context store key \"${ADAPTER_MCP_CLIENT_SERVERS}\".`,\n );\n }\n if (typeof config === \"string\") return config;\n return config.url;\n }\n throw new Error(\n \"MCP client: either url or serverId must be provided in McpClientOptions.\",\n );\n}\n\n/**\n * Default args extractor: use exchange body as tool arguments.\n * If body is a non-null object, use it as the args; otherwise use { input: body }.\n */\nexport const defaultArgs: McpArgsExtractor = (exchange) =>\n typeof exchange.body === \"object\" && exchange.body !== null\n ? (exchange.body as Record<string, unknown>)\n : { input: exchange.body };\n\n/**\n * McpClientAdapter calls a remote MCP server's tool and returns the result as the exchange body.\n * Use .to(mcp({ url, tool })) or .to(mcp({ serverId, tool, args })).\n */\nexport class McpClientAdapter implements Destination<unknown, unknown> {\n readonly adapterId = \"routecraft.adapter.mcp.client\";\n\n constructor(private readonly options: McpClientOptions) {\n (this as unknown as Record<symbol, boolean>)[BRAND.McpClientAdapter] = true;\n }\n\n async send(exchange: Exchange<unknown>): Promise<unknown> {\n const context = getExchangeContext(exchange);\n const url = resolveUrl(this.options, context);\n const toolName =\n this.options.tool ??\n (typeof exchange.body === \"object\" &&\n exchange.body !== null &&\n \"tool\" in exchange.body &&\n typeof (exchange.body as { tool: string }).tool === \"string\"\n ? (exchange.body as { tool: string }).tool\n : undefined);\n if (!toolName) {\n throw new Error(\n \"MCP client: tool name required. Set options.tool or exchange.body.tool.\",\n );\n }\n const argsExtractor = this.options.args ?? defaultArgs;\n const args = argsExtractor(exchange);\n\n const result = await this.callRemoteTool(url, toolName, args);\n return result;\n }\n\n private async callRemoteTool(\n serverUrl: string,\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<unknown> {\n const clientModule =\n await import(\"@modelcontextprotocol/sdk/client/index.js\");\n const Client = clientModule.Client;\n const transportModule =\n await import(\"@modelcontextprotocol/sdk/client/streamableHttp.js\");\n const StreamableHTTPClientTransport =\n transportModule.StreamableHTTPClientTransport as new (\n url: URL,\n options?: { sessionId?: string },\n ) => unknown;\n\n const url = new URL(serverUrl);\n const transport = new StreamableHTTPClientTransport(url);\n const clientInfo = { name: \"routecraft-mcp-client\", version: \"1.0.0\" };\n const client = new (Client as new (\n info: { name: string; version: string },\n options?: { capabilities?: Record<string, unknown> },\n ) => InstanceType<typeof clientModule.Client>)(clientInfo, {\n capabilities: {},\n });\n try {\n const connect = (\n client as unknown as { connect(transport: unknown): Promise<void> }\n ).connect;\n await connect.call(client, transport);\n\n const callTool = (\n client as unknown as {\n callTool(params: {\n name: string;\n arguments?: Record<string, unknown>;\n }): Promise<{ content?: Array<{ type: string; text?: string }> }>;\n }\n ).callTool;\n const response = await callTool.call(client, {\n name: toolName,\n arguments: args,\n });\n\n const content = response?.content;\n if (Array.isArray(content) && content.length > 0) {\n const first = content[0];\n if (first && typeof first === \"object\" && \"text\" in first)\n return first.text;\n if (first && typeof first === \"object\" && \"data\" in first)\n return (first as { data?: string }).data;\n }\n return response;\n } finally {\n const clientCleanup = client as unknown as {\n close?: () => void | Promise<void>;\n disconnect?: () => void | Promise<void>;\n };\n const closeOrDisconnect = clientCleanup.close ?? clientCleanup.disconnect;\n if (typeof closeOrDisconnect === \"function\") {\n try {\n await Promise.resolve(closeOrDisconnect.call(client));\n } catch {\n // Ignore cleanup errors so original error propagates\n }\n }\n const transportCleanup = transport as unknown as {\n close?: () => void | Promise<void>;\n destroy?: () => void;\n };\n const closeOrDestroy = transportCleanup.close ?? transportCleanup.destroy;\n if (typeof closeOrDestroy === \"function\") {\n try {\n await Promise.resolve(closeOrDestroy.call(transport));\n } catch {\n // Ignore cleanup errors so original error propagates\n }\n }\n }\n }\n}\n\nexport { ADAPTER_MCP_CLIENT_SERVERS };\n","import { DirectAdapter } from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\n\n/**\n * MCP adapter for local direct endpoints.\n * Extends DirectAdapter with MCP semantics; use via mcp(endpoint) with no options\n * for .to(mcp('endpoint')) or when another route defines the endpoint with options.\n */\nexport class MCPAdapter<T = unknown> extends DirectAdapter<T> {\n override readonly adapterId = \"routecraft.adapter.mcp.direct\";\n\n constructor(...args: ConstructorParameters<typeof DirectAdapter<T>>) {\n super(...args);\n (this as unknown as Record<symbol, boolean>)[BRAND.MCPAdapter] = true;\n }\n}\n","import type {\n DirectRouteMetadata,\n DirectServerOptions,\n Exchange,\n} from \"@routecraft/routecraft\";\n\n/** Store key set by mcpPlugin() when applied; routes using .from(mcp(...)) require it. */\nexport const MCP_PLUGIN_REGISTERED =\n \"routecraft.mcp.plugin.registered\" as const;\n\ndeclare module \"@routecraft/routecraft\" {\n interface StoreRegistry {\n [MCP_PLUGIN_REGISTERED]: boolean;\n }\n}\n\n/**\n * HTTP client config for a remote MCP server (Streamable HTTP).\n * Used in mcpPlugin({ clients: { name: config } }).\n */\nexport interface McpClientHttpConfig {\n transport?: \"streamable-http\";\n url: string;\n}\n\n/**\n * Stdio client config for a remote MCP server (subprocess).\n * Not yet accepted in plugin options; for future use.\n */\nexport interface McpClientStdioConfig {\n transport: \"stdio\";\n command: string;\n args?: string[];\n}\n\n/** Union of client configs. Plugin options use McpClientHttpConfig only for now. */\nexport type McpClientServerConfig = McpClientHttpConfig | McpClientStdioConfig;\n\n/**\n * Options for the MCP plugin (mcpPlugin).\n * One plugin per adapter: this is the single options type for the MCP plugin.\n */\nexport interface McpPluginOptions {\n /** Server name in MCP protocol handshake. Default: \"routecraft\" */\n name?: string;\n\n /** Server version. Default: \"1.0.0\" */\n version?: string;\n\n /** Transport mode for MCP server. Default: \"stdio\" */\n transport?: \"stdio\" | \"http\";\n\n /** Port for the streamable-http MCP server. Default: 3001 (only used with transport: \"http\") */\n port?: number;\n\n /** Host to bind to. Default: \"localhost\" (only used with transport: \"http\") */\n host?: string;\n\n /**\n * Filter which tools to expose. Default: all mcp() routes.\n * Can be an array of endpoint names or a filter function.\n */\n tools?: string[] | ((meta: DirectRouteMetadata) => boolean);\n\n /**\n * Named remote MCP servers for .to(mcp(\"name:tool\")). Keys are server names; values are HTTP config (url).\n * Stdio config not yet supported in plugin options.\n */\n clients?: Record<string, McpClientHttpConfig>;\n}\n\n/** @internal Used by MCPServer implementation; same shape as McpPluginOptions. */\nexport type MCPServerOptions = McpPluginOptions;\n\n/**\n * Options for mcp() when used as a server in .from().\n * Description is required for AI/MCP discoverability.\n */\nexport interface McpServerOptions extends DirectServerOptions {\n /** Human-readable description (required for MCP tools). */\n description: string;\n}\n\nexport type McpOptions = McpServerOptions;\n\n/**\n * Extracts MCP tool arguments from an exchange. Default implementation uses exchange.body.\n */\nexport type McpArgsExtractor = (\n exchange: Exchange<unknown>,\n) => Record<string, unknown>;\n\n/**\n * Options for mcp() when used as a Client in .to() to call a remote MCP server.\n * Provide either url (direct) or serverId (from plugin clients); tool is required.\n */\nexport interface McpClientOptions {\n /** URL of the remote MCP server. Omit when using serverId (from mcpPlugin clients). */\n url?: string;\n /** Tool name to invoke. If omitted, exchange body may specify it or a default applies. */\n tool?: string;\n /** Server id from context store; resolved to URL at runtime. Use when URL is registered via mcpPlugin({ clients }). */\n serverId?: string;\n /**\n * Extract tool arguments from the exchange. Receives the full exchange.\n * Default: body as object → use as args; otherwise { input: body }.\n */\n args?: McpArgsExtractor;\n}\n\n/**\n * Represents a tool exposed via MCP\n */\nexport interface MCPTool {\n name: string;\n description?: string;\n inputSchema: {\n type: \"object\";\n properties?: Record<string, unknown>;\n required?: string[];\n [key: string]: unknown;\n };\n}\n\n/**\n * MCP tool call result\n */\nexport interface MCPToolResult {\n content: Array<{\n type: \"text\" | \"image\" | \"resource\";\n text?: string;\n data?: string;\n mimeType?: string;\n }>;\n isError?: boolean;\n}\n","import {\n direct,\n type CraftContext,\n type Exchange,\n type ExchangeHeaders,\n type Source,\n} from \"@routecraft/routecraft\";\nimport { BRAND } from \"../brand.ts\";\nimport type { McpServerOptions } from \"./types.ts\";\nimport { MCP_PLUGIN_REGISTERED } from \"./types.ts\";\n\n/**\n * Source adapter for .from(mcp(endpoint, options)).\n * Delegates to direct() but requires the MCP plugin to be registered; fails at subscribe (route start) if not.\n */\nexport class McpSourceAdapter<T = unknown> implements Source<T> {\n readonly adapterId = \"routecraft.adapter.mcp.source\";\n\n constructor(\n private readonly endpoint: string,\n private readonly options: McpServerOptions,\n ) {\n (this as unknown as Record<symbol, boolean>)[BRAND.McpSourceAdapter] = true;\n }\n\n async subscribe(\n context: CraftContext,\n handler: (message: T, headers?: ExchangeHeaders) => Promise<Exchange>,\n abortController: AbortController,\n onReady?: () => void,\n ): Promise<void> {\n const registered = context.getStore(\n MCP_PLUGIN_REGISTERED as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n ) as boolean | undefined;\n if (registered !== true) {\n throw new Error(\n \"MCP plugin required: routes using .from(mcp(...)) require the MCP plugin. Add mcpPlugin() to your config: plugins: [mcpPlugin()].\",\n );\n }\n const directAdapter = direct<T>(this.endpoint, this.options);\n return directAdapter.subscribe(context, handler, abortController, onReady);\n }\n}\n","import {\n error as rcError,\n type Exchange,\n type Source,\n type Destination,\n} from \"@routecraft/routecraft\";\nimport { McpClientAdapter } from \"./client-adapter.ts\";\nimport { MCPAdapter } from \"./mcp-adapter.ts\";\nimport { McpSourceAdapter } from \"./source-adapter.ts\";\nimport type {\n McpArgsExtractor,\n McpClientOptions,\n McpServerOptions,\n} from \"./types.ts\";\n\n/**\n * Create an MCP endpoint - a discoverable direct route for AI/MCP integration.\n *\n * `mcp()` is an alias for `direct()` with semantics oriented toward AI/MCP use cases.\n * - .to(mcp(\"server:tool\", { args? })) — remote tool by name (server and tool from plugin clients).\n * - .to(mcp({ url | serverId, tool, args? })) — remote tool with explicit options.\n * - .from(mcp(endpoint, options)) — source with description.\n * - .to(mcp(endpoint)) — direct destination.\n */\nexport function mcp<T = unknown>(\n endpoint: string,\n options: McpServerOptions,\n): Source<T>;\nexport function mcp(options: McpClientOptions): Destination<unknown, unknown>;\nexport function mcp(\n target: string,\n options?: { args?: McpArgsExtractor },\n): Destination<unknown, unknown>;\nexport function mcp<T = unknown>(\n endpoint: string | ((exchange: Exchange<T>) => string),\n): Destination<T, T>;\nexport function mcp<T = unknown>(\n endpointOrOptions:\n | string\n | ((exchange: Exchange<T>) => string)\n | McpClientOptions,\n options?: McpServerOptions | McpClientOptions | { args?: McpArgsExtractor },\n): Source<T> | Destination<T, T> | Destination<unknown, unknown> {\n // Remote MCP client: .to(mcp({ url, tool })) or .to(mcp({ serverId, tool }))\n if (\n typeof endpointOrOptions === \"object\" &&\n endpointOrOptions !== null &&\n (\"url\" in endpointOrOptions || \"serverId\" in endpointOrOptions)\n ) {\n return new McpClientAdapter(endpointOrOptions as McpClientOptions);\n }\n\n // .to(mcp(\"server:tool\", { args? })) — parse target and create client adapter when options is undefined or lacks description (client-style; description = server/source)\n const isClientColonOptions =\n options === undefined ||\n (typeof options === \"object\" &&\n options !== null &&\n !(\"description\" in options));\n if (\n typeof endpointOrOptions === \"string\" &&\n endpointOrOptions.includes(\":\") &&\n isClientColonOptions\n ) {\n const colonIndex = endpointOrOptions.indexOf(\":\");\n const serverId = endpointOrOptions.slice(0, colonIndex);\n const tool = endpointOrOptions.slice(colonIndex + 1);\n const clientOptions: McpClientOptions = { serverId, tool };\n if (\n options !== undefined &&\n typeof options === \"object\" &&\n \"args\" in options &&\n options.args !== undefined\n ) {\n clientOptions.args = options.args;\n }\n return new McpClientAdapter(clientOptions);\n }\n\n const endpoint = endpointOrOptions as\n | string\n | ((exchange: Exchange<T>) => string);\n if (options !== undefined) {\n if (typeof endpoint !== \"string\") {\n throw rcError(\"RC5010\", undefined, {\n message: \"Dynamic endpoints cannot be used as source\",\n suggestion:\n \"Use a static string endpoint for source: .from(mcp('endpoint', options)).\",\n });\n }\n if (\"url\" in options || \"serverId\" in options) {\n throw rcError(\"RC5010\", undefined, {\n message:\n \"mcp() with url or serverId must be used as destination: .to(mcp({ url, tool }))\",\n suggestion:\n \"Use .to(mcp({ url: '...', tool: '...' })) to call a remote MCP server.\",\n });\n }\n if (\n \"args\" in options &&\n options.args !== undefined &&\n !(\"description\" in options)\n ) {\n throw rcError(\"RC5010\", undefined, {\n message:\n \"mcp(endpoint, { args }) is for client usage with a 'server:tool' target, not for defining a source\",\n suggestion:\n \"Use .to(mcp('server:tool', { args })) to call a remote tool, or .from(mcp('endpoint', { description: '...' })) to define a source.\",\n });\n }\n return new McpSourceAdapter<T>(endpoint, options as McpServerOptions);\n }\n return new MCPAdapter<T>(endpoint);\n}\n","import type { CraftContext, DirectRouteMetadata } from \"@routecraft/routecraft\";\nimport { DirectAdapter, DefaultExchange } from \"@routecraft/routecraft\";\nimport type { MCPServerOptions } from \"./types.ts\";\n\n/** Resolved options with defaults applied (internal use). */\ntype MCPServerResolvedOptions = Required<\n Pick<MCPServerOptions, \"name\" | \"version\" | \"transport\" | \"port\" | \"host\">\n> &\n Pick<MCPServerOptions, \"tools\">;\n\n/**\n * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.\n * It reads the MCP route registry lazily (on first tools/list request) to ensure routes have subscribed.\n *\n * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.\n * Supports both stdio and streamable-http transports.\n */\nexport class MCPServer {\n private context: CraftContext;\n private options: MCPServerResolvedOptions;\n private server: unknown = null;\n private transport: unknown = null;\n private running = false;\n private toolsListLogged = false;\n\n constructor(context: CraftContext, options: MCPServerOptions = {}) {\n this.context = context;\n this.options = {\n name: \"routecraft\",\n version: \"1.0.0\",\n transport: \"stdio\",\n port: 3001,\n host: \"localhost\",\n ...options,\n };\n }\n\n /**\n * Start the MCP server and listen for connections\n */\n async start(): Promise<void> {\n if (this.running) {\n this.context.logger.warn(\"MCP server already running\");\n return;\n }\n\n try {\n const transport = this.options.transport;\n\n if (transport === \"http\") {\n await this.startHttp();\n } else {\n await this.startStdio();\n }\n\n this.running = true;\n this.context.logger.info(\n `MCP server started (${this.options.name}@${this.options.version}) on ${transport}`,\n );\n this.logExposedToolsOnce();\n } catch (error) {\n this.context.logger.error(error, \"Failed to start MCP server\");\n throw error;\n }\n }\n\n /**\n * Start stdio transport\n */\n private async startStdio(): Promise<void> {\n // Dynamically import SDK to avoid TypeScript compatibility issues\n const { Server } =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { StdioServerTransport } =\n await import(\"@modelcontextprotocol/sdk/server/stdio.js\");\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n this.transport = new StdioServerTransport();\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n }\n\n /**\n * Start HTTP transport (streamable-http)\n */\n private async startHttp(): Promise<void> {\n // Dynamically import SDK\n const serverModule =\n await import(\"@modelcontextprotocol/sdk/server/index.js\");\n const { Server } = serverModule;\n\n // Try to get StreamableHTTPServerTransport from the SDK\n // The MCP SDK exports this from the main server module\n const StreamableHTTPServerTransport: unknown = (\n serverModule as Record<string, unknown>\n )[\"StreamableHTTPServerTransport\"];\n\n if (!StreamableHTTPServerTransport) {\n throw new Error(\n \"StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed\",\n );\n }\n\n this.server = new Server(\n {\n name: this.options.name,\n version: this.options.version,\n },\n { capabilities: { tools: {} } },\n );\n\n await this.setupRequestHandlers();\n\n const port = this.options.port;\n const host = this.options.host;\n\n // Create HTTP transport with port and host\n const TransportClass = StreamableHTTPServerTransport as {\n new (options: { port: number; host?: string }): unknown;\n };\n this.transport = new TransportClass({ port, host });\n\n const srvWithConnect = this.server as Record<\n string,\n (transport: unknown) => Promise<void>\n >;\n await srvWithConnect[\"connect\"](this.transport);\n\n this.context.logger.info(\n `MCP HTTP server listening on http://${host}:${port}`,\n );\n }\n\n /**\n * Set up request handlers (shared by both transports).\n * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).\n */\n private async setupRequestHandlers(): Promise<void> {\n const typesModule = await import(\"@modelcontextprotocol/sdk/types.js\");\n const t = typesModule as Record<string, unknown>;\n const ListToolsRequestSchema = t[\"ListToolsRequestSchema\"];\n const CallToolRequestSchema = t[\"CallToolRequestSchema\"];\n\n if (!ListToolsRequestSchema || !CallToolRequestSchema) {\n throw new Error(\n \"MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed\",\n );\n }\n\n const srv = this.server as Record<\n string,\n (schema: unknown, handler: (request: unknown) => Promise<unknown>) => void\n >;\n\n srv[\"setRequestHandler\"](ListToolsRequestSchema, async () => {\n const tools = this.getAvailableTools();\n this.logExposedToolsOnce();\n return { tools };\n });\n\n srv[\"setRequestHandler\"](\n CallToolRequestSchema,\n async (request: unknown) => {\n const req = request as Record<string, unknown>;\n const params = req[\"params\"] as Record<string, unknown>;\n return await this.handleToolCall(\n (params[\"name\"] as string) || \"\",\n (params[\"arguments\"] as Record<string, unknown>) || {},\n );\n },\n );\n }\n\n /**\n * Stop the MCP server\n */\n async stop(): Promise<void> {\n if (!this.running) {\n return;\n }\n\n try {\n if (this.transport) {\n const tr = this.transport as Record<string, () => Promise<void>>;\n await tr[\"close\"]();\n }\n this.running = false;\n this.context.logger.info(\"MCP server stopped\");\n } catch (error) {\n this.context.logger.error(error, \"Error stopping MCP server\");\n }\n }\n\n /**\n * Log exposed MCP tool names once (at start or on first tools/list).\n */\n private logExposedToolsOnce(): void {\n if (this.toolsListLogged) return;\n const tools = this.getAvailableTools();\n if (tools.length === 0) return;\n const names = tools.map((t) => (t[\"name\"] as string) ?? \"?\");\n this.context.logger.info(\n { tools: names },\n `Exposing ${tools.length} MCP tool(s): ${names.join(\", \")}`,\n );\n this.toolsListLogged = true;\n }\n\n /**\n * Get list of tools that should be exposed via MCP.\n * Reads the registry lazily - called on every tools/list request and for tests.\n */\n getAvailableTools(): Array<Record<string, unknown>> {\n const registry = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_REGISTRY,\n ) as Map<string, DirectRouteMetadata> | undefined;\n\n if (!registry) {\n return [];\n }\n\n // Get all mcp() routes (those with description)\n let tools = Array.from(registry.values()).filter(\n (t) => t.description !== undefined,\n );\n\n // Apply user filter if provided\n const toolsFilter = this.options.tools;\n if (toolsFilter) {\n if (Array.isArray(toolsFilter)) {\n const allowed = new Set(toolsFilter);\n tools = tools.filter((t) => allowed.has(t.endpoint));\n } else if (typeof toolsFilter === \"function\") {\n tools = tools.filter(toolsFilter);\n }\n }\n\n // Convert to MCP tool format\n return tools.map((meta) => this.metadataToMCPTool(meta));\n }\n\n /**\n * Convert RouteCraft mcp() route metadata to MCP tool format\n */\n private metadataToMCPTool(\n metadata: DirectRouteMetadata,\n ): Record<string, unknown> {\n return {\n name: metadata.endpoint,\n description: metadata.description || \"\",\n inputSchema: this.schemaToJsonSchema(metadata.schema),\n };\n }\n\n /**\n * Convert StandardSchema to JSON Schema\n */\n private schemaToJsonSchema(schema: unknown): Record<string, unknown> {\n if (!schema) {\n return { type: \"object\" };\n }\n\n // Check for Zod 4 toJsonSchema method\n if (typeof schema === \"object\" && schema !== null && \"_def\" in schema) {\n const schemaObj = schema as Record<string, unknown>;\n if (typeof schemaObj[\"toJsonSchema\"] === \"function\") {\n try {\n const schemaWithMethod = schema as Record<string, unknown>;\n const toJsonSchema = schemaWithMethod[\"toJsonSchema\"] as (\n this: unknown,\n ) => Record<string, unknown>;\n return toJsonSchema.call(schemaWithMethod);\n } catch (error) {\n this.context.logger.debug(\n error,\n \"Failed to convert schema to JSON Schema\",\n );\n return { type: \"object\" };\n }\n }\n }\n\n // Check for standard-schema validate method and try to extract info\n if (\n typeof schema === \"object\" &&\n schema !== null &&\n \"~standard\" in schema\n ) {\n // For now, return generic object schema for standard-schema\n // In the future, we could enhance this based on the schema library\n return { type: \"object\", additionalProperties: true };\n }\n\n return { type: \"object\" };\n }\n\n /**\n * Handle a tool call from MCP client\n */\n private async handleToolCall(\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n // Get the direct channel store\n const channelStore = this.context.getStore(\n DirectAdapter.ADAPTER_DIRECT_STORE,\n ) as Map<string, Record<string, unknown>> | undefined;\n\n if (!channelStore) {\n const err = new Error(\"No direct channels available\");\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: No direct channels available` },\n ],\n };\n }\n\n // Get the channel for this tool endpoint\n const channel = channelStore.get(toolName);\n if (!channel) {\n const err = new Error(`Tool not found: ${toolName}`);\n this.context.emit(\"error\", { error: err });\n return {\n content: [\n { type: \"text\", text: `Error: Tool not found: ${toolName}` },\n ],\n };\n }\n\n // Create an exchange with the tool arguments\n const exchange = new DefaultExchange(this.context, {\n body: args,\n headers: {\n \"routecraft.mcp.tool\": toolName,\n \"routecraft.mcp.session\": `mcp-${Date.now()}`,\n },\n });\n\n // Send the exchange through the direct channel\n const channelTyped = channel as Record<\n string,\n (name: string, ex: unknown) => Promise<unknown>\n >;\n const resultExchange = (await channelTyped[\"send\"](\n toolName,\n exchange,\n )) as Record<string, unknown>;\n\n // Convert result to MCP format\n const resultText =\n typeof resultExchange[\"body\"] === \"string\"\n ? (resultExchange[\"body\"] as string)\n : JSON.stringify(resultExchange[\"body\"]);\n\n return {\n content: [{ type: \"text\", text: resultText }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.context.logger.error(error, `Tool call failed: ${toolName}`);\n this.context.emit(\"error\", { error });\n return {\n content: [{ type: \"text\", text: `Error: ${message}` }],\n };\n }\n }\n}\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { McpPluginOptions } from \"./types.ts\";\n\n/** Standard Schema validate result: success has value, failure has issues. */\ntype ValidateResult<T = unknown> =\n | { value: T; issues?: never }\n | { value?: never; issues: readonly unknown[] };\n\n/**\n * Validates MCP plugin options at apply time.\n * For full schema validation (required props, shape), use validateWithSchema() with a\n * StandardSchemaV1 from Zod, Valibot, or ArkType before calling mcpPlugin().\n */\nexport function validateMcpPluginOptions(options: McpPluginOptions): void {\n if (options.transport === \"http\") {\n if (options.port !== undefined) {\n if (typeof options.port !== \"number\") {\n throw new TypeError(\n \"mcpPlugin: when transport is 'http', port must be a number\",\n );\n }\n if (options.port < 0 || options.port > 65535) {\n throw new RangeError(\n \"mcpPlugin: port must be between 0 and 65535 when transport is 'http'\",\n );\n }\n }\n if (options.host !== undefined && typeof options.host !== \"string\") {\n throw new TypeError(\"mcpPlugin: when provided, host must be a string\");\n }\n }\n}\n\n/**\n * Validate plugin options with a StandardSchemaV1 (e.g. from Zod, Valibot, ArkType).\n * Use this when you need required props or full shape validation before mcpPlugin().\n *\n * @example\n * import { z } from \"zod\";\n * const schema = z.object({ transport: z.enum([\"stdio\", \"http\"]), port: z.number().optional() });\n * const validated = await validateWithSchema(options, schema);\n * mcpPlugin(validated);\n */\nexport async function validateWithSchema(\n options: McpPluginOptions,\n schema: StandardSchemaV1,\n): Promise<McpPluginOptions> {\n const standard = (\n schema as {\n \"~standard\"?: {\n validate: (\n v: unknown,\n ) => ValidateResult<unknown> | Promise<ValidateResult<unknown>>;\n };\n }\n )[\"~standard\"];\n if (!standard?.validate) {\n throw new Error(\n \"mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate\",\n );\n }\n let result = standard.validate(options);\n if (result instanceof Promise) {\n result = await result;\n }\n if (result.issues && result.issues.length > 0) {\n throw new Error(\n `mcpPlugin options validation failed: ${JSON.stringify(result.issues)}`,\n );\n }\n return result.value as McpPluginOptions;\n}\n","import type { CraftContext, CraftPlugin } from \"@routecraft/routecraft\";\nimport { ADAPTER_MCP_CLIENT_SERVERS } from \"./client-adapter.ts\";\nimport { MCPServer } from \"./server.ts\";\nimport { MCP_PLUGIN_REGISTERED } from \"./types.ts\";\nimport type { McpPluginOptions } from \"./types.ts\";\nimport { validateMcpPluginOptions } from \"./validate-options.ts\";\n\n/**\n * MCP plugin: one plugin per adapter. Starts the MCP server on context start and exposes mcp() routes to external MCP clients.\n * Optional clients: register named remote MCP servers so routes can use .to(mcp(\"name:tool\")) without passing url.\n * Required when any route uses .from(mcp(...)); the route will fail at start if this plugin is not applied.\n */\nexport function mcpPlugin(options: McpPluginOptions = {}): CraftPlugin {\n validateMcpPluginOptions(options);\n\n return (ctx: CraftContext) => {\n ctx.setStore(\n MCP_PLUGIN_REGISTERED as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n true,\n );\n\n if (options.clients && Object.keys(options.clients).length > 0) {\n const map = new Map(Object.entries(options.clients));\n ctx.setStore(\n ADAPTER_MCP_CLIENT_SERVERS as keyof import(\"@routecraft/routecraft\").StoreRegistry,\n map,\n );\n }\n\n let server: MCPServer | null = null;\n\n ctx.on(\"contextStarted\", async () => {\n server = new MCPServer(ctx, options);\n try {\n await server.start();\n } catch (error) {\n ctx.logger.error(error, \"Failed to start MCP server plugin\");\n throw error;\n }\n });\n\n ctx.on(\"contextStopping\", async () => {\n if (server) {\n try {\n await server.stop();\n } catch (error) {\n ctx.logger.error(error, \"Error stopping MCP server plugin\");\n }\n }\n });\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routecraft/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-canary.2",
|
|
4
4
|
"description": "AI and MCP integrations for RouteCraft",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -23,15 +23,16 @@
|
|
|
23
23
|
"prepublishOnly": "pnpm run build"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
27
|
+
"@routecraft/routecraft": "^0.3.0-canary.2",
|
|
28
|
+
"@standard-schema/spec": "^1.1.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"vitest": "^4.0.18",
|
|
31
32
|
"zod": "^4.3.6"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"@routecraft/routecraft": "
|
|
35
|
+
"@routecraft/routecraft": "*"
|
|
35
36
|
},
|
|
36
37
|
"keywords": [
|
|
37
38
|
"routecraft",
|