@routecraft/ai 0.4.0-canary.6 → 0.4.0-canary.8
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 +5 -5
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -13
- package/dist/index.d.ts +131 -13
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @routecraft/ai
|
|
2
2
|
|
|
3
|
-
AI adapters and MCP integration for
|
|
3
|
+
AI adapters and MCP integration for Routecraft. Call LLMs, run agents, generate embeddings, and expose your capabilities to Claude, Cursor, and other MCP clients.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -21,10 +21,10 @@ Define a capability and expose it as an MCP tool:
|
|
|
21
21
|
```typescript
|
|
22
22
|
// capabilities/fetch-webpage.ts
|
|
23
23
|
import { mcp } from '@routecraft/ai';
|
|
24
|
-
import { craft,
|
|
24
|
+
import { craft, ContextBuilder, http } from '@routecraft/routecraft';
|
|
25
25
|
import { z } from 'zod';
|
|
26
26
|
|
|
27
|
-
const ctx =
|
|
27
|
+
const ctx = new ContextBuilder()
|
|
28
28
|
.routes([
|
|
29
29
|
craft()
|
|
30
30
|
.id('fetch-webpage')
|
|
@@ -55,10 +55,10 @@ Use `mcp()` as a `.from()` source. This registers the capability as an MCP tool
|
|
|
55
55
|
|
|
56
56
|
```typescript
|
|
57
57
|
import { mcp, llm, llmPlugin } from '@routecraft/ai';
|
|
58
|
-
import { craft,
|
|
58
|
+
import { craft, ContextBuilder, http } from '@routecraft/routecraft';
|
|
59
59
|
import { z } from 'zod';
|
|
60
60
|
|
|
61
|
-
const ctx =
|
|
61
|
+
const ctx = new ContextBuilder()
|
|
62
62
|
.plugins([
|
|
63
63
|
llmPlugin({
|
|
64
64
|
providers: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! } },
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var routecraft=require('@routecraft/routecraft'),ai=require('ai'),http=require('http');var Y={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function fe(n,e){return typeof n=="object"&&n!==null&&n[e]===true}function ye(n){return fe(n,Y.McpAdapter)}function O(n,e){throw new Error(`The ${e} LLM provider requires the "${n}" package. Install it with: pnpm add ${n}`)}function L(n,e){if(!(n instanceof Error))return false;let t=n.message??"";return (t.includes("ERR_MODULE_NOT_FOUND")||t.includes("Cannot find module")||t.includes("Cannot find package"))&&t.includes(e)}var he={ollama:{baseURL:"http://localhost:11434/api"}};function A(n){return {...n.inputTokens!==void 0&&{inputTokens:n.inputTokens},...n.outputTokens!==void 0&&{outputTokens:n.outputTokens},...n.totalTokens!==void 0&&{totalTokens:n.totalTokens}}}function I(n){try{if("output"in n&&n.output!==void 0)return n.output}catch{}}function X(n,e,t){if(n===null||typeof n!="object")throw new Error(`[${e}] Invalid model: expected an object, got ${typeof n}. Model id: ${t}`);let r=n;if(typeof r.doGenerate!="function")throw new Error(`[${e}] Invalid model: missing or invalid doGenerate method. Model id: ${t}. Ensure the provider returns an AI SDK-compatible language model.`);if(typeof r.doStream!="function")throw new Error(`[${e}] Invalid model: missing or invalid doStream method. Model id: ${t}. Ensure the provider returns an AI SDK-compatible language model.`)}async function Q(n){let{config:e,modelId:t,options:r,systemPrompt:i,userPrompt:o,output:a}=n;switch(e.provider){case "openai":return we(e,t,r,i,o,a);case "anthropic":return ve(e,t,r,i,o,a);case "gemini":return Pe(e,t,r,i,o,a);case "openrouter":return Me(e,t,r,i,o,a);case "ollama":return Ee(e,t,r,i,o,a);default:{let u=e;throw new Error(`LLM provider not implemented: ${u.provider}`)}}}async function we(n,e,t,r,i,o){let a;try{a=(await import('@ai-sdk/openai')).createOpenAI;}catch(y){throw L(y,"@ai-sdk/openai")&&O("@ai-sdk/openai","OpenAI"),y}let{generateText:u}=await import('ai'),m={apiKey:n.apiKey};n.baseURL!==void 0&&(m.baseURL=n.baseURL);let l={model:a(m)(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};r&&(l.system=r),t.topP!==void 0&&(l.topP=t.topP),t.frequencyPenalty!==void 0&&(l.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(l.presencePenalty=t.presencePenalty);let p=o!==void 0?{...l,output:o}:l,c=await u(p),d={text:c.text??"",raw:c};c.usage&&(d.usage=A(c.usage));let f=I(c);return f!==void 0&&(d.output=f),d}async function ve(n,e,t,r,i,o){let a;try{a=(await import('@ai-sdk/anthropic')).createAnthropic;}catch(f){throw L(f,"@ai-sdk/anthropic")&&O("@ai-sdk/anthropic","Anthropic"),f}let{generateText:u}=await import('ai'),s={model:a({apiKey:n.apiKey})(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};r&&(s.system=r),t.topP!==void 0&&(s.topP=t.topP),t.frequencyPenalty!==void 0&&(s.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(s.presencePenalty=t.presencePenalty);let l=o!==void 0?{...s,output:o}:s,p=await u(l),c={text:p.text??"",raw:p};p.usage&&(c.usage=A(p.usage));let d=I(p);return d!==void 0&&(c.output=d),c}async function Pe(n,e,t,r,i,o){let a;try{a=(await import('@ai-sdk/google')).createGoogleGenerativeAI;}catch(f){throw L(f,"@ai-sdk/google")&&O("@ai-sdk/google","Gemini"),f}let{generateText:u}=await import('ai'),s={model:a({apiKey:n.apiKey})(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};r&&(s.system=r),t.topP!==void 0&&(s.topP=t.topP),t.frequencyPenalty!==void 0&&(s.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(s.presencePenalty=t.presencePenalty);let l=o!==void 0?{...s,output:o}:s,p=await u(l),c={text:p.text??"",raw:p};p.usage&&(c.usage=A(p.usage));let d=I(p);return d!==void 0&&(c.output=d),c}async function Me(n,e,t,r,i,o){let a;try{a=(await import('@openrouter/ai-sdk-provider')).createOpenRouter;}catch(h){throw L(h,"@openrouter/ai-sdk-provider")&&O("@openrouter/ai-sdk-provider","OpenRouter"),h}let{generateText:u}=await import('ai'),m=a({apiKey:n.apiKey}),g=n.modelId??e,s=m.chat(g);X(s,"OpenRouter",g);let p={model:s,prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};r&&(p.system=r),t.topP!==void 0&&(p.topP=t.topP),t.frequencyPenalty!==void 0&&(p.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(p.presencePenalty=t.presencePenalty);let c=o!==void 0?{...p,output:o}:p,d=await u(c),f={text:d.text??"",raw:d};d.usage&&(f.usage=A(d.usage));let y=I(d);return y!==void 0&&(f.output=y),f}async function Ee(n,e,t,r,i,o){let a;try{a=(await import('ollama-ai-provider-v2')).createOllama;}catch(h){throw L(h,"ollama-ai-provider-v2")&&O("ollama-ai-provider-v2","Ollama"),h}let{generateText:u}=await import('ai'),m=a({baseURL:n.baseURL??he.ollama.baseURL}),g=n.modelId??e,s=m(g);X(s,"Ollama",g);let p={model:s,prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};r&&(p.system=r),t.topP!==void 0&&(p.topP=t.topP),t.frequencyPenalty!==void 0&&(p.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(p.presencePenalty=t.presencePenalty);let c=o!==void 0?{...p,output:o}:p,d=await u(c),f={text:d.text??"",raw:d};d.usage&&(f.usage=A(d.usage));let y=I(d);return y!==void 0&&(f.output=y),f}function Z(n){let e=n["~standard"];if(!e?.validate)throw new Error("LLM outputSchema must be a StandardSchemaV1 with ~standard.validate");let t=e.jsonSchema?.output?.({target:"draft-2020-12"})??e.jsonSchema?.input?.({target:"draft-2020-12"});if(!t||typeof t!="object")throw new Error("LLM outputSchema must expose ~standard.jsonSchema.output or .input for provider structured output");function r(o){let a;try{a=e.validate(o);}catch(m){return {success:false,error:m instanceof Error?m:new Error(String(m))}}return a instanceof Promise?{success:false,error:new Error("Async output schema is not supported for LLM structured output")}:a.issues!=null&&(Array.isArray(a.issues)?a.issues.length>0:typeof a.issues=="object"&&a.issues!==null?Object.keys(a.issues).length>0:!!a.issues)?{success:false,error:new Error(typeof a.issues=="string"?a.issues:JSON.stringify(a.issues))}:{success:true,value:a.value}}let i=ai.jsonSchema(t,{validate:r});return ai.Output.object({schema:i})}var P=Symbol.for("routecraft.adapter.llm.providers"),E=Symbol.for("routecraft.adapter.llm.options");async function be(n,e){let t;try{t=JSON.parse(n);}catch{return}let r=e["~standard"];if(!r?.validate)return;let i=r.validate(t);if(i instanceof Promise&&(i=await i),!(i&&typeof i=="object"&&"issues"in i&&i.issues))return i&&typeof i=="object"&&"value"in i?i.value:void 0}var Ce=0,xe=1024;function ee(n,e){return n===void 0||n===""?"":typeof n=="function"?n(e):n}function ke(n){let e=n.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}function te(n){let e=n.indexOf(":");if(e<1||e===n.length-1)throw new Error(`LLM adapter: model id must be "providerId:modelName" (e.g. ollama:lfm2.5-thinking). Got: "${n}"`);return {providerId:n.slice(0,e),modelName:n.slice(e+1)}}function Oe(n,e){if(!e)throw new Error(`LLM adapter: model id "${n}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${P.description}" can be read.`);let t=e.getStore(P);if(!t)throw new Error('LLM provider not found: no providers registered. Add llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }) to your config.');let{providerId:r,modelName:i}=te(n),o=t.get(r);if(!o)throw new Error(`LLM provider "${r}" not found. Register it with llmPlugin({ providers: { "${r}": { provider, apiKey?, baseURL? } } }).`);return {config:o,modelName:i}}var R=class{constructor(e,t={}){this.modelId=e;this.options=t;}adapterId="routecraft.adapter.llm";options;mergedOptions(e){let t=e.getStore(E);return {temperature:Ce,maxTokens:xe,...t,...this.options}}async send(e){let t=routecraft.getExchangeContext(e),{config:r,modelName:i}=Oe(this.modelId,t),o=this.mergedOptions(t),a=ee(o.systemPrompt,e),u=ee(o.userPrompt,e)||ke(e),m={temperature:o.temperature,maxTokens:o.maxTokens};o.topP!==void 0&&(m.topP=o.topP),o.frequencyPenalty!==void 0&&(m.frequencyPenalty=o.frequencyPenalty),o.presencePenalty!==void 0&&(m.presencePenalty=o.presencePenalty);let g=o.outputSchema!==void 0?Z(o.outputSchema):void 0,s=await Q({config:r,modelId:i,options:m,systemPrompt:a,userPrompt:u,output:g});if(s.output===void 0&&s.text&&o.outputSchema!==void 0){let l=await be(s.text,o.outputSchema);l!==void 0&&(s.output=l);}return s}getMetadata(e){let t=e,{providerId:r}=te(this.modelId),i={model:this.modelId,provider:r};return t.usage&&(t.usage.inputTokens!==void 0&&(i.inputTokens=t.usage.inputTokens),t.usage.outputTokens!==void 0&&(i.outputTokens=t.usage.outputTokens)),i}};function ne(n,e){return new R(n,e)}var re=["openai","anthropic","openrouter","ollama","gemini"];function Le(n){return re.includes(n)}function N(n){if(!n||typeof n!="object")throw new TypeError("llmPlugin: options must be an object");if(!n.providers||typeof n.providers!="object")throw new TypeError("llmPlugin: options.providers must be an object (record of provider id \u2192 options)");for(let[e,t]of Object.entries(n.providers))if(t!==void 0){if(!t||typeof t!="object")throw new TypeError(`llmPlugin: providers["${e}"] must be an object`);if(!Le(e))throw new TypeError(`llmPlugin: providers["${e}"] is not a supported provider. Supported: ${re.join(", ")}`);switch(e){case "openai":case "anthropic":case "openrouter":case "gemini":if(typeof t.apiKey!="string"||!t.apiKey.trim())throw new TypeError(`llmPlugin: providers["${e}"].apiKey is required`);break;case "ollama":if(t.baseURL!==void 0&&typeof t.baseURL!="string")throw new TypeError(`llmPlugin: providers["${e}"].baseURL must be a string when provided`);if(t.modelId!==void 0&&(typeof t.modelId!="string"||!t.modelId.trim()))throw new TypeError(`llmPlugin: providers["${e}"].modelId must be a non-empty string when provided`);break}}if(n.defaultOptions!==void 0&&(typeof n.defaultOptions!="object"||n.defaultOptions===null))throw new TypeError("llmPlugin: defaultOptions must be an object when provided")}var Ae=["openai","anthropic","openrouter","ollama","gemini"];function Ie(n,e){return {provider:n,...e}}function oe(n={providers:{}}){return N(n),{apply(e){let t=new Map;for(let r of Ae){let i=n.providers[r];i!==void 0&&t.set(r,Ie(r,i));}e.setStore(P,t),n.defaultOptions&&Object.keys(n.defaultOptions).length>0&&e.setStore(E,n.defaultOptions);}}}var S=Symbol.for("routecraft.mcp.plugin.registered"),w=Symbol.for("routecraft.mcp.client.servers"),j=Symbol.for("routecraft.mcp.tool.registry"),T=Symbol.for("routecraft.mcp.stdio.managers");var M=Symbol.for("routecraft.mcp.adapter");var $=class{adapterId="routecraft.adapter.mcp";endpoint;options;constructor(e,t){if(this[M]=true,typeof e!="string")throw routecraft.rcError("RC5003",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.rcError("RC5003",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.rcError("RC5003",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."});if(!("description"in t)||typeof t.description!="string")throw routecraft.rcError("RC5003",void 0,{message:"mcp(endpoint, options) as source requires options.description",suggestion:"Use .from(mcp('endpoint', { description: '...' })) to define a source."});this.endpoint=e,this.options=t;}async subscribe(e,t,r,i){if(e.getStore(S)!==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(e,t,r,i)}};function q(n){let e=n?.content;if(!Array.isArray(e)||e.length===0)return n;if(e.length===1){let t=e[0];if(t.type==="text"&&typeof t.text=="string")return t.text;if(typeof t.data=="string")return t.data}return e}function Ne(n){let e=n.trim().toLowerCase();if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error(`MCP client: url must be HTTP or HTTPS. Stdio is not supported in routes; register stdio clients via mcpPlugin({ clients: { name: { command, args } } }). Got: "${n.slice(0,50)}${n.length>50?"...":""}"`)}function je(n,e){if(n.url)return Ne(n.url),n.url;if(n.serverId&&!e)throw new Error(`MCP client: serverId "${n.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${String(w)}" can be read.`);if(n.serverId&&e){let r=e.getStore(w)?.get(n.serverId);if(!r)throw new Error(`MCP client: serverId "${n.serverId}" not found in context store. Register it with context store key "${String(w)}".`);return typeof r=="string"?r:r.url}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var G=n=>typeof n.body=="object"&&n.body!==null?n.body:{input:n.body},D=class{constructor(e){this.options=e;if(this[M]=true,!e.url&&!e.serverId)throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.");if(e.url&&e.serverId)throw new Error("MCP client: cannot provide both url and serverId. Use either url for direct HTTP or serverId for registered servers.")}adapterId="routecraft.adapter.mcp";async send(e){let t=routecraft.getExchangeContext(e),r=this.options.tool??(typeof e.body=="object"&&e.body!==null&&"tool"in e.body&&typeof e.body.tool=="string"?e.body.tool:void 0);if(!r)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let o=(this.options.args??G)(e);if(this.options.serverId&&t){let g=t.getStore(T)?.get(this.options.serverId);if(g){let p=await g.callTool(r,o);return p&&typeof p=="object"&&(p.metadata={toolName:r,transport:"stdio",serverId:this.options.serverId}),p}let l=t.getStore(w)?.get(this.options.serverId);if(l&&typeof l=="object"&&"transport"in l&&l.transport==="stdio")throw new Error(`MCP client: stdio server "${this.options.serverId}" is not running. Ensure mcpPlugin is applied and the stdio client started successfully.`)}let a=je(this.options,t),u=await this.callRemoteTool(a,r,o);return u&&typeof u=="object"&&(u.metadata={toolName:r,url:a,transport:"http",...this.options.serverId?{serverId:this.options.serverId}:{}}),u}getMetadata(e){return e&&typeof e=="object"&&"metadata"in e?e.metadata:{toolName:"unknown",transport:"unknown"}}async callRemoteTool(e,t,r){let i,o;try{i=await import('@modelcontextprotocol/sdk/client/index.js'),o=await import('@modelcontextprotocol/sdk/client/streamableHttp.js');}catch{throw new Error('MCP client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk')}let a=i.Client,u=o.StreamableHTTPClientTransport,m=new URL(e),g=new u(m),s={name:"routecraft-mcp-client",version:"1.0.0"},l=new a(s,{capabilities:{}});try{await l.connect.call(l,g);let d=await l.callTool.call(l,{name:t,arguments:r});return q(d)}finally{let p=l,c=p.close??p.disconnect;if(typeof c=="function")try{await Promise.resolve(c.call(l));}catch{}let d=g,f=d.close??d.destroy;if(typeof f=="function")try{await Promise.resolve(f.call(g));}catch{}}}};function ie(n,e){if(typeof n=="object"&&n!==null&&("url"in n||"serverId"in n))return new D(n);let t=e===void 0||typeof e=="object"&&e!==null&&!("description"in e);if(typeof n=="string"&&n.includes(":")&&t){let i=n.indexOf(":"),o=n.slice(0,i),a=n.slice(i+1),u={serverId:o,tool:a};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(u.args=e.args),new D(u)}let r=n;if(e!==void 0)return new $(r,e);throw routecraft.rcError("RC5003",void 0,{message:"mcp() with only an endpoint is not supported. Use direct('endpoint') for in-process. For MCP server use .from(mcp('endpoint', { description: '...' })); for client use .to(mcp({ url, tool })) or .to(mcp('server:tool', { args })).",suggestion:"Use .from(mcp('endpoint', { description: '...' })) or .to(mcp({ url, tool })) or direct('endpoint')."})}var J='MCP server requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',b=class{context;options;server=null;transport=null;httpServer=null;running=false;toolsListLogged=false;constructor(e,t={}){this.context=e,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...t};}async start(){if(this.running){this.context.logger.warn({},"MCP server already running");return}try{let e=this.options.transport;e==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info({name:this.options.name,version:this.options.version,transport:e},"MCP server started"),this.logExposedToolsOnce();}catch(e){let t=routecraft.isRouteCraftError(e)?e.meta.message:e instanceof Error?e.message:"Failed to start MCP server";throw this.context.logger.error({err:e},t),e}}async startStdio(){let e,t;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server,t=(await import('@modelcontextprotocol/sdk/server/stdio.js')).StdioServerTransport;}catch{throw new Error(J)}this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers(),this.transport=new t,await this.server.connect(this.transport);}async startHttp(){let e;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server;}catch{throw new Error(J)}let r=(await import('@modelcontextprotocol/sdk/server/streamableHttp.js').catch(()=>null))?.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers();let i=this.options.port,o=this.options.host;this.transport=new r({sessionIdGenerator:()=>crypto.randomUUID(),enableJsonResponse:true}),await this.server.connect(this.transport);let u=this.transport.handleRequest;if(typeof u!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");this.httpServer=http.createServer(async(g,s)=>{let l=g.url?.split("?")[0]??"";if(l!=="/mcp"&&l!=="/mcp/"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not Found",path:l}));return}try{await u.call(this.transport,g,s);}catch(p){let c=routecraft.isRouteCraftError(p)?p.meta.message:p instanceof Error?p.message:"MCP HTTP request error";this.context.logger.error({err:p},c),s.headersSent||(s.writeHead(500,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Internal Server Error"})));}}),await new Promise((g,s)=>{this.httpServer.listen(i,o,()=>g()),this.httpServer.on("error",l=>{let p=routecraft.isRouteCraftError(l)?l.meta.message:l instanceof Error?l.message:"MCP HTTP server listen failed";this.context.logger.error({err:l},p),s(l);});});let m=this.getHttpPort()??i;this.context.logger.info({host:o,port:m,path:"/mcp"},"MCP HTTP server listening");}getHttpPort(){let e=this.httpServer?.address();if(e&&typeof e=="object"&&"port"in e)return e.port}async setupRequestHandlers(){let e;try{e=await import('@modelcontextprotocol/sdk/types.js');}catch{throw new Error(J)}let t=e,r=t.ListToolsRequestSchema,i=t.CallToolRequestSchema;if(!r||!i)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let o=this.server;o.setRequestHandler(r,async()=>{let a=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:a}}),o.setRequestHandler(i,async a=>{let m=a.params;return await this.handleToolCall(m.name||"",m.arguments||{})});}async stop(){if(this.running)try{if(this.httpServer&&(await new Promise(e=>{this.httpServer.close(()=>e());}),this.httpServer=null),this.transport){let e=this.transport;typeof e.close=="function"&&await e.close();}this.running=!1,this.context.logger.info({},"MCP server stopped");}catch(e){let t=routecraft.isRouteCraftError(e)?e.meta.message:e instanceof Error?e.message:"Error stopping MCP server";this.context.logger.error({err:e},t);}}logExposedToolsOnce(){if(this.toolsListLogged)return;let e=this.getAvailableTools();if(e.length===0)return;let t=e.map(r=>r.name??"?");this.context.logger.info({tools:t,count:t.length},"Exposing MCP tools"),this.toolsListLogged=true;}getAvailableTools(){let e=this.context.getStore(routecraft.ADAPTER_DIRECT_REGISTRY);if(!e)return [];let t=Array.from(e.values()).filter(i=>i.description!==void 0),r=this.options.tools;if(r)if(Array.isArray(r)){let i=new Set(r);t=t.filter(o=>i.has(o.endpoint));}else typeof r=="function"&&(t=t.filter(r));return t.map(i=>this.metadataToMcpTool(i))}metadataToMcpTool(e){return {name:e.endpoint,description:e.description||"",inputSchema:this.schemaToJsonSchema(e.schema)}}schemaToJsonSchema(e){if(!e||typeof e!="object")return {type:"object"};let t=e["~standard"];if(t?.jsonSchema?.input)try{let r=t.jsonSchema.input({target:"draft-2020-12"});return typeof r=="object"&&r!==null?r:{type:"object"}}catch(r){return this.context.logger.debug(r,"Standard JSON Schema conversion failed"),{type:"object"}}return "~standard"in e?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(e,t){try{let r=this.context.getStore(routecraft.ADAPTER_DIRECT_STORE);if(!r){let s=new Error("No direct channels available");return this.context.emit("error",{error:s}),{content:[{type:"text",text:"Error: No direct channels available"}]}}let i=r.get(e);if(!i){let s=new Error(`Tool not found: ${e}`);return this.context.emit("error",{error:s}),{content:[{type:"text",text:`Error: Tool not found: ${e}`}]}}let o=typeof t=="string"?(()=>{try{return JSON.parse(t)||{}}catch{return {input:t}}})():t&&typeof t=="object"?t:{};this.context.logger.debug({bodyType:typeof o,body:o},"MCP tool call exchange body");let a=new routecraft.DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`}}),m=await i.send(e,a);return {content:[{type:"text",text:typeof m.body=="string"?m.body:JSON.stringify(m.body)}]}}catch(r){let i=routecraft.isRouteCraftError(r)?r.meta.message:r instanceof Error?r.message:String(r);return this.context.logger.error({tool:e,err:r},i),this.context.emit("error",{error:r}),{content:[{type:"text",text:`Error: ${i}`}]}}}};function se(n){if(n.transport==="http"){if(n.port!==void 0){if(typeof n.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(n.port<0||n.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(n.host!==void 0&&typeof n.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}if(n.clients){for(let[e,t]of Object.entries(n.clients))if(typeof t=="object"&&t!==null&&"transport"in t&&t.transport==="stdio"&&(!t.command||typeof t.command!="string"))throw new TypeError(`mcpPlugin: stdio client "${e}" must have a non-empty command string`)}if(n.maxRestarts!==void 0&&(typeof n.maxRestarts!="number"||!Number.isInteger(n.maxRestarts)||n.maxRestarts<0))throw new TypeError("mcpPlugin: maxRestarts must be a non-negative integer");if(n.restartDelayMs!==void 0&&(typeof n.restartDelayMs!="number"||n.restartDelayMs<=0))throw new TypeError("mcpPlugin: restartDelayMs must be a positive number");if(n.restartBackoffMultiplier!==void 0&&(typeof n.restartBackoffMultiplier!="number"||n.restartBackoffMultiplier<1))throw new TypeError("mcpPlugin: restartBackoffMultiplier must be >= 1");if(n.toolRefreshIntervalMs!==void 0&&(typeof n.toolRefreshIntervalMs!="number"||!Number.isInteger(n.toolRefreshIntervalMs)||n.toolRefreshIntervalMs<0))throw new TypeError("mcpPlugin: toolRefreshIntervalMs must be a non-negative integer")}async function ae(n,e){let t=e["~standard"];if(!t?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let r=t.validate(n);if(r instanceof Promise&&(r=await r),r.issues)throw new Error(`mcpPlugin options validation failed: ${JSON.stringify(r.issues)}`);if(r.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return r.value}var Fe='MCP stdio client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',H=class{constructor(e,t,r,i){this.options=e;this.logger=t;this.onEvent=r;this.onToolsUpdated=i;}client=null;transport=null;running=false;stopping=false;restartCount=0;restartTimer=null;stabilityTimer=null;tools=[];async start(){if(this.running)return;this.stopping=false,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null);let e,t;try{e=await import('@modelcontextprotocol/sdk/client/index.js'),t=await import('@modelcontextprotocol/sdk/client/stdio.js');}catch{throw new Error(Fe)}let{serverId:r,command:i,args:o,env:a,cwd:u}=this.options;this.transport=new t.StdioClientTransport({command:i,args:o??[],...a?{env:a}:{},...u?{cwd:u}:{},stderr:"pipe"});let m=this.transport;m.onerror=g=>{this.logger.error({err:g,serverId:r},"Stdio client transport error"),this.onEvent(`plugin:mcp:client:${r}:error`,{serverId:r,error:g});},m.onclose=()=>{this.stopping||(this.running=false,this.handleDisconnect());},m.stderr?.on&&m.stderr.on("data",g=>{this.logger.debug({serverId:r,stderr:String(g).trimEnd()},"Stdio client stderr output");}),this.client=new e.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{},listChanged:{tools:{onChanged:(g,s)=>{s&&Array.isArray(s.tools)?(this.tools=s.tools,this.onToolsUpdated(r,this.tools),this.onEvent(`plugin:mcp:client:${r}:tools:listed`,{serverId:r,toolCount:this.tools.length})):this.refreshTools();}}}}),await this.client.connect(this.transport),this.running=true,await this.refreshTools(),this.logger.info({serverId:r,toolCount:this.tools.length},"Stdio client started"),this.onEvent(`plugin:mcp:client:${r}:started`,{serverId:r,toolCount:this.tools.length});}async stop(){this.stopping=true,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null),this.stabilityTimer&&(clearTimeout(this.stabilityTimer),this.stabilityTimer=null);let{serverId:e}=this.options;if(this.client){try{await this.client.close();}catch{}this.client=null;}if(this.transport){let t=this.transport;if(typeof t.close=="function")try{await Promise.resolve(t.close());}catch{}this.transport=null;}this.running=false,this.logger.info({serverId:e},"Stdio client stopped"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"graceful"});}async callTool(e,t){if(!this.running||!this.client)throw new Error(`Stdio client "${this.options.serverId}" is not running. Cannot call tool "${e}".`);let r=await this.client.callTool({name:e,arguments:t});return q(r)}getTools(){return [...this.tools]}isRunning(){return this.running}async refreshTools(){if(!this.client)return;let{serverId:e}=this.options;try{let t=await this.client.listTools();this.tools=t.tools??[],this.onToolsUpdated(e,this.tools),this.onEvent(`plugin:mcp:client:${e}:tools:listed`,{serverId:e,toolCount:this.tools.length});}catch(t){this.logger.warn({err:t,serverId:e},"Failed to list tools for stdio client");}}handleDisconnect(){let{serverId:e,maxRestarts:t,restartDelayMs:r,restartBackoffMultiplier:i}=this.options;if(this.tools.length>0&&(this.tools=[],this.onToolsUpdated(e,[])),this.logger.warn({serverId:e},"Stdio client disconnected unexpectedly"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"unexpected"}),this.restartCount>=t){this.logger.error({serverId:e,restartCount:this.restartCount,maxRestarts:t},"Stdio client exceeded max restarts, giving up"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:new Error(`Max restarts (${t}) exceeded for stdio client "${e}"`)});return}let o=r*Math.pow(i,this.restartCount);this.logger.info({serverId:e,restartCount:this.restartCount,delayMs:o},"Scheduling stdio client restart"),this.restartTimer=setTimeout(()=>{this.restartTimer=null,this.restart();},o);}async restart(){let{serverId:e}=this.options;this.restartCount++,this.client=null,this.transport=null;try{await this.start(),this.logger.info({serverId:e,restartCount:this.restartCount},"Stdio client restarted successfully"),this.onEvent(`plugin:mcp:client:${e}:restarted`,{serverId:e,restartCount:this.restartCount});let t=this.options.restartDelayMs*2;this.stabilityTimer=setTimeout(()=>{this.stabilityTimer=null,this.running&&(this.restartCount=0);},t);}catch(t){this.logger.error({err:t,serverId:e,restartCount:this.restartCount},"Stdio client restart failed"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:t}),this.handleDisconnect();}}};var C=class{tools=new Map;setToolsForSource(e,t,r){let i=new Map;for(let o of r){let a={name:o.name,inputSchema:o.inputSchema,source:e,transport:t};o.description!==void 0&&(a.description=o.description),i.set(o.name,a);}this.tools.set(e,i);}removeSource(e){this.tools.delete(e);}getTools(){let e=[];for(let t of this.tools.values())for(let r of t.values())e.push(r);return e}getToolsByServer(e){let t=this.tools.get(e);return t?Array.from(t.values()):[]}getTool(e){for(let t of this.tools.values()){let r=t.get(e);if(r)return r}}getToolBySource(e,t){return this.tools.get(e)?.get(t)}};function le(n){return "transport"in n&&n.transport==="stdio"}function pe(n={}){se(n);let e=null,t=new Map,r=new Map,i=[],o=null;return {async apply(s){if(s.setStore(S,true),o=new C,s.setStore(j,o),s.setStore(T,t),n.clients&&Object.keys(n.clients).length>0){let l=Object.entries(n.clients),p=new Map;for(let[c,d]of l)p.set(c,d);s.setStore(w,p);for(let[c,d]of l){if(le(d))await a(s,c,d,o);else {let y=d;await m(s,c,y.url,o),g(s,c,y.url,o);}let f=le(d)?"stdio":"http";s.emit(`plugin:mcp:client:${c}:registered`,{serverId:c,transport:f});}}e=new b(s,n),await e.start();},async teardown(s){for(let l of i)clearInterval(l);i.length=0;for(let[l,p]of r)try{await p.close();}catch(c){s.logger.error({err:c,serverId:l,operation:"close"},"Failed to close HTTP client");}r.clear();for(let[l,p]of t)try{await p.stop();}catch(c){s.logger.error({err:c,serverId:l,operation:"stop"},"Failed to stop stdio client");}if(t.clear(),e){try{await e.stop();}catch(l){s.logger.error({err:l,operation:"stop"},"Failed to stop MCP server plugin");}e=null;}o=null;}};async function a(s,l,p,c){let d={serverId:l,command:p.command,args:p.args??[],maxRestarts:n.maxRestarts??5,restartDelayMs:n.restartDelayMs??1e3,restartBackoffMultiplier:n.restartBackoffMultiplier??2};p.env!==void 0&&(d.env=p.env),p.cwd!==void 0&&(d.cwd=p.cwd);let f=new H(d,s.logger,(y,h)=>{s.emit(y,h);},(y,h)=>{c.setToolsForSource(y,"stdio",h.map(v=>{let z={name:v.name,inputSchema:v.inputSchema};return v.description!==void 0&&(z.description=v.description),z}));});t.set(l,f);try{await f.start();}catch(y){s.logger.error({err:y,serverId:l,operation:"start"},"Failed to start stdio client"),s.emit(`plugin:mcp:client:${l}:error`,{serverId:l,error:y});}}async function u(s,l){let p=r.get(s);if(p)return p;let{Client:c}=await import('@modelcontextprotocol/sdk/client/index.js'),{StreamableHTTPClientTransport:d}=await import('@modelcontextprotocol/sdk/client/streamableHttp.js'),f=new d(new URL(l)),y=new c({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}});await y.connect(f);let h=y;return r.set(s,h),h}async function m(s,l,p,c){try{let y=(await(await u(l,p)).listTools()).tools??[];c.setToolsForSource(l,"http",y.map(h=>{let v={name:h.name,inputSchema:h.inputSchema};return h.description!==void 0&&(v.description=h.description),v})),s.emit(`plugin:mcp:client:${l}:tools:listed`,{serverId:l,toolCount:y.length});}catch(d){r.delete(l),s.logger.warn({err:d,serverId:l,url:p,operation:"listTools"},"Failed to list tools from HTTP client");}}function g(s,l,p,c){let d=n.toolRefreshIntervalMs??6e4;if(d<=0)return;let f=setInterval(()=>{m(s,l,p,c);},d);i.push(f);}}var F=class{constructor(e){this._options=e;}async run(e){return this._options,e.logger&&e.logger.debug({adapter:"agent"},"Agent pass-through \u2014 implementation pending"),{output:e.body,steps:0}}};function Ve(n){let e=n.indexOf(":");if(e<1||e===n.length-1)throw new Error(`Agent adapter: modelId must be "providerId:modelName" (e.g. ollama:llama3). Got: "${n}"`)}var x=class{adapterId="routecraft.adapter.agent";runner;constructor(e){Ve(e.modelId),this.runner=new F(e);}async send(e){return this.runner.run(e)}};function de(n){return new x(n)}var V=new Map;async function K(){let n=[...V.entries()];V.clear(),n.length!==0&&await Promise.all(n.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function Ke(n){return n.includes("/")?n:`Xenova/${n}`}var Be='The Hugging Face embedding provider requires "@huggingface/transformers". Install it with: pnpm add @huggingface/transformers';function We(n,e){if(!(n instanceof Error))return false;let t=n.message??"";return (t.includes("ERR_MODULE_NOT_FOUND")||t.includes("Cannot find module")||t.includes("Cannot find package"))&&t.includes(e)}async function Je(n){let e=Ke(n),t=V.get(e);if(t)return t.run;let r;try{r=(await import('@huggingface/transformers')).pipeline;}catch(u){throw We(u,"@huggingface/transformers")?new Error(Be):u}let i=await r("feature-extraction",e,{dtype:"fp32"}),o=(u,m)=>i(u,{pooling:m?.pooling??"mean",normalize:m?.normalize??true}),a={run:o,dispose:typeof i.dispose=="function"?()=>Promise.resolve(i.dispose()):void 0};return V.set(e,a),o}function ce(n){if(n===null)return "null";if(typeof n!="object")return String(n);if(Array.isArray(n))return `array(${n.length})`;try{let e=JSON.stringify(n);return e.length>100?e.slice(0,100)+"...":e}catch{return "object"}}function ze(n){if(Array.isArray(n))return n;if(n instanceof Float32Array)return Array.from(n);if(n&&typeof n=="object"&&"data"in n){let e=n.data;if(Array.isArray(e))return e;if(e instanceof Float32Array)return Array.from(e);throw new Error(`Embedding output .data must be an Array or Float32Array; got ${typeof e}: ${ce(e)}`)}throw new Error(`Embedding output must be an Array, Float32Array, or object with .data; got ${typeof n}: ${ce(n)}`)}async function ue(n){let{config:e,modelName:t,text:r}=n;if(e.provider==="mock"){let o=[];for(let a=0;a<8;a++)o.push((r.length+a)%100/100);return o}if(e.provider==="huggingface"){let o=await(await Je(t))(r,{pooling:"mean",normalize:true}),a=o&&typeof o=="object"&&"data"in o?o.data:o;return ze(a)}throw e.provider==="ollama"||e.provider==="openai"?new Error(`Embedding provider "${e.provider}" is not yet implemented. Use huggingface for now.`):new Error(`Unknown embedding provider: ${e.provider}`)}var B=Symbol.for("routecraft.adapter.embedding.providers"),W=Symbol.for("routecraft.adapter.embedding.options");function Xe(n){let e=n.indexOf(":");if(e<1||e===n.length-1)throw new Error(`Embedding adapter: model id must be "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2). Got: "${n}"`);return {providerId:n.slice(0,e),modelName:n.slice(e+1)}}function Qe(n,e){if(!e)throw new Error(`Embedding adapter: model id "${n}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so embedding providers can be read.`);let t=e.getStore(B);if(!t)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:r,modelName:i}=Xe(n),o=t.get(r);if(!o)throw new Error(`Embedding provider "${r}" not found. Register it with embeddingPlugin({ providers: { "${r}": {} } }).`);return {config:o,modelName:i}}function Ze(n){return e=>{let t=n(e);return Array.isArray(t)?t.filter(Boolean).join(" | "):t}}var k=class{constructor(e,t={}){this.modelId=e;this.options=t;}adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore(W),...this.options}}async send(e){let t=routecraft.getExchangeContext(e),{config:r,modelName:i}=Qe(this.modelId,t),o=this.mergedOptions(t);if(!o.using)throw new Error("Embedding adapter: options.using(exchange) is required to build the string to embed.");let u=Ze(o.using)(e);return {embedding:await ue({config:r,modelName:i,text:u})}}};function me(n,e){return new k(n,e)}function et(n,e){return {...e,provider:n}}function ge(n={providers:{}}){return {apply(e){let t=new Map;for(let[r,i]of Object.entries(n.providers))i!==void 0&&t.set(r,et(r,i));e.setStore(B,t),n.defaultOptions&&Object.keys(n.defaultOptions).length>0&&e.setStore(W,n.defaultOptions);},async teardown(){await K();}}}
|
|
2
|
-
exports.ADAPTER_LLM_OPTIONS=
|
|
1
|
+
'use strict';var routecraft=require('@routecraft/routecraft'),ai=require('ai'),http=require('http');var Y={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function fe(r,e){return typeof r=="object"&&r!==null&&r[e]===true}function ye(r){return fe(r,Y.McpAdapter)}function O(r,e){throw new Error(`The ${e} LLM provider requires the "${r}" package. Install it with: pnpm add ${r}`)}function L(r,e){if(!(r instanceof Error))return false;let t=r.message??"";return (t.includes("ERR_MODULE_NOT_FOUND")||t.includes("Cannot find module")||t.includes("Cannot find package"))&&t.includes(e)}var he={ollama:{baseURL:"http://localhost:11434/api"}};function A(r){return {...r.inputTokens!==void 0&&{inputTokens:r.inputTokens},...r.outputTokens!==void 0&&{outputTokens:r.outputTokens},...r.totalTokens!==void 0&&{totalTokens:r.totalTokens}}}function I(r){try{if("output"in r&&r.output!==void 0)return r.output}catch{}}function X(r,e,t){if(r===null||typeof r!="object")throw new Error(`[${e}] Invalid model: expected an object, got ${typeof r}. Model id: ${t}`);let n=r;if(typeof n.doGenerate!="function")throw new Error(`[${e}] Invalid model: missing or invalid doGenerate method. Model id: ${t}. Ensure the provider returns an AI SDK-compatible language model.`);if(typeof n.doStream!="function")throw new Error(`[${e}] Invalid model: missing or invalid doStream method. Model id: ${t}. Ensure the provider returns an AI SDK-compatible language model.`)}async function Q(r){let{config:e,modelId:t,options:n,systemPrompt:i,userPrompt:o,output:a}=r;switch(e.provider){case "openai":return we(e,t,n,i,o,a);case "anthropic":return ve(e,t,n,i,o,a);case "gemini":return Pe(e,t,n,i,o,a);case "openrouter":return Me(e,t,n,i,o,a);case "ollama":return Re(e,t,n,i,o,a);default:{let u=e;throw new Error(`LLM provider not implemented: ${u.provider}`)}}}async function we(r,e,t,n,i,o){let a;try{a=(await import('@ai-sdk/openai')).createOpenAI;}catch(y){throw L(y,"@ai-sdk/openai")&&O("@ai-sdk/openai","OpenAI"),y}let{generateText:u}=await import('ai'),m={apiKey:r.apiKey};r.baseURL!==void 0&&(m.baseURL=r.baseURL);let l={model:a(m)(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};n&&(l.system=n),t.topP!==void 0&&(l.topP=t.topP),t.frequencyPenalty!==void 0&&(l.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(l.presencePenalty=t.presencePenalty);let p=o!==void 0?{...l,output:o}:l,c=await u(p),d={text:c.text??"",raw:c};c.usage&&(d.usage=A(c.usage));let f=I(c);return f!==void 0&&(d.output=f),d}async function ve(r,e,t,n,i,o){let a;try{a=(await import('@ai-sdk/anthropic')).createAnthropic;}catch(f){throw L(f,"@ai-sdk/anthropic")&&O("@ai-sdk/anthropic","Anthropic"),f}let{generateText:u}=await import('ai'),s={model:a({apiKey:r.apiKey})(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};n&&(s.system=n),t.topP!==void 0&&(s.topP=t.topP),t.frequencyPenalty!==void 0&&(s.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(s.presencePenalty=t.presencePenalty);let l=o!==void 0?{...s,output:o}:s,p=await u(l),c={text:p.text??"",raw:p};p.usage&&(c.usage=A(p.usage));let d=I(p);return d!==void 0&&(c.output=d),c}async function Pe(r,e,t,n,i,o){let a;try{a=(await import('@ai-sdk/google')).createGoogleGenerativeAI;}catch(f){throw L(f,"@ai-sdk/google")&&O("@ai-sdk/google","Gemini"),f}let{generateText:u}=await import('ai'),s={model:a({apiKey:r.apiKey})(e),prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};n&&(s.system=n),t.topP!==void 0&&(s.topP=t.topP),t.frequencyPenalty!==void 0&&(s.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(s.presencePenalty=t.presencePenalty);let l=o!==void 0?{...s,output:o}:s,p=await u(l),c={text:p.text??"",raw:p};p.usage&&(c.usage=A(p.usage));let d=I(p);return d!==void 0&&(c.output=d),c}async function Me(r,e,t,n,i,o){let a;try{a=(await import('@openrouter/ai-sdk-provider')).createOpenRouter;}catch(h){throw L(h,"@openrouter/ai-sdk-provider")&&O("@openrouter/ai-sdk-provider","OpenRouter"),h}let{generateText:u}=await import('ai'),m=a({apiKey:r.apiKey}),g=r.modelId??e,s=m.chat(g);X(s,"OpenRouter",g);let p={model:s,prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};n&&(p.system=n),t.topP!==void 0&&(p.topP=t.topP),t.frequencyPenalty!==void 0&&(p.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(p.presencePenalty=t.presencePenalty);let c=o!==void 0?{...p,output:o}:p,d=await u(c),f={text:d.text??"",raw:d};d.usage&&(f.usage=A(d.usage));let y=I(d);return y!==void 0&&(f.output=y),f}async function Re(r,e,t,n,i,o){let a;try{a=(await import('ollama-ai-provider-v2')).createOllama;}catch(h){throw L(h,"ollama-ai-provider-v2")&&O("ollama-ai-provider-v2","Ollama"),h}let{generateText:u}=await import('ai'),m=a({baseURL:r.baseURL??he.ollama.baseURL}),g=r.modelId??e,s=m(g);X(s,"Ollama",g);let p={model:s,prompt:i,...t.maxTokens!==void 0&&{maxOutputTokens:t.maxTokens},temperature:t.temperature};n&&(p.system=n),t.topP!==void 0&&(p.topP=t.topP),t.frequencyPenalty!==void 0&&(p.frequencyPenalty=t.frequencyPenalty),t.presencePenalty!==void 0&&(p.presencePenalty=t.presencePenalty);let c=o!==void 0?{...p,output:o}:p,d=await u(c),f={text:d.text??"",raw:d};d.usage&&(f.usage=A(d.usage));let y=I(d);return y!==void 0&&(f.output=y),f}function Z(r){let e=r["~standard"];if(!e?.validate)throw new Error("LLM outputSchema must be a StandardSchemaV1 with ~standard.validate");let t=e.jsonSchema?.output?.({target:"draft-2020-12"})??e.jsonSchema?.input?.({target:"draft-2020-12"});if(!t||typeof t!="object")throw new Error("LLM outputSchema must expose ~standard.jsonSchema.output or .input for provider structured output");function n(o){let a;try{a=e.validate(o);}catch(m){return {success:false,error:m instanceof Error?m:new Error(String(m))}}return a instanceof Promise?{success:false,error:new Error("Async output schema is not supported for LLM structured output")}:a.issues!=null&&(Array.isArray(a.issues)?a.issues.length>0:typeof a.issues=="object"&&a.issues!==null?Object.keys(a.issues).length>0:!!a.issues)?{success:false,error:new Error(typeof a.issues=="string"?a.issues:JSON.stringify(a.issues))}:{success:true,value:a.value}}let i=ai.jsonSchema(t,{validate:n});return ai.Output.object({schema:i})}var P=Symbol.for("routecraft.adapter.llm.providers"),R=Symbol.for("routecraft.adapter.llm.options");async function be(r,e){let t;try{t=JSON.parse(r);}catch{return}let n=e["~standard"];if(!n?.validate)return;let i=n.validate(t);if(i instanceof Promise&&(i=await i),!(i&&typeof i=="object"&&"issues"in i&&i.issues))return i&&typeof i=="object"&&"value"in i?i.value:void 0}var Ce=0,xe=1024;function ee(r,e){return r===void 0||r===""?"":typeof r=="function"?r(e):r}function ke(r){let e=r.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}function te(r){let e=r.indexOf(":");if(e<1||e===r.length-1)throw new Error(`LLM adapter: model id must be "providerId:modelName" (e.g. ollama:lfm2.5-thinking). Got: "${r}"`);return {providerId:r.slice(0,e),modelName:r.slice(e+1)}}function Oe(r,e){if(!e)throw new Error(`LLM adapter: model id "${r}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${P.description}" can be read.`);let t=e.getStore(P);if(!t)throw new Error('LLM provider not found: no providers registered. Add llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }) to your config.');let{providerId:n,modelName:i}=te(r),o=t.get(n);if(!o)throw new Error(`LLM provider "${n}" not found. Register it with llmPlugin({ providers: { "${n}": { provider, apiKey?, baseURL? } } }).`);return {config:o,modelName:i}}var E=class{constructor(e,t={}){this.modelId=e;this.options=t;}adapterId="routecraft.adapter.llm";options;mergedOptions(e){let t=e.getStore(R);return {temperature:Ce,maxTokens:xe,...t,...this.options}}async send(e){let t=routecraft.getExchangeContext(e),{config:n,modelName:i}=Oe(this.modelId,t),o=this.mergedOptions(t),a=ee(o.systemPrompt,e),u=ee(o.userPrompt,e)||ke(e),m={temperature:o.temperature,maxTokens:o.maxTokens};o.topP!==void 0&&(m.topP=o.topP),o.frequencyPenalty!==void 0&&(m.frequencyPenalty=o.frequencyPenalty),o.presencePenalty!==void 0&&(m.presencePenalty=o.presencePenalty);let g=o.outputSchema!==void 0?Z(o.outputSchema):void 0,s=await Q({config:n,modelId:i,options:m,systemPrompt:a,userPrompt:u,output:g});if(s.output===void 0&&s.text&&o.outputSchema!==void 0){let l=await be(s.text,o.outputSchema);l!==void 0&&(s.output=l);}return s}getMetadata(e){let t=e,{providerId:n}=te(this.modelId),i={model:this.modelId,provider:n};return t.usage&&(t.usage.inputTokens!==void 0&&(i.inputTokens=t.usage.inputTokens),t.usage.outputTokens!==void 0&&(i.outputTokens=t.usage.outputTokens)),i}};function re(r,e){return new E(r,e)}var ne=["openai","anthropic","openrouter","ollama","gemini"];function Le(r){return ne.includes(r)}function N(r){if(!r||typeof r!="object")throw new TypeError("llmPlugin: options must be an object");if(!r.providers||typeof r.providers!="object")throw new TypeError("llmPlugin: options.providers must be an object (record of provider id \u2192 options)");for(let[e,t]of Object.entries(r.providers))if(t!==void 0){if(!t||typeof t!="object")throw new TypeError(`llmPlugin: providers["${e}"] must be an object`);if(!Le(e))throw new TypeError(`llmPlugin: providers["${e}"] is not a supported provider. Supported: ${ne.join(", ")}`);switch(e){case "openai":case "anthropic":case "openrouter":case "gemini":if(typeof t.apiKey!="string"||!t.apiKey.trim())throw new TypeError(`llmPlugin: providers["${e}"].apiKey is required`);break;case "ollama":if(t.baseURL!==void 0&&typeof t.baseURL!="string")throw new TypeError(`llmPlugin: providers["${e}"].baseURL must be a string when provided`);if(t.modelId!==void 0&&(typeof t.modelId!="string"||!t.modelId.trim()))throw new TypeError(`llmPlugin: providers["${e}"].modelId must be a non-empty string when provided`);break}}if(r.defaultOptions!==void 0&&(typeof r.defaultOptions!="object"||r.defaultOptions===null))throw new TypeError("llmPlugin: defaultOptions must be an object when provided")}var Ae=["openai","anthropic","openrouter","ollama","gemini"];function Ie(r,e){return {provider:r,...e}}function oe(r={providers:{}}){return N(r),{apply(e){let t=new Map;for(let n of Ae){let i=r.providers[n];i!==void 0&&t.set(n,Ie(n,i));}e.setStore(P,t),r.defaultOptions&&Object.keys(r.defaultOptions).length>0&&e.setStore(R,r.defaultOptions);}}}var S=Symbol.for("routecraft.mcp.plugin.registered"),w=Symbol.for("routecraft.mcp.client.servers"),j=Symbol.for("routecraft.mcp.tool.registry"),T=Symbol.for("routecraft.mcp.stdio.managers");var M=Symbol.for("routecraft.mcp.adapter");var $=class{adapterId="routecraft.adapter.mcp";endpoint;options;constructor(e,t){if(this[M]=true,typeof e!="string")throw routecraft.rcError("RC5003",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.rcError("RC5003",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.rcError("RC5003",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."});if(!("description"in t)||typeof t.description!="string")throw routecraft.rcError("RC5003",void 0,{message:"mcp(endpoint, options) as source requires options.description",suggestion:"Use .from(mcp('endpoint', { description: '...' })) to define a source."});this.endpoint=e,this.options=t;}async subscribe(e,t,n,i){if(e.getStore(S)!==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(e,t,n,i)}};function q(r){let e=r?.content;if(!Array.isArray(e)||e.length===0)return r;if(e.length===1){let t=e[0];if(t.type==="text"&&typeof t.text=="string")return t.text;if(typeof t.data=="string")return t.data}return e}function Ne(r){let e=r.trim().toLowerCase();if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error(`MCP client: url must be HTTP or HTTPS. Stdio is not supported in routes; register stdio clients via mcpPlugin({ clients: { name: { command, args } } }). Got: "${r.slice(0,50)}${r.length>50?"...":""}"`)}function je(r,e){if(r.url)return Ne(r.url),r.url;if(r.serverId&&!e)throw new Error(`MCP client: serverId "${r.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${String(w)}" can be read.`);if(r.serverId&&e){let n=e.getStore(w)?.get(r.serverId);if(!n)throw new Error(`MCP client: serverId "${r.serverId}" not found in context store. Register it with context store key "${String(w)}".`);return typeof n=="string"?n:n.url}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var G=r=>typeof r.body=="object"&&r.body!==null?r.body:{input:r.body},D=class{constructor(e){this.options=e;if(this[M]=true,!e.url&&!e.serverId)throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.");if(e.url&&e.serverId)throw new Error("MCP client: cannot provide both url and serverId. Use either url for direct HTTP or serverId for registered servers.")}adapterId="routecraft.adapter.mcp";async send(e){let t=routecraft.getExchangeContext(e),n=this.options.tool??(typeof e.body=="object"&&e.body!==null&&"tool"in e.body&&typeof e.body.tool=="string"?e.body.tool:void 0);if(!n)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let o=(this.options.args??G)(e);if(this.options.serverId&&t){let g=t.getStore(T)?.get(this.options.serverId);if(g){let p=await g.callTool(n,o);return p&&typeof p=="object"&&(p.metadata={toolName:n,transport:"stdio",serverId:this.options.serverId}),p}let l=t.getStore(w)?.get(this.options.serverId);if(l&&typeof l=="object"&&"transport"in l&&l.transport==="stdio")throw new Error(`MCP client: stdio server "${this.options.serverId}" is not running. Ensure mcpPlugin is applied and the stdio client started successfully.`)}let a=je(this.options,t),u=await this.callRemoteTool(a,n,o);return u&&typeof u=="object"&&(u.metadata={toolName:n,url:a,transport:"http",...this.options.serverId?{serverId:this.options.serverId}:{}}),u}getMetadata(e){return e&&typeof e=="object"&&"metadata"in e?e.metadata:{toolName:"unknown",transport:"unknown"}}async callRemoteTool(e,t,n){let i,o;try{i=await import('@modelcontextprotocol/sdk/client/index.js'),o=await import('@modelcontextprotocol/sdk/client/streamableHttp.js');}catch{throw new Error('MCP client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk')}let a=i.Client,u=o.StreamableHTTPClientTransport,m=new URL(e),g=new u(m),s={name:"routecraft-mcp-client",version:"1.0.0"},l=new a(s,{capabilities:{}});try{await l.connect.call(l,g);let d=await l.callTool.call(l,{name:t,arguments:n});return q(d)}finally{let p=l,c=p.close??p.disconnect;if(typeof c=="function")try{await Promise.resolve(c.call(l));}catch{}let d=g,f=d.close??d.destroy;if(typeof f=="function")try{await Promise.resolve(f.call(g));}catch{}}}};function ie(r,e){if(typeof r=="object"&&r!==null&&("url"in r||"serverId"in r))return new D(r);let t=e===void 0||typeof e=="object"&&e!==null&&!("description"in e);if(typeof r=="string"&&r.includes(":")&&t){let i=r.indexOf(":"),o=r.slice(0,i),a=r.slice(i+1),u={serverId:o,tool:a};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(u.args=e.args),new D(u)}let n=r;if(e!==void 0)return new $(n,e);throw routecraft.rcError("RC5003",void 0,{message:"mcp() with only an endpoint is not supported. Use direct('endpoint') for in-process. For MCP server use .from(mcp('endpoint', { description: '...' })); for client use .to(mcp({ url, tool })) or .to(mcp('server:tool', { args })).",suggestion:"Use .from(mcp('endpoint', { description: '...' })) or .to(mcp({ url, tool })) or direct('endpoint')."})}var J='MCP server requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',b=class{context;options;server=null;transport=null;httpServer=null;running=false;toolsListLogged=false;constructor(e,t={}){this.context=e,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...t};}async start(){if(this.running){this.context.logger.warn({},"MCP server already running");return}try{let e=this.options.transport;e==="http"?await this.startHttp():await this.startStdio(),this.running=!0,this.context.logger.info({name:this.options.name,version:this.options.version,transport:e},"MCP server started"),this.logExposedToolsOnce();}catch(e){let t=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Failed to start MCP server";throw this.context.logger.error({err:e},t),e}}async startStdio(){let e,t;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server,t=(await import('@modelcontextprotocol/sdk/server/stdio.js')).StdioServerTransport;}catch{throw new Error(J)}this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers(),this.transport=new t,await this.server.connect(this.transport);}async startHttp(){let e;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server;}catch{throw new Error(J)}let n=(await import('@modelcontextprotocol/sdk/server/streamableHttp.js').catch(()=>null))?.StreamableHTTPServerTransport;if(!n)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers();let i=this.options.port,o=this.options.host;this.transport=new n({sessionIdGenerator:()=>crypto.randomUUID(),enableJsonResponse:true}),await this.server.connect(this.transport);let u=this.transport.handleRequest;if(typeof u!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");this.httpServer=http.createServer(async(g,s)=>{let l=g.url?.split("?")[0]??"";if(l!=="/mcp"&&l!=="/mcp/"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not Found",path:l}));return}try{await u.call(this.transport,g,s);}catch(p){let c=routecraft.isRoutecraftError(p)?p.meta.message:p instanceof Error?p.message:"MCP HTTP request error";this.context.logger.error({err:p},c),s.headersSent||(s.writeHead(500,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Internal Server Error"})));}}),await new Promise((g,s)=>{this.httpServer.listen(i,o,()=>g()),this.httpServer.on("error",l=>{let p=routecraft.isRoutecraftError(l)?l.meta.message:l instanceof Error?l.message:"MCP HTTP server listen failed";this.context.logger.error({err:l},p),s(l);});});let m=this.getHttpPort()??i;this.context.logger.info({host:o,port:m,path:"/mcp"},"MCP HTTP server listening");}getHttpPort(){let e=this.httpServer?.address();if(e&&typeof e=="object"&&"port"in e)return e.port}async setupRequestHandlers(){let e;try{e=await import('@modelcontextprotocol/sdk/types.js');}catch{throw new Error(J)}let t=e,n=t.ListToolsRequestSchema,i=t.CallToolRequestSchema;if(!n||!i)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let o=this.server;o.setRequestHandler(n,async()=>{let a=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:a}}),o.setRequestHandler(i,async a=>{let m=a.params;return await this.handleToolCall(m.name||"",m.arguments||{})});}async stop(){if(this.running)try{if(this.httpServer&&(await new Promise(e=>{this.httpServer.close(()=>e());}),this.httpServer=null),this.transport){let e=this.transport;typeof e.close=="function"&&await e.close();}this.running=!1,this.context.logger.info({},"MCP server stopped");}catch(e){let t=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Error stopping MCP server";this.context.logger.error({err:e},t);}}logExposedToolsOnce(){if(this.toolsListLogged)return;let e=this.getAvailableTools();if(e.length===0)return;let t=e.map(n=>n.name??"?");this.context.logger.info({tools:t,count:t.length},"Exposing MCP tools"),this.toolsListLogged=true;}getAvailableTools(){let e=this.context.getStore(routecraft.ADAPTER_DIRECT_REGISTRY);if(!e)return [];let t=Array.from(e.values()).filter(i=>i.description!==void 0),n=this.options.tools;if(n)if(Array.isArray(n)){let i=new Set(n);t=t.filter(o=>i.has(o.endpoint));}else typeof n=="function"&&(t=t.filter(n));return t.map(i=>this.metadataToMcpTool(i))}metadataToMcpTool(e){return {name:e.endpoint,description:e.description||"",inputSchema:this.schemaToJsonSchema(e.schema)}}schemaToJsonSchema(e){if(!e||typeof e!="object")return {type:"object"};let t=e["~standard"];if(t?.jsonSchema?.input)try{let n=t.jsonSchema.input({target:"draft-2020-12"});return typeof n=="object"&&n!==null?n:{type:"object"}}catch(n){return this.context.logger.debug(n,"Standard JSON Schema conversion failed"),{type:"object"}}return "~standard"in e?{type:"object",additionalProperties:true}:{type:"object"}}async handleToolCall(e,t){try{let n=this.context.getStore(routecraft.ADAPTER_DIRECT_STORE);if(!n){let s=new Error("No direct channels available");return this.context.emit("error",{error:s}),{content:[{type:"text",text:"Error: No direct channels available"}]}}let i=n.get(e);if(!i){let s=new Error(`Tool not found: ${e}`);return this.context.emit("error",{error:s}),{content:[{type:"text",text:`Error: Tool not found: ${e}`}]}}let o=typeof t=="string"?(()=>{try{return JSON.parse(t)||{}}catch{return {input:t}}})():t&&typeof t=="object"?t:{};this.context.logger.debug({bodyType:typeof o,body:o},"MCP tool call exchange body");let a=new routecraft.DefaultExchange(this.context,{body:o,headers:{"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`}}),m=await i.send(e,a);return {content:[{type:"text",text:typeof m.body=="string"?m.body:JSON.stringify(m.body)}]}}catch(n){let i=routecraft.isRoutecraftError(n)?n.meta.message:n instanceof Error?n.message:String(n);return this.context.logger.error({tool:e,err:n},i),this.context.emit("error",{error:n}),{content:[{type:"text",text:`Error: ${i}`}]}}}};function se(r){if(r.transport==="http"){if(r.port!==void 0){if(typeof r.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(r.port<0||r.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(r.host!==void 0&&typeof r.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}if(r.clients){for(let[e,t]of Object.entries(r.clients))if(typeof t=="object"&&t!==null&&"transport"in t&&t.transport==="stdio"&&(!t.command||typeof t.command!="string"))throw new TypeError(`mcpPlugin: stdio client "${e}" must have a non-empty command string`)}if(r.maxRestarts!==void 0&&(typeof r.maxRestarts!="number"||!Number.isInteger(r.maxRestarts)||r.maxRestarts<0))throw new TypeError("mcpPlugin: maxRestarts must be a non-negative integer");if(r.restartDelayMs!==void 0&&(typeof r.restartDelayMs!="number"||r.restartDelayMs<=0))throw new TypeError("mcpPlugin: restartDelayMs must be a positive number");if(r.restartBackoffMultiplier!==void 0&&(typeof r.restartBackoffMultiplier!="number"||r.restartBackoffMultiplier<1))throw new TypeError("mcpPlugin: restartBackoffMultiplier must be >= 1");if(r.toolRefreshIntervalMs!==void 0&&(typeof r.toolRefreshIntervalMs!="number"||!Number.isInteger(r.toolRefreshIntervalMs)||r.toolRefreshIntervalMs<0))throw new TypeError("mcpPlugin: toolRefreshIntervalMs must be a non-negative integer")}async function ae(r,e){let t=e["~standard"];if(!t?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let n=t.validate(r);if(n instanceof Promise&&(n=await n),n.issues)throw new Error(`mcpPlugin options validation failed: ${JSON.stringify(n.issues)}`);if(n.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return n.value}var Fe='MCP stdio client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',H=class{constructor(e,t,n,i){this.options=e;this.logger=t;this.onEvent=n;this.onToolsUpdated=i;}client=null;transport=null;running=false;stopping=false;restartCount=0;restartTimer=null;stabilityTimer=null;tools=[];async start(){if(this.running)return;this.stopping=false,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null);let e,t;try{e=await import('@modelcontextprotocol/sdk/client/index.js'),t=await import('@modelcontextprotocol/sdk/client/stdio.js');}catch{throw new Error(Fe)}let{serverId:n,command:i,args:o,env:a,cwd:u}=this.options;this.transport=new t.StdioClientTransport({command:i,args:o??[],...a?{env:a}:{},...u?{cwd:u}:{},stderr:"pipe"});let m=this.transport;m.onerror=g=>{this.logger.error({err:g,serverId:n},"Stdio client transport error"),this.onEvent(`plugin:mcp:client:${n}:error`,{serverId:n,error:g});},m.onclose=()=>{this.stopping||(this.running=false,this.handleDisconnect());},m.stderr?.on&&m.stderr.on("data",g=>{this.logger.debug({serverId:n,stderr:String(g).trimEnd()},"Stdio client stderr output");}),this.client=new e.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{},listChanged:{tools:{onChanged:(g,s)=>{s&&Array.isArray(s.tools)?(this.tools=s.tools,this.onToolsUpdated(n,this.tools),this.onEvent(`plugin:mcp:client:${n}:tools:listed`,{serverId:n,toolCount:this.tools.length})):this.refreshTools();}}}}),await this.client.connect(this.transport),this.running=true,await this.refreshTools(),this.logger.info({serverId:n,toolCount:this.tools.length},"Stdio client started"),this.onEvent(`plugin:mcp:client:${n}:started`,{serverId:n,toolCount:this.tools.length});}async stop(){this.stopping=true,this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=null),this.stabilityTimer&&(clearTimeout(this.stabilityTimer),this.stabilityTimer=null);let{serverId:e}=this.options;if(this.client){try{await this.client.close();}catch{}this.client=null;}if(this.transport){let t=this.transport;if(typeof t.close=="function")try{await Promise.resolve(t.close());}catch{}this.transport=null;}this.running=false,this.logger.info({serverId:e},"Stdio client stopped"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"graceful"});}async callTool(e,t){if(!this.running||!this.client)throw new Error(`Stdio client "${this.options.serverId}" is not running. Cannot call tool "${e}".`);let n=await this.client.callTool({name:e,arguments:t});return q(n)}getTools(){return [...this.tools]}isRunning(){return this.running}async refreshTools(){if(!this.client)return;let{serverId:e}=this.options;try{let t=await this.client.listTools();this.tools=t.tools??[],this.onToolsUpdated(e,this.tools),this.onEvent(`plugin:mcp:client:${e}:tools:listed`,{serverId:e,toolCount:this.tools.length});}catch(t){this.logger.warn({err:t,serverId:e},"Failed to list tools for stdio client");}}handleDisconnect(){let{serverId:e,maxRestarts:t,restartDelayMs:n,restartBackoffMultiplier:i}=this.options;if(this.tools.length>0&&(this.tools=[],this.onToolsUpdated(e,[])),this.logger.warn({serverId:e},"Stdio client disconnected unexpectedly"),this.onEvent(`plugin:mcp:client:${e}:stopped`,{serverId:e,reason:"unexpected"}),this.restartCount>=t){this.logger.error({serverId:e,restartCount:this.restartCount,maxRestarts:t},"Stdio client exceeded max restarts, giving up"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:new Error(`Max restarts (${t}) exceeded for stdio client "${e}"`)});return}let o=n*Math.pow(i,this.restartCount);this.logger.info({serverId:e,restartCount:this.restartCount,delayMs:o},"Scheduling stdio client restart"),this.restartTimer=setTimeout(()=>{this.restartTimer=null,this.restart();},o);}async restart(){let{serverId:e}=this.options;this.restartCount++,this.client=null,this.transport=null;try{await this.start(),this.logger.info({serverId:e,restartCount:this.restartCount},"Stdio client restarted successfully"),this.onEvent(`plugin:mcp:client:${e}:restarted`,{serverId:e,restartCount:this.restartCount});let t=this.options.restartDelayMs*2;this.stabilityTimer=setTimeout(()=>{this.stabilityTimer=null,this.running&&(this.restartCount=0);},t);}catch(t){this.logger.error({err:t,serverId:e,restartCount:this.restartCount},"Stdio client restart failed"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:t}),this.handleDisconnect();}}};var C=class{tools=new Map;setToolsForSource(e,t,n){let i=new Map;for(let o of n){let a={name:o.name,inputSchema:o.inputSchema,source:e,transport:t};o.description!==void 0&&(a.description=o.description),i.set(o.name,a);}this.tools.set(e,i);}removeSource(e){this.tools.delete(e);}getTools(){let e=[];for(let t of this.tools.values())for(let n of t.values())e.push(n);return e}getToolsByServer(e){let t=this.tools.get(e);return t?Array.from(t.values()):[]}getTool(e){for(let t of this.tools.values()){let n=t.get(e);if(n)return n}}getToolBySource(e,t){return this.tools.get(e)?.get(t)}};function le(r){return "transport"in r&&r.transport==="stdio"}function pe(r={}){se(r);let e=null,t=new Map,n=new Map,i=[],o=null;return {async apply(s){if(s.setStore(S,true),o=new C,s.setStore(j,o),s.setStore(T,t),r.clients&&Object.keys(r.clients).length>0){let l=Object.entries(r.clients),p=new Map;for(let[c,d]of l)p.set(c,d);s.setStore(w,p);for(let[c,d]of l){if(le(d))await a(s,c,d,o);else {let y=d;await m(s,c,y.url,o),g(s,c,y.url,o);}let f=le(d)?"stdio":"http";s.emit(`plugin:mcp:client:${c}:registered`,{serverId:c,transport:f});}}e=new b(s,r),await e.start();},async teardown(s){for(let l of i)clearInterval(l);i.length=0;for(let[l,p]of n)try{await p.close();}catch(c){s.logger.error({err:c,serverId:l,operation:"close"},"Failed to close HTTP client");}n.clear();for(let[l,p]of t)try{await p.stop();}catch(c){s.logger.error({err:c,serverId:l,operation:"stop"},"Failed to stop stdio client");}if(t.clear(),e){try{await e.stop();}catch(l){s.logger.error({err:l,operation:"stop"},"Failed to stop MCP server plugin");}e=null;}o=null;}};async function a(s,l,p,c){let d={serverId:l,command:p.command,args:p.args??[],maxRestarts:r.maxRestarts??5,restartDelayMs:r.restartDelayMs??1e3,restartBackoffMultiplier:r.restartBackoffMultiplier??2};p.env!==void 0&&(d.env=p.env),p.cwd!==void 0&&(d.cwd=p.cwd);let f=new H(d,s.logger,(y,h)=>{s.emit(y,h);},(y,h)=>{c.setToolsForSource(y,"stdio",h.map(v=>{let z={name:v.name,inputSchema:v.inputSchema};return v.description!==void 0&&(z.description=v.description),z}));});t.set(l,f);try{await f.start();}catch(y){s.logger.error({err:y,serverId:l,operation:"start"},"Failed to start stdio client"),s.emit(`plugin:mcp:client:${l}:error`,{serverId:l,error:y});}}async function u(s,l){let p=n.get(s);if(p)return p;let{Client:c}=await import('@modelcontextprotocol/sdk/client/index.js'),{StreamableHTTPClientTransport:d}=await import('@modelcontextprotocol/sdk/client/streamableHttp.js'),f=new d(new URL(l)),y=new c({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}});await y.connect(f);let h=y;return n.set(s,h),h}async function m(s,l,p,c){try{let y=(await(await u(l,p)).listTools()).tools??[];c.setToolsForSource(l,"http",y.map(h=>{let v={name:h.name,inputSchema:h.inputSchema};return h.description!==void 0&&(v.description=h.description),v})),s.emit(`plugin:mcp:client:${l}:tools:listed`,{serverId:l,toolCount:y.length});}catch(d){n.delete(l),s.logger.warn({err:d,serverId:l,url:p,operation:"listTools"},"Failed to list tools from HTTP client");}}function g(s,l,p,c){let d=r.toolRefreshIntervalMs??6e4;if(d<=0)return;let f=setInterval(()=>{m(s,l,p,c);},d);i.push(f);}}var F=class{constructor(e){this._options=e;}async run(e){return this._options,e.logger&&e.logger.debug({adapter:"agent"},"Agent pass-through \u2014 implementation pending"),{output:e.body,steps:0}}};function Ve(r){let e=r.indexOf(":");if(e<1||e===r.length-1)throw new Error(`Agent adapter: modelId must be "providerId:modelName" (e.g. ollama:llama3). Got: "${r}"`)}var x=class{adapterId="routecraft.adapter.agent";runner;constructor(e){Ve(e.modelId),this.runner=new F(e);}async send(e){return this.runner.run(e)}};function de(r){return new x(r)}var V=new Map;async function K(){let r=[...V.entries()];V.clear(),r.length!==0&&await Promise.all(r.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function Ke(r){return r.includes("/")?r:`Xenova/${r}`}var Be='The Hugging Face embedding provider requires "@huggingface/transformers". Install it with: pnpm add @huggingface/transformers';function We(r,e){if(!(r instanceof Error))return false;let t=r.message??"";return (t.includes("ERR_MODULE_NOT_FOUND")||t.includes("Cannot find module")||t.includes("Cannot find package"))&&t.includes(e)}async function Je(r){let e=Ke(r),t=V.get(e);if(t)return t.run;let n;try{n=(await import('@huggingface/transformers')).pipeline;}catch(u){throw We(u,"@huggingface/transformers")?new Error(Be):u}let i=await n("feature-extraction",e,{dtype:"fp32"}),o=(u,m)=>i(u,{pooling:m?.pooling??"mean",normalize:m?.normalize??true}),a={run:o,dispose:typeof i.dispose=="function"?()=>Promise.resolve(i.dispose()):void 0};return V.set(e,a),o}function ce(r){if(r===null)return "null";if(typeof r!="object")return String(r);if(Array.isArray(r))return `array(${r.length})`;try{let e=JSON.stringify(r);return e.length>100?e.slice(0,100)+"...":e}catch{return "object"}}function ze(r){if(Array.isArray(r))return r;if(r instanceof Float32Array)return Array.from(r);if(r&&typeof r=="object"&&"data"in r){let e=r.data;if(Array.isArray(e))return e;if(e instanceof Float32Array)return Array.from(e);throw new Error(`Embedding output .data must be an Array or Float32Array; got ${typeof e}: ${ce(e)}`)}throw new Error(`Embedding output must be an Array, Float32Array, or object with .data; got ${typeof r}: ${ce(r)}`)}async function ue(r){let{config:e,modelName:t,text:n}=r;if(e.provider==="mock"){let o=[];for(let a=0;a<8;a++)o.push((n.length+a)%100/100);return o}if(e.provider==="huggingface"){let o=await(await Je(t))(n,{pooling:"mean",normalize:true}),a=o&&typeof o=="object"&&"data"in o?o.data:o;return ze(a)}throw e.provider==="ollama"||e.provider==="openai"?new Error(`Embedding provider "${e.provider}" is not yet implemented. Use huggingface for now.`):new Error(`Unknown embedding provider: ${e.provider}`)}var B=Symbol.for("routecraft.adapter.embedding.providers"),W=Symbol.for("routecraft.adapter.embedding.options");function Xe(r){let e=r.indexOf(":");if(e<1||e===r.length-1)throw new Error(`Embedding adapter: model id must be "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2). Got: "${r}"`);return {providerId:r.slice(0,e),modelName:r.slice(e+1)}}function Qe(r,e){if(!e)throw new Error(`Embedding adapter: model id "${r}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so embedding providers can be read.`);let t=e.getStore(B);if(!t)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:n,modelName:i}=Xe(r),o=t.get(n);if(!o)throw new Error(`Embedding provider "${n}" not found. Register it with embeddingPlugin({ providers: { "${n}": {} } }).`);return {config:o,modelName:i}}function Ze(r){return e=>{let t=r(e);return Array.isArray(t)?t.filter(Boolean).join(" | "):t}}var k=class{constructor(e,t={}){this.modelId=e;this.options=t;}adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore(W),...this.options}}async send(e){let t=routecraft.getExchangeContext(e),{config:n,modelName:i}=Qe(this.modelId,t),o=this.mergedOptions(t);if(!o.using)throw new Error("Embedding adapter: options.using(exchange) is required to build the string to embed.");let u=Ze(o.using)(e);return {embedding:await ue({config:n,modelName:i,text:u})}}};function me(r,e){return new k(r,e)}function et(r,e){return {...e,provider:r}}function ge(r={providers:{}}){return {apply(e){let t=new Map;for(let[n,i]of Object.entries(r.providers))i!==void 0&&t.set(n,et(n,i));e.setStore(B,t),r.defaultOptions&&Object.keys(r.defaultOptions).length>0&&e.setStore(W,r.defaultOptions);},async teardown(){await K();}}}
|
|
2
|
+
exports.ADAPTER_LLM_OPTIONS=R;exports.ADAPTER_LLM_PROVIDERS=P;exports.ADAPTER_MCP_CLIENT_SERVERS=w;exports.AgentDestinationAdapter=x;exports.BRAND=Y;exports.BRAND_MCP_ADAPTER=M;exports.EmbeddingDestinationAdapter=k;exports.LlmDestinationAdapter=E;exports.MCP_PLUGIN_REGISTERED=S;exports.MCP_STDIO_MANAGERS=T;exports.MCP_TOOL_REGISTRY=j;exports.McpServer=b;exports.McpToolRegistry=C;exports.agent=de;exports.defaultArgs=G;exports.disposeEmbeddingPipelineCache=K;exports.embedding=me;exports.embeddingPlugin=ge;exports.isMcpAdapter=ye;exports.llm=re;exports.llmPlugin=oe;exports.mcp=ie;exports.mcpPlugin=pe;exports.validateLlmPluginOptions=N;exports.validateWithSchema=ae;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|