@routecraft/ai 0.2.0-canary.10

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 ADDED
@@ -0,0 +1,114 @@
1
+ # @routecraft/ai
2
+
3
+ AI and MCP integrations for RouteCraft.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @routecraft/ai
9
+ ```
10
+
11
+ or
12
+
13
+ ```bash
14
+ pnpm add @routecraft/ai
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { tool } from '@routecraft/ai';
21
+ import { craft, simple } from '@routecraft/routecraft';
22
+
23
+ craft()
24
+ .from(simple({ query: 'hello' }))
25
+ .to(tool('my-tool'));
26
+
27
+ craft()
28
+ .from(tool('my-tool'))
29
+ .process((body) => body);
30
+ ```
31
+
32
+ ## Features
33
+
34
+ - **tool()**: Alias for `direct()` with semantics for AI/MCP—discoverable routes with optional schema and description
35
+ - **Discovery**: Tools register in the context store for querying endpoints, descriptions, and schemas
36
+ - **Schema validation**: Use Zod (or other Standard Schema libs) for body and header validation on tools
37
+ - **Coming soon**: LLM adapters (OpenAI, Gemini), MCP source/destination, agent routing
38
+
39
+ ## Connecting from Cursor / Claude Desktop (MCP)
40
+
41
+ To connect an MCP client (Cursor, Claude Desktop, etc.) to your RouteCraft MCP server over stdio, configure the server with **the full path** to the `craft` executable. The client process often has a minimal `PATH`, so a bare `craft` command can fail with **"Failed to spawn process: No such file or directory"**.
42
+
43
+ **Node version:** The server requires **Node.js 18.19+ or 20+** (Pino 10 needs `diagnostics_channel.tracingChannel`). If the client spawns with an older Node (e.g. 18.17), the process will exit with a clear message. Fix it by using a newer Node for the MCP server:
44
+
45
+ - **Option A:** Start Cursor/Claude from a shell where a newer Node is first in `PATH` (e.g. run `nvm use 22` then open the app from that terminal).
46
+ - **Option B:** In MCP config, set **command** to the full path of a Node 20+ binary and **args** so the first element is the path to the **CLI’s JavaScript entry** (the `.js` file). This is the **recommended** approach; using the `craft` executable as `command` often fails because the MCP client has a minimal `PATH`. To get the CLI entry path: from a project that has `@routecraft/cli` installed, run `node -e "console.log(require.resolve('@routecraft/cli/dist/index.js'))"`.
47
+
48
+ **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS) or **Cursor** (MCP settings):
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "routecraft": {
54
+ "command": "/path/to/node",
55
+ "args": [
56
+ "/path/to/@routecraft/cli/dist/index.js",
57
+ "run",
58
+ "--log-file",
59
+ "/path/to/craft.log",
60
+ "--log-level",
61
+ "debug",
62
+ "/path/to/your/index.mjs"
63
+ ]
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ Replace `/path/to/node` with the full path to your Node 20+ binary (e.g. from `which node`), the CLI and entry paths with your actual paths, and the log path if desired. Logging to a file keeps stdout JSON-RPC-only. To disable logs, use `--log-level silent`.
70
+
71
+ ## Documentation
72
+
73
+ For comprehensive documentation, examples, and guides, visit [routecraft.dev](https://routecraft.dev).
74
+
75
+ ## Example
76
+
77
+ ```typescript
78
+ import { tool } from '@routecraft/ai';
79
+ import { craft, context, DirectAdapter } from '@routecraft/routecraft';
80
+ import { z } from 'zod';
81
+
82
+ craft()
83
+ .from(
84
+ tool('fetch-webpage', {
85
+ description: 'Fetch and return the content of a webpage',
86
+ schema: z.object({
87
+ url: z.string().url(),
88
+ }),
89
+ keywords: ['fetch', 'web', 'http'],
90
+ })
91
+ )
92
+ .process(async ({ url }) => {
93
+ const response = await fetch(url);
94
+ return { content: await response.text() };
95
+ });
96
+
97
+ // After context.start(), query registered tools
98
+ const registry = ctx.getStore(DirectAdapter.ADAPTER_DIRECT_REGISTRY);
99
+ const tools = Array.from(registry?.values() ?? []);
100
+ ```
101
+
102
+ ## Contributing
103
+
104
+ Contributions are welcome! Please see our [Contributing Guide](https://github.com/routecraftjs/routecraft/blob/main/CONTRIBUTING.md).
105
+
106
+ ## License
107
+
108
+ Apache-2.0
109
+
110
+ ## Links
111
+
112
+ - [Documentation](https://routecraft.dev)
113
+ - [GitHub Repository](https://github.com/routecraftjs/routecraft)
114
+ - [Issue Tracker](https://github.com/routecraftjs/routecraft/issues)
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';var routecraft=require('@routecraft/routecraft');function f(s,t){if(t!==void 0){if(typeof s!="string")throw routecraft.error("RC5010",void 0,{message:"Dynamic endpoints cannot be used as source",suggestion:"Use a static string endpoint for source: .from(tool('endpoint', options))."});return routecraft.direct(s,t)}return routecraft.direct(s)}var i=class{context;options;server=null;transport=null;running=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}`);}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,e=t.StreamableHTTPServerTransport;if(!e)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 r=this.options.port,n=this.options.host,c=e;this.transport=new c({port:r,host:n}),await this.server.connect(this.transport),this.context.logger.info(`MCP HTTP server listening on http://${n}:${r}`);}async setupRequestHandlers(){let o=await import('@modelcontextprotocol/sdk/types.js'),e=o.ListToolsRequestSchema,r=o.CallToolRequestSchema;if(!e||!r)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let n=this.server;n.setRequestHandler(e,async()=>({tools:this.getAvailableTools()})),n.setRequestHandler(r,async c=>{let p=c.params;return await this.handleToolCall(p.name||"",p.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");}}getAvailableTools(){let t=this.context.getStore(routecraft.DirectAdapter.ADAPTER_DIRECT_REGISTRY);if(!t)return [];let o=Array.from(t.values()).filter(r=>r.description!==void 0),e=this.options.tools;if(e)if(Array.isArray(e)){let r=new Set(e);o=o.filter(n=>r.has(n.endpoint));}else typeof e=="function"&&(o=o.filter(e));return o.map(r=>this.metadataToMCPTool(r))}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 e=t;return e.toJsonSchema.call(e)}catch(e){return this.context.logger.debug(e,"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 e=this.context.getStore(routecraft.DirectAdapter.ADAPTER_DIRECT_STORE);if(!e){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 r=e.get(t);if(!r){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 n=new routecraft.DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":t,"routecraft.mcp.session":`mcp-${Date.now()}`}}),a=await r.send(t,n);return {content:[{type:"text",text:typeof a.body=="string"?a.body:JSON.stringify(a.body)}]}}catch(e){let r=e instanceof Error?e.message:String(e);return this.context.logger.error(e,`Tool call failed: ${t}`),this.context.emit("error",{error:e}),{content:[{type:"text",text:`Error: ${r}`}]}}}};function h(s={}){return t=>{let o=null;t.on("contextStarted",async()=>{o=new i(t,s);try{await o.start();}catch(e){throw t.logger.error(e,"Failed to start MCP server plugin"),e}}),t.on("contextStopping",async()=>{if(o)try{await o.stop();}catch(e){t.logger.error(e,"Error stopping MCP server plugin");}});}}
2
+ exports.MCPServer=i;exports.plugin=h;exports.tool=f;//# sourceMappingURL=index.cjs.map
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"]}
@@ -0,0 +1,128 @@
1
+ import { DirectDestinationOptions, DirectSourceOptions, Source, Exchange, Destination, CraftPlugin, DirectRouteMetadata, CraftContext } from '@routecraft/routecraft';
2
+
3
+ /**
4
+ * Options for tool() when used as a Source in .from().
5
+ * Description is required for AI/MCP discoverability.
6
+ */
7
+ interface ToolSourceOptions extends DirectSourceOptions {
8
+ /** Human-readable description (required for tools). */
9
+ description: string;
10
+ }
11
+ /**
12
+ * Options for tool() when used as a Destination in .to().
13
+ */
14
+ type ToolDestinationOptions = DirectDestinationOptions;
15
+ type ToolOptions = ToolSourceOptions;
16
+ /**
17
+ * Create a tool - a discoverable direct route for AI/MCP integration.
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.
21
+ */
22
+ declare function tool<T = unknown>(endpoint: string, options: ToolSourceOptions): Source<T>;
23
+ declare function tool<T = unknown>(endpoint: string | ((exchange: Exchange<T>) => string)): Destination<T, T>;
24
+
25
+ declare function plugin(options?: Record<string, unknown>): CraftPlugin;
26
+
27
+ /**
28
+ * Options for configuring the MCP server and plugin.
29
+ * Used by MCPServer and the RouteCraft MCP plugin.
30
+ */
31
+ interface MCPServerOptions {
32
+ /** Server name in MCP protocol handshake. Default: "routecraft" */
33
+ name?: string;
34
+ /** Server version. Default: "1.0.0" */
35
+ version?: string;
36
+ /** Transport mode for MCP server. Default: "stdio" */
37
+ transport?: "stdio" | "http";
38
+ /** Port for the streamable-http MCP server. Default: 3001 (only used with transport: "http") */
39
+ port?: number;
40
+ /** Host to bind to. Default: "localhost" (only used with transport: "http") */
41
+ host?: string;
42
+ /**
43
+ * Filter which tools to expose. Default: all tool() routes.
44
+ * Can be an array of endpoint names or a filter function.
45
+ */
46
+ tools?: string[] | ((meta: DirectRouteMetadata) => boolean);
47
+ }
48
+ /**
49
+ * Represents a tool exposed via MCP
50
+ */
51
+ interface MCPTool {
52
+ name: string;
53
+ description?: string;
54
+ inputSchema: {
55
+ type: "object";
56
+ properties?: Record<string, unknown>;
57
+ required?: string[];
58
+ [key: string]: unknown;
59
+ };
60
+ }
61
+ /**
62
+ * MCP tool call result
63
+ */
64
+ interface MCPToolResult {
65
+ content: Array<{
66
+ type: "text" | "image" | "resource";
67
+ text?: string;
68
+ data?: string;
69
+ mimeType?: string;
70
+ }>;
71
+ isError?: boolean;
72
+ }
73
+
74
+ /**
75
+ * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.
76
+ * It reads the tool registry lazily (on first tools/list request) to ensure routes have subscribed.
77
+ *
78
+ * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.
79
+ * Supports both stdio and streamable-http transports.
80
+ */
81
+ declare class MCPServer {
82
+ private context;
83
+ private options;
84
+ private server;
85
+ private transport;
86
+ private running;
87
+ constructor(context: CraftContext, options?: MCPServerOptions);
88
+ /**
89
+ * Start the MCP server and listen for connections
90
+ */
91
+ start(): Promise<void>;
92
+ /**
93
+ * Start stdio transport
94
+ */
95
+ private startStdio;
96
+ /**
97
+ * Start HTTP transport (streamable-http)
98
+ */
99
+ private startHttp;
100
+ /**
101
+ * Set up request handlers (shared by both transports).
102
+ * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).
103
+ */
104
+ private setupRequestHandlers;
105
+ /**
106
+ * Stop the MCP server
107
+ */
108
+ stop(): Promise<void>;
109
+ /**
110
+ * Get list of tools that should be exposed via MCP.
111
+ * Reads the registry lazily - called on every tools/list request and for tests.
112
+ */
113
+ getAvailableTools(): Array<Record<string, unknown>>;
114
+ /**
115
+ * Convert RouteCraft tool metadata to MCP tool format
116
+ */
117
+ private metadataToMCPTool;
118
+ /**
119
+ * Convert StandardSchema to JSON Schema
120
+ */
121
+ private schemaToJsonSchema;
122
+ /**
123
+ * Handle a tool call from MCP client
124
+ */
125
+ private handleToolCall;
126
+ }
127
+
128
+ export { MCPServer, type MCPServerOptions, type MCPTool, type MCPToolResult, type ToolDestinationOptions, type ToolOptions, type ToolSourceOptions, plugin, tool };
@@ -0,0 +1,128 @@
1
+ import { DirectDestinationOptions, DirectSourceOptions, Source, Exchange, Destination, CraftPlugin, DirectRouteMetadata, CraftContext } from '@routecraft/routecraft';
2
+
3
+ /**
4
+ * Options for tool() when used as a Source in .from().
5
+ * Description is required for AI/MCP discoverability.
6
+ */
7
+ interface ToolSourceOptions extends DirectSourceOptions {
8
+ /** Human-readable description (required for tools). */
9
+ description: string;
10
+ }
11
+ /**
12
+ * Options for tool() when used as a Destination in .to().
13
+ */
14
+ type ToolDestinationOptions = DirectDestinationOptions;
15
+ type ToolOptions = ToolSourceOptions;
16
+ /**
17
+ * Create a tool - a discoverable direct route for AI/MCP integration.
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.
21
+ */
22
+ declare function tool<T = unknown>(endpoint: string, options: ToolSourceOptions): Source<T>;
23
+ declare function tool<T = unknown>(endpoint: string | ((exchange: Exchange<T>) => string)): Destination<T, T>;
24
+
25
+ declare function plugin(options?: Record<string, unknown>): CraftPlugin;
26
+
27
+ /**
28
+ * Options for configuring the MCP server and plugin.
29
+ * Used by MCPServer and the RouteCraft MCP plugin.
30
+ */
31
+ interface MCPServerOptions {
32
+ /** Server name in MCP protocol handshake. Default: "routecraft" */
33
+ name?: string;
34
+ /** Server version. Default: "1.0.0" */
35
+ version?: string;
36
+ /** Transport mode for MCP server. Default: "stdio" */
37
+ transport?: "stdio" | "http";
38
+ /** Port for the streamable-http MCP server. Default: 3001 (only used with transport: "http") */
39
+ port?: number;
40
+ /** Host to bind to. Default: "localhost" (only used with transport: "http") */
41
+ host?: string;
42
+ /**
43
+ * Filter which tools to expose. Default: all tool() routes.
44
+ * Can be an array of endpoint names or a filter function.
45
+ */
46
+ tools?: string[] | ((meta: DirectRouteMetadata) => boolean);
47
+ }
48
+ /**
49
+ * Represents a tool exposed via MCP
50
+ */
51
+ interface MCPTool {
52
+ name: string;
53
+ description?: string;
54
+ inputSchema: {
55
+ type: "object";
56
+ properties?: Record<string, unknown>;
57
+ required?: string[];
58
+ [key: string]: unknown;
59
+ };
60
+ }
61
+ /**
62
+ * MCP tool call result
63
+ */
64
+ interface MCPToolResult {
65
+ content: Array<{
66
+ type: "text" | "image" | "resource";
67
+ text?: string;
68
+ data?: string;
69
+ mimeType?: string;
70
+ }>;
71
+ isError?: boolean;
72
+ }
73
+
74
+ /**
75
+ * MCPServer wraps the MCP SDK and bridges it to RouteCraft's DirectChannel infrastructure.
76
+ * It reads the tool registry lazily (on first tools/list request) to ensure routes have subscribed.
77
+ *
78
+ * Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.
79
+ * Supports both stdio and streamable-http transports.
80
+ */
81
+ declare class MCPServer {
82
+ private context;
83
+ private options;
84
+ private server;
85
+ private transport;
86
+ private running;
87
+ constructor(context: CraftContext, options?: MCPServerOptions);
88
+ /**
89
+ * Start the MCP server and listen for connections
90
+ */
91
+ start(): Promise<void>;
92
+ /**
93
+ * Start stdio transport
94
+ */
95
+ private startStdio;
96
+ /**
97
+ * Start HTTP transport (streamable-http)
98
+ */
99
+ private startHttp;
100
+ /**
101
+ * Set up request handlers (shared by both transports).
102
+ * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).
103
+ */
104
+ private setupRequestHandlers;
105
+ /**
106
+ * Stop the MCP server
107
+ */
108
+ stop(): Promise<void>;
109
+ /**
110
+ * Get list of tools that should be exposed via MCP.
111
+ * Reads the registry lazily - called on every tools/list request and for tests.
112
+ */
113
+ getAvailableTools(): Array<Record<string, unknown>>;
114
+ /**
115
+ * Convert RouteCraft tool metadata to MCP tool format
116
+ */
117
+ private metadataToMCPTool;
118
+ /**
119
+ * Convert StandardSchema to JSON Schema
120
+ */
121
+ private schemaToJsonSchema;
122
+ /**
123
+ * Handle a tool call from MCP client
124
+ */
125
+ private handleToolCall;
126
+ }
127
+
128
+ export { MCPServer, type MCPServerOptions, type MCPTool, type MCPToolResult, type ToolDestinationOptions, type ToolOptions, type ToolSourceOptions, plugin, tool };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import {error,direct,DirectAdapter,DefaultExchange}from'@routecraft/routecraft';function f(s,t){if(t!==void 0){if(typeof s!="string")throw error("RC5010",void 0,{message:"Dynamic endpoints cannot be used as source",suggestion:"Use a static string endpoint for source: .from(tool('endpoint', options))."});return direct(s,t)}return direct(s)}var i=class{context;options;server=null;transport=null;running=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}`);}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,e=t.StreamableHTTPServerTransport;if(!e)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 r=this.options.port,n=this.options.host,c=e;this.transport=new c({port:r,host:n}),await this.server.connect(this.transport),this.context.logger.info(`MCP HTTP server listening on http://${n}:${r}`);}async setupRequestHandlers(){let o=await import('@modelcontextprotocol/sdk/types.js'),e=o.ListToolsRequestSchema,r=o.CallToolRequestSchema;if(!e||!r)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let n=this.server;n.setRequestHandler(e,async()=>({tools:this.getAvailableTools()})),n.setRequestHandler(r,async c=>{let p=c.params;return await this.handleToolCall(p.name||"",p.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");}}getAvailableTools(){let t=this.context.getStore(DirectAdapter.ADAPTER_DIRECT_REGISTRY);if(!t)return [];let o=Array.from(t.values()).filter(r=>r.description!==void 0),e=this.options.tools;if(e)if(Array.isArray(e)){let r=new Set(e);o=o.filter(n=>r.has(n.endpoint));}else typeof e=="function"&&(o=o.filter(e));return o.map(r=>this.metadataToMCPTool(r))}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 e=t;return e.toJsonSchema.call(e)}catch(e){return this.context.logger.debug(e,"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 e=this.context.getStore(DirectAdapter.ADAPTER_DIRECT_STORE);if(!e){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 r=e.get(t);if(!r){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 n=new DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":t,"routecraft.mcp.session":`mcp-${Date.now()}`}}),a=await r.send(t,n);return {content:[{type:"text",text:typeof a.body=="string"?a.body:JSON.stringify(a.body)}]}}catch(e){let r=e instanceof Error?e.message:String(e);return this.context.logger.error(e,`Tool call failed: ${t}`),this.context.emit("error",{error:e}),{content:[{type:"text",text:`Error: ${r}`}]}}}};function h(s={}){return t=>{let o=null;t.on("contextStarted",async()=>{o=new i(t,s);try{await o.start();}catch(e){throw t.logger.error(e,"Failed to start MCP server plugin"),e}}),t.on("contextStopping",async()=>{if(o)try{await o.stop();}catch(e){t.logger.error(e,"Error stopping MCP server plugin");}});}}
2
+ export{i as MCPServer,h as plugin,f as tool};//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@routecraft/ai",
3
+ "version": "0.2.0-canary.10",
4
+ "description": "AI and MCP integrations for RouteCraft",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts",
21
+ "test": "vitest",
22
+ "test:coverage": "vitest --coverage",
23
+ "prepublishOnly": "pnpm run build"
24
+ },
25
+ "dependencies": {
26
+ "@routecraft/routecraft": "^0.2.0-canary.10",
27
+ "@modelcontextprotocol/sdk": "^1.26.0"
28
+ },
29
+ "devDependencies": {
30
+ "vitest": "^4.0.18",
31
+ "zod": "^4.3.6"
32
+ },
33
+ "peerDependencies": {
34
+ "@routecraft/routecraft": ">=0.2.0"
35
+ },
36
+ "keywords": [
37
+ "routecraft",
38
+ "ai",
39
+ "mcp",
40
+ "tools",
41
+ "llm"
42
+ ],
43
+ "author": "routecraftjs",
44
+ "license": "Apache-2.0",
45
+ "homepage": "https://routecraft.dev",
46
+ "bugs": "https://github.com/routecraftjs/routecraft/issues",
47
+ "repository": "https://github.com/routecraftjs/routecraft",
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }