@routecraft/ai 0.4.0-canary.8 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +315 -17
- package/dist/index.d.ts +315 -17
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
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(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=
|
|
1
|
+
'use strict';var routecraft=require('@routecraft/routecraft'),ai=require('ai'),crypto$1=require('crypto'),async_hooks=require('async_hooks'),http=require('http');var te={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function Te(t,e){return typeof t=="object"&&t!==null&&t[e]===true}function Ce(t){return Te(t,te.McpAdapter)}function L(t,e){throw new Error(`The ${e} LLM provider requires the "${t}" package. Install it with: pnpm add ${t}`)}function I(t,e){if(!(t instanceof Error))return false;let n=t.message??"";return (n.includes("ERR_MODULE_NOT_FOUND")||n.includes("Cannot find module")||n.includes("Cannot find package"))&&n.includes(e)}var xe={ollama:{baseURL:"http://localhost:11434/api"}};function D(t){return {...t.inputTokens!==void 0&&{inputTokens:t.inputTokens},...t.outputTokens!==void 0&&{outputTokens:t.outputTokens},...t.totalTokens!==void 0&&{totalTokens:t.totalTokens}}}function _(t){try{if("output"in t&&t.output!==void 0)return t.output}catch{}}function ne(t,e,n){if(t===null||typeof t!="object")throw new Error(`[${e}] Invalid model: expected an object, got ${typeof t}. Model id: ${n}`);let r=t;if(typeof r.doGenerate!="function")throw new Error(`[${e}] Invalid model: missing or invalid doGenerate method. Model id: ${n}. 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: ${n}. Ensure the provider returns an AI SDK-compatible language model.`)}async function re(t){let{config:e,modelId:n,options:r,systemPrompt:o,userPrompt:i,output:a}=t;switch(e.provider){case "openai":return ke(e,n,r,o,i,a);case "anthropic":return Oe(e,n,r,o,i,a);case "gemini":return Ae(e,n,r,o,i,a);case "openrouter":return Le(e,n,r,o,i,a);case "ollama":return Ie(e,n,r,o,i,a);default:{let u=e;throw new Error(`LLM provider not implemented: ${u.provider}`)}}}async function ke(t,e,n,r,o,i){let a;try{a=(await import('@ai-sdk/openai')).createOpenAI;}catch(h){throw I(h,"@ai-sdk/openai")&&L("@ai-sdk/openai","OpenAI"),h}let{generateText:u}=await import('ai'),d={apiKey:t.apiKey};t.baseURL!==void 0&&(d.baseURL=t.baseURL);let p={model:a(d)(e),prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(p.system=r),n.topP!==void 0&&(p.topP=n.topP),n.frequencyPenalty!==void 0&&(p.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(p.presencePenalty=n.presencePenalty);let l=i!==void 0?{...p,output:i}:p,m=await u(l),g={text:m.text??"",raw:m};m.usage&&(g.usage=D(m.usage));let c=_(m);return c!==void 0&&(g.output=c),g}async function Oe(t,e,n,r,o,i){let a;try{a=(await import('@ai-sdk/anthropic')).createAnthropic;}catch(c){throw I(c,"@ai-sdk/anthropic")&&L("@ai-sdk/anthropic","Anthropic"),c}let{generateText:u}=await import('ai'),s={model:a({apiKey:t.apiKey})(e),prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(s.system=r),n.topP!==void 0&&(s.topP=n.topP),n.frequencyPenalty!==void 0&&(s.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(s.presencePenalty=n.presencePenalty);let p=i!==void 0?{...s,output:i}:s,l=await u(p),m={text:l.text??"",raw:l};l.usage&&(m.usage=D(l.usage));let g=_(l);return g!==void 0&&(m.output=g),m}async function Ae(t,e,n,r,o,i){let a;try{a=(await import('@ai-sdk/google')).createGoogleGenerativeAI;}catch(c){throw I(c,"@ai-sdk/google")&&L("@ai-sdk/google","Gemini"),c}let{generateText:u}=await import('ai'),s={model:a({apiKey:t.apiKey})(e),prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(s.system=r),n.topP!==void 0&&(s.topP=n.topP),n.frequencyPenalty!==void 0&&(s.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(s.presencePenalty=n.presencePenalty);let p=i!==void 0?{...s,output:i}:s,l=await u(p),m={text:l.text??"",raw:l};l.usage&&(m.usage=D(l.usage));let g=_(l);return g!==void 0&&(m.output=g),m}async function Le(t,e,n,r,o,i){let a;try{a=(await import('@openrouter/ai-sdk-provider')).createOpenRouter;}catch(y){throw I(y,"@openrouter/ai-sdk-provider")&&L("@openrouter/ai-sdk-provider","OpenRouter"),y}let{generateText:u}=await import('ai'),d=a({apiKey:t.apiKey}),f=t.modelId??e,s=d.chat(f);ne(s,"OpenRouter",f);let l={model:s,prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(l.system=r),n.topP!==void 0&&(l.topP=n.topP),n.frequencyPenalty!==void 0&&(l.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(l.presencePenalty=n.presencePenalty);let m=i!==void 0?{...l,output:i}:l,g=await u(m),c={text:g.text??"",raw:g};g.usage&&(c.usage=D(g.usage));let h=_(g);return h!==void 0&&(c.output=h),c}async function Ie(t,e,n,r,o,i){let a;try{a=(await import('ollama-ai-provider-v2')).createOllama;}catch(y){throw I(y,"ollama-ai-provider-v2")&&L("ollama-ai-provider-v2","Ollama"),y}let{generateText:u}=await import('ai'),d=a({baseURL:t.baseURL??xe.ollama.baseURL}),f=t.modelId??e,s=d(f);ne(s,"Ollama",f);let l={model:s,prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(l.system=r),n.topP!==void 0&&(l.topP=n.topP),n.frequencyPenalty!==void 0&&(l.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(l.presencePenalty=n.presencePenalty);let m=i!==void 0?{...l,output:i}:l,g=await u(m),c={text:g.text??"",raw:g};g.usage&&(c.usage=D(g.usage));let h=_(g);return h!==void 0&&(c.output=h),c}function oe(t){let e=t["~standard"];if(!e?.validate)throw new Error("LLM outputSchema must be a StandardSchemaV1 with ~standard.validate");let n=e.jsonSchema?.output?.({target:"draft-2020-12"})??e.jsonSchema?.input?.({target:"draft-2020-12"});if(!n||typeof n!="object")throw new Error("LLM outputSchema must expose ~standard.jsonSchema.output or .input for provider structured output");function r(i){let a;try{a=e.validate(i);}catch(d){return {success:false,error:d instanceof Error?d:new Error(String(d))}}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(routecraft.formatSchemaIssues(a.issues))}:{success:true,value:a.value}}let o=ai.jsonSchema(n,{validate:r});return ai.Output.object({schema:o})}var M=Symbol.for("routecraft.adapter.llm.providers"),S=Symbol.for("routecraft.adapter.llm.options");async function Ue(t,e){let n;try{n=JSON.parse(t);}catch{return}let r=e["~standard"];if(!r?.validate)return;let o=r.validate(n);if(o instanceof Promise&&(o=await o),!(o&&typeof o=="object"&&"issues"in o&&o.issues))return o&&typeof o=="object"&&"value"in o?o.value:void 0}var Ne=0,$e=1024;function ie(t,e){return t===void 0||t===""?"":typeof t=="function"?t(e):t}function qe(t){let e=t.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}function se(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`LLM adapter: model id must be "providerId:modelName" (e.g. ollama:lfm2.5-thinking). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function Ge(t,e){if(!e)throw new Error(`LLM adapter: model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${M.description}" can be read.`);let n=e.getStore(M);if(!n)throw new Error('LLM provider not found: no providers registered. Add llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }) to your config.');let{providerId:r,modelName:o}=se(t),i=n.get(r);if(!i)throw new Error(`LLM provider "${r}" not found. Register it with llmPlugin({ providers: { "${r}": { provider, apiKey?, baseURL? } } }).`);return {config:i,modelName:o}}var E=class{constructor(e,n={}){this.modelId=e;this.options=n;}adapterId="routecraft.adapter.llm";options;mergedOptions(e){let n=e.getStore(S);return {temperature:Ne,maxTokens:$e,...n,...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:r,modelName:o}=Ge(this.modelId,n),i=this.mergedOptions(n),a=ie(i.systemPrompt,e),u=ie(i.userPrompt,e)||qe(e),d={temperature:i.temperature,maxTokens:i.maxTokens};i.topP!==void 0&&(d.topP=i.topP),i.frequencyPenalty!==void 0&&(d.frequencyPenalty=i.frequencyPenalty),i.presencePenalty!==void 0&&(d.presencePenalty=i.presencePenalty);let f=i.outputSchema!==void 0?oe(i.outputSchema):void 0,s=await re({config:r,modelId:o,options:d,systemPrompt:a,userPrompt:u,output:f});if(s.output===void 0&&s.text&&i.outputSchema!==void 0){let p=await Ue(s.text,i.outputSchema);p!==void 0&&(s.output=p);}return s}getMetadata(e){let n=e,{providerId:r}=se(this.modelId),o={model:this.modelId,provider:r};return n.usage&&(n.usage.inputTokens!==void 0&&(o.inputTokens=n.usage.inputTokens),n.usage.outputTokens!==void 0&&(o.outputTokens=n.usage.outputTokens)),o}};function ae(t,e){return new E(t,e)}var le=["openai","anthropic","openrouter","ollama","gemini"];function Be(t){return le.includes(t)}function j(t){if(!t||typeof t!="object")throw new TypeError("llmPlugin: options must be an object");if(!t.providers||typeof t.providers!="object")throw new TypeError("llmPlugin: options.providers must be an object (record of provider id \u2192 options)");for(let[e,n]of Object.entries(t.providers))if(n!==void 0){if(!n||typeof n!="object")throw new TypeError(`llmPlugin: providers["${e}"] must be an object`);if(!Be(e))throw new TypeError(`llmPlugin: providers["${e}"] is not a supported provider. Supported: ${le.join(", ")}`);switch(e){case "openai":case "anthropic":case "openrouter":case "gemini":if(typeof n.apiKey!="string"||!n.apiKey.trim())throw new TypeError(`llmPlugin: providers["${e}"].apiKey is required`);break;case "ollama":if(n.baseURL!==void 0&&typeof n.baseURL!="string")throw new TypeError(`llmPlugin: providers["${e}"].baseURL must be a string when provided`);if(n.modelId!==void 0&&(typeof n.modelId!="string"||!n.modelId.trim()))throw new TypeError(`llmPlugin: providers["${e}"].modelId must be a non-empty string when provided`);break}}if(t.defaultOptions!==void 0&&(typeof t.defaultOptions!="object"||t.defaultOptions===null))throw new TypeError("llmPlugin: defaultOptions must be an object when provided")}var Ve=["openai","anthropic","openrouter","ollama","gemini"];function Fe(t,e){return {provider:t,...e}}function pe(t={providers:{}}){return j(t),{apply(e){let n=new Map;for(let r of Ve){let o=t.providers[r];o!==void 0&&n.set(r,Fe(r,o));}e.setStore(M,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(S,t.defaultOptions);}}}var b=Symbol.for("routecraft.mcp.plugin.registered"),v=Symbol.for("routecraft.mcp.client.servers"),U=Symbol.for("routecraft.mcp.tool.registry"),T=Symbol.for("routecraft.mcp.stdio.managers"),N=(s=>(s.TOOL="routecraft.mcp.tool",s.SESSION="routecraft.mcp.session",s.AUTH_SUBJECT="routecraft.auth.subject",s.AUTH_SCHEME="routecraft.auth.scheme",s.AUTH_ROLES="routecraft.auth.roles",s.AUTH_SCOPES="routecraft.auth.scopes",s.AUTH_EMAIL="routecraft.auth.email",s.AUTH_NAME="routecraft.auth.name",s.AUTH_ISSUER="routecraft.auth.issuer",s.AUTH_AUDIENCE="routecraft.auth.audience",s))(N||{});var R=Symbol.for("routecraft.mcp.adapter");var q=class{adapterId="routecraft.adapter.mcp";endpoint;options;constructor(e,n){if(this[R]=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 n||"serverId"in n)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 n&&n.args!==void 0&&!("description"in n))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 n)||typeof n.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=n;}async subscribe(e,n,r,o){if(e.getStore(b)!==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,n,r,o)}};function G(t){let e=t?.content;if(!Array.isArray(e)||e.length===0)return t;if(e.length===1){let n=e[0];if(n.type==="text"&&typeof n.text=="string")return n.text;if(typeof n.data=="string")return n.data}return e}var ce=new WeakMap;async function Ke(t){if(typeof t=="function")return t();if(Array.isArray(t)){if(t.length===0)throw new Error("McpClientAuthOptions.token array must not be empty");let e=(ce.get(t)??0)%t.length;return ce.set(t,e+1),t[e]}return t}async function B(t){if(!t)return;let e={},n=t.headers??{},r=Object.entries(n).find(([o])=>o.toLowerCase()==="authorization")?.[1];if(r)e.Authorization=r;else if(t.token!==void 0){let o=await Ke(t.token);if(o.length===0)throw new Error("McpClientAuthOptions.token must be a non-empty string when provided");e.Authorization=`Bearer ${o}`;}for(let[o,i]of Object.entries(n))o.toLowerCase()!=="authorization"&&(e[o]=i);return Object.keys(e).length>0?e:void 0}function ze(t){let e=t.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: "${t.slice(0,50)}${t.length>50?"...":""}"`)}function Ye(t,e){return !t.serverId||!e?void 0:e.getStore(v)?.get(t.serverId)}function Xe(t,e){if(t.url)return ze(t.url),{url:t.url,auth:t.auth};if(t.serverId&&!e)throw new Error(`MCP client: serverId "${t.serverId}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so store "${String(v)}" can be read.`);if(t.serverId&&e){let n=Ye(t,e);if(!n)throw new Error(`MCP client: serverId "${t.serverId}" not found in context store. Register it with context store key "${String(v)}".`);let r=typeof n=="string"?n:n.url,o=t.auth??(typeof n=="object"&&"auth"in n?n.auth:void 0);return {url:r,auth:o}}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var V=t=>typeof t.body=="object"&&t.body!==null?t.body:{input:t.body},H=class{constructor(e){this.options=e;if(this[R]=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 n=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 i=(this.options.args??V)(e);if(this.options.serverId&&n){let s=n.getStore(T)?.get(this.options.serverId);if(s){let m=await s.callTool(r,i);return m&&typeof m=="object"&&(m.metadata={toolName:r,transport:"stdio",serverId:this.options.serverId}),m}let l=n.getStore(v)?.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{url:a,auth:u}=Xe(this.options,n),d=await this.callRemoteTool(a,r,i,u);return d&&typeof d=="object"&&(d.metadata={toolName:r,url:a,transport:"http",...this.options.serverId?{serverId:this.options.serverId}:{}}),d}getMetadata(e){return e&&typeof e=="object"&&"metadata"in e?e.metadata:{toolName:"unknown",transport:"unknown"}}async callRemoteTool(e,n,r,o){let i,a;try{i=await import('@modelcontextprotocol/sdk/client/index.js'),a=await import('@modelcontextprotocol/sdk/client/streamableHttp.js');}catch{throw new Error('MCP client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk')}let u=i.Client,d=a.StreamableHTTPClientTransport,f=new URL(e),s=await B(o),p=s?{requestInit:{headers:s}}:void 0,l=new d(f,p),m={name:"routecraft-mcp-client",version:"1.0.0"},g=new u(m,{capabilities:{}});try{await g.connect.call(g,l);let y=await g.callTool.call(g,{name:n,arguments:r});return G(y)}finally{let c=g,h=c.close??c.disconnect;if(typeof h=="function")try{await Promise.resolve(h.call(g));}catch{}let y=l,w=y.close??y.destroy;if(typeof w=="function")try{await Promise.resolve(w.call(l));}catch{}}}};function ue(t,e){if(typeof t=="object"&&t!==null&&("url"in t||"serverId"in t)){let o=t;if(typeof o.auth?.token=="string"&&o.auth.token.trim().length===0)throw new TypeError("mcp(): auth.token must be a non-empty string when provided");if(Array.isArray(o.auth?.token)){if(o.auth.token.length===0)throw new TypeError("mcp(): auth.token array must not be empty");for(let i=0;i<o.auth.token.length;i++){let a=o.auth.token[i];if(typeof a!="string"||a.trim().length===0)throw new TypeError(`mcp(): auth.token[${i}] must be a non-empty string`)}}return new H(o)}let n=e===void 0||typeof e=="object"&&e!==null&&!("description"in e);if(typeof t=="string"&&t.includes(":")&&n){let o=t.indexOf(":"),i=t.slice(0,o),a=t.slice(o+1),u={serverId:i,tool:a};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(u.args=e.args),new H(u)}let r=t;if(e!==void 0)return new q(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 X={HS256:"sha256",HS384:"sha384",HS512:"sha512"},Q={RS256:"RSA-SHA256",RS384:"RSA-SHA384",RS512:"RSA-SHA512"};function de(t){let e=t.split(".");if(e.length!==3)return null;let[n,r,o]=e;try{let i=JSON.parse(Buffer.from(n,"base64url").toString()),a=JSON.parse(Buffer.from(r,"base64url").toString());return {headerB64:n,payloadB64:r,signatureB64:o,header:i,payload:a}}catch{return null}}function me(t,e){let n=Math.floor(Date.now()/1e3);return !(typeof t.exp=="number"&&n>t.exp+e||typeof t.nbf=="number"&&n<t.nbf-e)}function ge(t){let e=t.sub;if(typeof e!="string"||e.length===0)return null;let n={subject:e,scheme:"bearer",claims:t};typeof t.iss=="string"&&(n.issuer=t.iss),typeof t.exp=="number"&&(n.expiresAt=t.exp),typeof t.email=="string"&&(n.email=t.email),typeof t.name=="string"&&(n.name=t.name);let r=t.aud;Array.isArray(r)?n.audience=r.filter(i=>typeof i=="string"):typeof r=="string"&&(n.audience=[r]);let o=t.scope;return typeof o=="string"&&(n.scopes=o.split(" ").filter(Boolean)),Array.isArray(t.roles)&&(n.roles=t.roles.filter(i=>typeof i=="string")),n}function nt(t){return "secret"in t}function rt(t){let{secret:e,algorithm:n="HS256",clockToleranceSec:r=0}=t;if(!e||typeof e!="string")throw new TypeError("jwt: secret must be a non-empty string");if(!(n in X))throw new TypeError(`jwt: unsupported algorithm "${n}". Supported: ${Object.keys(X).join(", ")}`);let o=X[n];return i=>{let a=de(i);if(!a)return null;let{headerB64:u,payloadB64:d,signatureB64:f,header:s,payload:p}=a;if(s.alg!==n)return null;let l=crypto$1.createHmac(o,e);l.update(`${u}.${d}`);let m=l.digest("base64url"),g=Buffer.from(m),c=Buffer.from(f);return g.length!==c.length||!crypto$1.timingSafeEqual(g,c)||!me(p,r)?null:ge(p)}}function ot(t){let{publicKey:e,algorithm:n="RS256",clockToleranceSec:r=0}=t;if(!e||typeof e!="string")throw new TypeError("jwt: publicKey must be a non-empty PEM string");if(!(n in Q))throw new TypeError(`jwt: unsupported algorithm "${n}". Supported: ${Object.keys(Q).join(", ")}`);let o=Q[n];return i=>{let a=de(i);if(!a)return null;let{headerB64:u,payloadB64:d,signatureB64:f,header:s,payload:p}=a;if(s.alg!==n)return null;let l=crypto$1.createVerify(o);return l.update(`${u}.${d}`),!l.verify(e,f,"base64url")||!me(p,r)?null:ge(p)}}function fe(t){return {validator:nt(t)?rt(t):ot(t)}}var he=new async_hooks.AsyncLocalStorage,Z='MCP server requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',x=class{context;options;server=null;transport=null;httpServer=null;httpSessions=new Map;running=false;toolsListLogged=false;constructor(e,n={}){this.context=e,this.options={name:"routecraft",version:"1.0.0",transport:"stdio",port:3001,host:"localhost",...n};}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 n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Failed to start MCP server";throw this.context.logger.error({err:e},n),e}}async startStdio(){let e,n;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server,n=(await import('@modelcontextprotocol/sdk/server/stdio.js')).StdioServerTransport;}catch{throw new Error(Z)}this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers(),this.transport=new n,await this.server.connect(this.transport);}async startHttp(){let e;try{e=(await import('@modelcontextprotocol/sdk/server/index.js')).Server;}catch{throw new Error(Z)}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");let o=this.options.port,i=this.options.host,a=async()=>{let f=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}});await this.setupRequestHandlersOn(f);let s=new r({sessionIdGenerator:()=>crypto.randomUUID(),onsessioninitialized:c=>{this.httpSessions.set(c,{server:f,transport:s}),this.context.logger.debug({sessionId:c},"MCP session created"),this.context.emit("plugin:mcp:session:created",{sessionId:c});},enableJsonResponse:true});await f.connect(s);let l=s,m=l.onclose;l.onclose=()=>{let c=s.sessionId;c&&(this.httpSessions.delete(c),this.context.logger.debug({sessionId:c},"MCP session closed"),this.context.emit("plugin:mcp:session:closed",{sessionId:c})),m?.();};let g=s.handleRequest;if(typeof g!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");return {transport:s,handleRequest:g.bind(s)}};this.httpServer=http.createServer(async(f,s)=>{let p=f.url?.split("?")[0]??"";if(p!=="/mcp"&&p!=="/mcp/"){s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Not Found",path:p}));return}let l;if(this.options.auth){let c=await this.validateAuth(f);if(!c){s.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="mcp"'}),s.end(JSON.stringify({error:"Unauthorized"}));return}l=c;}let m=f.headers["mcp-session-id"],g=c=>he.run(l,c);try{if(m&&this.httpSessions.has(m)){let c=this.httpSessions.get(m),h=c.transport.handleRequest;await g(()=>h.call(c.transport,f,s));}else if(!m||f.method==="POST"){let{handleRequest:c}=await a();await g(()=>c(f,s));}else s.writeHead(404,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Session not found"}));}catch(c){let h=routecraft.isRoutecraftError(c)?c.meta.message:c instanceof Error?c.message:"MCP HTTP request error";this.context.logger.error({err:c},h),s.headersSent||(s.writeHead(500,{"Content-Type":"application/json"}),s.end(JSON.stringify({error:"Internal Server Error"})));}}),await new Promise((f,s)=>{this.httpServer.listen(o,i,()=>f()),this.httpServer.on("error",p=>{let l=routecraft.isRoutecraftError(p)?p.meta.message:p instanceof Error?p.message:"MCP HTTP server listen failed";this.context.logger.error({err:p},l),s(p);});});let u=this.getHttpPort()??o,d={host:i,port:u,path:"/mcp"};this.context.logger.info(d,"MCP HTTP server listening"),this.context.emit("plugin:mcp:server:listening",d);}getHttpPort(){let e=this.httpServer?.address();if(e&&typeof e=="object"&&"port"in e)return e.port}async validateAuth(e){let n=this.options.auth;if(!n)return null;let r=e.headers.authorization;if(!r||Array.isArray(r)){let d={reason:"missing_header",scheme:"bearer",source:"mcp"};return this.context.logger.warn(d,"Auth rejected: missing or malformed Authorization header"),this.context.emit("auth:rejected",d),null}let o=/^bearer\s+(.+)$/i.exec(r);if(!o){let d={reason:"unsupported_scheme",scheme:"bearer",source:"mcp"};return this.context.logger.warn(d,"Auth rejected: unsupported authorization scheme"),this.context.emit("auth:rejected",d),null}let i=o[1],a=await n.validator(i);if(!a){let d={reason:"invalid_token",scheme:"bearer",source:"mcp"};return this.context.logger.warn(d,"Auth rejected: token validation failed"),this.context.emit("auth:rejected",d),null}let u={subject:a.subject,scheme:a.scheme,source:"mcp"};return this.context.logger.info(u,"Auth succeeded"),this.context.emit("auth:success",u),a}async setupRequestHandlers(){await this.setupRequestHandlersOn(this.server);}async setupRequestHandlersOn(e){let n;try{n=await import('@modelcontextprotocol/sdk/types.js');}catch{throw new Error(Z)}let r=n,o=r.ListToolsRequestSchema,i=r.CallToolRequestSchema;if(!o||!i)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let a=e;a.setRequestHandler(o,async()=>{let u=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:u}}),a.setRequestHandler(i,async u=>{let f=u.params;return await this.handleToolCall(f.name||"",f.arguments||{})});}async stop(){if(this.running)try{if(this.httpServer){for(let[,e]of this.httpSessions){let n=e.transport;typeof n.close=="function"&&await n.close();}this.httpSessions.clear(),await new Promise(e=>{this.httpServer.close(()=>e());}),this.httpServer=null;}if(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 n=routecraft.isRoutecraftError(e)?e.meta.message:e instanceof Error?e.message:"Error stopping MCP server";this.context.logger.error({err:e},n);}}logExposedToolsOnce(){if(this.toolsListLogged)return;let e=this.getAvailableTools();if(e.length===0)return;let n=e.map(o=>o.name??"?"),r={tools:n,count:n.length};this.context.logger.info(r,"Exposing MCP tools"),this.context.emit("plugin:mcp:server:tools:exposed",r),this.toolsListLogged=true;}getAvailableTools(){let e=this.context.getStore(routecraft.ADAPTER_DIRECT_REGISTRY);if(!e)return [];let n=Array.from(e.values()).filter(o=>o.description!==void 0),r=this.options.tools;if(r)if(Array.isArray(r)){let o=new Set(r);n=n.filter(i=>o.has(i.endpoint));}else typeof r=="function"&&(n=n.filter(r));return n.map(o=>this.metadataToMcpTool(o))}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 n=e["~standard"];if(n?.jsonSchema?.input)try{let r=n.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,n){try{let r=this.context.getStore(routecraft.ADAPTER_DIRECT_STORE);if(!r){let l=new Error("No direct channels available");return this.context.emit("plugin:mcp:tool:failed",{tool:e,error:l.message}),{isError:!0,content:[{type:"text",text:"Error: No direct channels available"}]}}let o=r.get(e);if(!o){let l=new Error(`Tool not found: ${e}`);return this.context.emit("plugin:mcp:tool:failed",{tool:e,error:l.message}),{isError:!0,content:[{type:"text",text:`Error: Tool not found: ${e}`}]}}let i=typeof n=="string"?(()=>{try{return JSON.parse(n)||{}}catch{return {input:n}}})():n&&typeof n=="object"?n:{};this.context.logger.debug({bodyType:typeof i,body:i},"MCP tool call exchange body");let a=he.getStore(),u={"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`};a&&(u["routecraft.auth.subject"]=a.subject,u["routecraft.auth.scheme"]=a.scheme,a.roles&&(u["routecraft.auth.roles"]=a.roles),a.scopes&&(u["routecraft.auth.scopes"]=a.scopes),a.email&&(u["routecraft.auth.email"]=a.email),a.name&&(u["routecraft.auth.name"]=a.name),a.issuer&&(u["routecraft.auth.issuer"]=a.issuer),a.audience&&(u["routecraft.auth.audience"]=a.audience));let d=new routecraft.DefaultExchange(this.context,{body:i,headers:u});this.context.emit("plugin:mcp:tool:called",{tool:e,args:n});let s=await o.send(e,d),p=typeof s.body=="string"?s.body:JSON.stringify(s.body);return this.context.emit("plugin:mcp:tool:completed",{tool:e}),{content:[{type:"text",text:p}]}}catch(r){let o=routecraft.isRoutecraftError(r)?r.meta.message:r instanceof Error?r.message:String(r);this.context.logger.error({tool:e,err:r},o),this.context.emit("plugin:mcp:tool:failed",{tool:e,error:o});let i=o;if(routecraft.isRoutecraftError(r)){let a=r.cause;a?.message&&(i=`${o}: ${a.message}`);}return {content:[{type:"text",text:`Error: ${i}`}],isError:true}}}};function ye(t){if(t.transport==="http"){if(t.port!==void 0){if(typeof t.port!="number")throw new TypeError("mcpPlugin: when transport is 'http', port must be a number");if(t.port<0||t.port>65535)throw new RangeError("mcpPlugin: port must be between 0 and 65535 when transport is 'http'")}if(t.host!==void 0&&typeof t.host!="string")throw new TypeError("mcpPlugin: when provided, host must be a string")}if(t.auth!==void 0&&typeof t.auth.validator!="function")throw new TypeError("mcpPlugin: auth.validator must be a function that returns an AuthPrincipal or null");if(t.clients){for(let[e,n]of Object.entries(t.clients))if(typeof n=="object"&&n!==null&&"transport"in n&&n.transport==="stdio"&&(!n.command||typeof n.command!="string"))throw new TypeError(`mcpPlugin: stdio client "${e}" must have a non-empty command string`)}if(t.maxRestarts!==void 0&&(typeof t.maxRestarts!="number"||!Number.isInteger(t.maxRestarts)||t.maxRestarts<0))throw new TypeError("mcpPlugin: maxRestarts must be a non-negative integer");if(t.restartDelayMs!==void 0&&(typeof t.restartDelayMs!="number"||t.restartDelayMs<=0))throw new TypeError("mcpPlugin: restartDelayMs must be a positive number");if(t.restartBackoffMultiplier!==void 0&&(typeof t.restartBackoffMultiplier!="number"||t.restartBackoffMultiplier<1))throw new TypeError("mcpPlugin: restartBackoffMultiplier must be >= 1");if(t.toolRefreshIntervalMs!==void 0&&(typeof t.toolRefreshIntervalMs!="number"||!Number.isInteger(t.toolRefreshIntervalMs)||t.toolRefreshIntervalMs<0))throw new TypeError("mcpPlugin: toolRefreshIntervalMs must be a non-negative integer")}async function we(t,e){let n=e["~standard"];if(!n?.validate)throw new Error("mcpPlugin: schema must be a StandardSchemaV1 with ~standard.validate");let r=n.validate(t);if(r instanceof Promise&&(r=await r),r.issues)throw new Error(`mcpPlugin options validation failed: ${routecraft.formatSchemaIssues(r.issues)}`);if(r.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return r.value}var ut='MCP stdio client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',F=class{constructor(e,n,r,o){this.options=e;this.logger=n;this.onEvent=r;this.onToolsUpdated=o;}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,n;try{e=await import('@modelcontextprotocol/sdk/client/index.js'),n=await import('@modelcontextprotocol/sdk/client/stdio.js');}catch{throw new Error(ut)}let{serverId:r,command:o,args:i,env:a,cwd:u}=this.options;this.transport=new n.StdioClientTransport({command:o,args:i??[],...a?{env:a}:{},...u?{cwd:u}:{},stderr:"pipe"});let d=this.transport;d.onerror=f=>{this.logger.error({err:f,serverId:r},"Stdio client transport error"),this.onEvent(`plugin:mcp:client:${r}:error`,{serverId:r,error:f});},d.onclose=()=>{this.stopping||(this.running=false,this.handleDisconnect());},d.stderr?.on&&d.stderr.on("data",f=>{this.logger.debug({serverId:r,stderr:String(f).trimEnd()},"Stdio client stderr output");}),this.client=new e.Client({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{},listChanged:{tools:{onChanged:(f,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 n=this.transport;if(typeof n.close=="function")try{await Promise.resolve(n.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,n){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:n});return G(r)}getTools(){return [...this.tools]}isRunning(){return this.running}async refreshTools(){if(!this.client)return;let{serverId:e}=this.options;try{let n=await this.client.listTools();this.tools=n.tools??[],this.onToolsUpdated(e,this.tools),this.onEvent(`plugin:mcp:client:${e}:tools:listed`,{serverId:e,toolCount:this.tools.length});}catch(n){this.logger.warn({err:n,serverId:e},"Failed to list tools for stdio client");}}handleDisconnect(){let{serverId:e,maxRestarts:n,restartDelayMs:r,restartBackoffMultiplier:o}=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>=n){this.logger.error({serverId:e,restartCount:this.restartCount,maxRestarts:n},"Stdio client exceeded max restarts, giving up"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:new Error(`Max restarts (${n}) exceeded for stdio client "${e}"`)});return}let i=r*Math.pow(o,this.restartCount);this.logger.info({serverId:e,restartCount:this.restartCount,delayMs:i},"Scheduling stdio client restart"),this.restartTimer=setTimeout(()=>{this.restartTimer=null,this.restart();},i);}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 n=this.options.restartDelayMs*2;this.stabilityTimer=setTimeout(()=>{this.stabilityTimer=null,this.running&&(this.restartCount=0);},n);}catch(n){this.logger.error({err:n,serverId:e,restartCount:this.restartCount},"Stdio client restart failed"),this.onEvent(`plugin:mcp:client:${e}:error`,{serverId:e,error:n}),this.handleDisconnect();}}};var k=class{tools=new Map;setToolsForSource(e,n,r){let o=new Map;for(let i of r){let a={name:i.name,inputSchema:i.inputSchema,source:e,transport:n};i.description!==void 0&&(a.description=i.description),o.set(i.name,a);}this.tools.set(e,o);}removeSource(e){this.tools.delete(e);}getTools(){let e=[];for(let n of this.tools.values())for(let r of n.values())e.push(r);return e}getToolsByServer(e){let n=this.tools.get(e);return n?Array.from(n.values()):[]}getTool(e){for(let n of this.tools.values()){let r=n.get(e);if(r)return r}}getToolBySource(e,n){return this.tools.get(e)?.get(n)}};function ve(t){return "transport"in t&&t.transport==="stdio"}function Pe(t={}){ye(t);let e=null,n=new Map,r=new Map,o=[],i=null;return {async apply(s){if(s.setStore(b,true),i=new k,s.setStore(U,i),s.setStore(T,n),t.clients&&Object.keys(t.clients).length>0){let p=Object.entries(t.clients),l=new Map;for(let[m,g]of p)l.set(m,g);s.setStore(v,l);for(let[m,g]of p){if(ve(g))await a(s,m,g,i);else {let h=g;await d(s,m,h.url,i,h.auth),f(s,m,h.url,i,h.auth);}let c=ve(g)?"stdio":"http";s.emit(`plugin:mcp:client:${m}:registered`,{serverId:m,transport:c});}}e=new x(s,t),await e.start();},async teardown(s){for(let p of o)clearInterval(p);o.length=0;for(let[p,l]of r)try{await l.close();}catch(m){s.logger.error({err:m,serverId:p,operation:"close"},"Failed to close HTTP client");}r.clear();for(let[p,l]of n)try{await l.stop();}catch(m){s.logger.error({err:m,serverId:p,operation:"stop"},"Failed to stop stdio client");}if(n.clear(),e){try{await e.stop();}catch(p){s.logger.error({err:p,operation:"stop"},"Failed to stop MCP server plugin");}e=null;}i=null;}};async function a(s,p,l,m){let g={serverId:p,command:l.command,args:l.args??[],maxRestarts:t.maxRestarts??5,restartDelayMs:t.restartDelayMs??1e3,restartBackoffMultiplier:t.restartBackoffMultiplier??2};l.env!==void 0&&(g.env=l.env),l.cwd!==void 0&&(g.cwd=l.cwd);let c=new F(g,s.logger,(h,y)=>{s.emit(h,y);},(h,y)=>{m.setToolsForSource(h,"stdio",y.map(w=>{let P={name:w.name,inputSchema:w.inputSchema};return w.description!==void 0&&(P.description=w.description),P}));});n.set(p,c);try{await c.start();}catch(h){s.logger.error({err:h,serverId:p,operation:"start"},"Failed to start stdio client"),s.emit(`plugin:mcp:client:${p}:error`,{serverId:p,error:h});}}async function u(s,p,l){let m=r.get(s);if(m)return m;let{Client:g}=await import('@modelcontextprotocol/sdk/client/index.js'),{StreamableHTTPClientTransport:c}=await import('@modelcontextprotocol/sdk/client/streamableHttp.js'),h=await B(l),y=h?{requestInit:{headers:h}}:void 0,w=new c(new URL(p),y),P=new g({name:"routecraft-mcp-client",version:"1.0.0"},{capabilities:{}});await P.connect(w);let ee=P;return r.set(s,ee),ee}async function d(s,p,l,m,g){try{let y=(await(await u(p,l,g)).listTools()).tools??[];m.setToolsForSource(p,"http",y.map(w=>{let P={name:w.name,inputSchema:w.inputSchema};return w.description!==void 0&&(P.description=w.description),P})),s.emit(`plugin:mcp:client:${p}:tools:listed`,{serverId:p,toolCount:y.length});}catch(c){r.delete(p),s.logger.warn({err:c,serverId:p,url:l,operation:"listTools"},"Failed to list tools from HTTP client");}}function f(s,p,l,m,g){let c=t.toolRefreshIntervalMs??6e4;if(c<=0)return;let h=setInterval(()=>{d(s,p,l,m,g);},c);o.push(h);}}var J=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 dt(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`Agent adapter: modelId must be "providerId:modelName" (e.g. ollama:llama3). Got: "${t}"`)}var O=class{adapterId="routecraft.adapter.agent";runner;constructor(e){dt(e.modelId),this.runner=new J(e);}async send(e){return this.runner.run(e)}};function Me(t){return new O(t)}var K=new Map;async function W(){let t=[...K.entries()];K.clear(),t.length!==0&&await Promise.all(t.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function mt(t){return t.includes("/")?t:`Xenova/${t}`}var gt='The Hugging Face embedding provider requires "@huggingface/transformers". Install it with: pnpm add @huggingface/transformers';function ft(t,e){if(!(t instanceof Error))return false;let n=t.message??"";return (n.includes("ERR_MODULE_NOT_FOUND")||n.includes("Cannot find module")||n.includes("Cannot find package"))&&n.includes(e)}async function ht(t){let e=mt(t),n=K.get(e);if(n)return n.run;let r;try{r=(await import('@huggingface/transformers')).pipeline;}catch(u){throw ft(u,"@huggingface/transformers")?new Error(gt):u}let o=await r("feature-extraction",e,{dtype:"fp32"}),i=(u,d)=>o(u,{pooling:d?.pooling??"mean",normalize:d?.normalize??true}),a={run:i,dispose:typeof o.dispose=="function"?()=>Promise.resolve(o.dispose()):void 0};return K.set(e,a),i}function Re(t){if(t===null)return "null";if(typeof t!="object")return String(t);if(Array.isArray(t))return `array(${t.length})`;try{let e=JSON.stringify(t);return e.length>100?e.slice(0,100)+"...":e}catch{return "object"}}function yt(t){if(Array.isArray(t))return t;if(t instanceof Float32Array)return Array.from(t);if(t&&typeof t=="object"&&"data"in t){let e=t.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}: ${Re(e)}`)}throw new Error(`Embedding output must be an Array, Float32Array, or object with .data; got ${typeof t}: ${Re(t)}`)}async function Se(t){let{config:e,modelName:n,text:r}=t;if(e.provider==="mock"){let i=[];for(let a=0;a<8;a++)i.push((r.length+a)%100/100);return i}if(e.provider==="huggingface"){let i=await(await ht(n))(r,{pooling:"mean",normalize:true}),a=i&&typeof i=="object"&&"data"in i?i.data:i;return yt(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 z=Symbol.for("routecraft.adapter.embedding.providers"),Y=Symbol.for("routecraft.adapter.embedding.options");function vt(t){let e=t.indexOf(":");if(e<1||e===t.length-1)throw new Error(`Embedding adapter: model id must be "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2). Got: "${t}"`);return {providerId:t.slice(0,e),modelName:t.slice(e+1)}}function Pt(t,e){if(!e)throw new Error(`Embedding adapter: model id "${t}" requires a context to resolve. Ensure the exchange has context (e.g. from a route) so embedding providers can be read.`);let n=e.getStore(z);if(!n)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:r,modelName:o}=vt(t),i=n.get(r);if(!i)throw new Error(`Embedding provider "${r}" not found. Register it with embeddingPlugin({ providers: { "${r}": {} } }).`);return {config:i,modelName:o}}function Mt(t){return e=>{let n=t(e);return Array.isArray(n)?n.filter(Boolean).join(" | "):n}}var A=class{constructor(e,n={}){this.modelId=e;this.options=n;}adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore(Y),...this.options}}async send(e){let n=routecraft.getExchangeContext(e),{config:r,modelName:o}=Pt(this.modelId,n),i=this.mergedOptions(n);if(!i.using)throw new Error("Embedding adapter: options.using(exchange) is required to build the string to embed.");let u=Mt(i.using)(e);return {embedding:await Se({config:r,modelName:o,text:u})}}};function Ee(t,e){return new A(t,e)}function Rt(t,e){return {...e,provider:t}}function be(t={providers:{}}){return {apply(e){let n=new Map;for(let[r,o]of Object.entries(t.providers))o!==void 0&&n.set(r,Rt(r,o));e.setStore(z,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(Y,t.defaultOptions);},async teardown(){await W();}}}
|
|
2
|
+
exports.ADAPTER_LLM_OPTIONS=S;exports.ADAPTER_LLM_PROVIDERS=M;exports.ADAPTER_MCP_CLIENT_SERVERS=v;exports.AgentDestinationAdapter=O;exports.BRAND=te;exports.BRAND_MCP_ADAPTER=R;exports.EmbeddingDestinationAdapter=A;exports.LlmDestinationAdapter=E;exports.MCP_PLUGIN_REGISTERED=b;exports.MCP_STDIO_MANAGERS=T;exports.MCP_TOOL_REGISTRY=U;exports.McpHeadersKeys=N;exports.McpServer=x;exports.McpToolRegistry=k;exports.agent=Me;exports.defaultArgs=V;exports.disposeEmbeddingPipelineCache=W;exports.embedding=Ee;exports.embeddingPlugin=be;exports.isMcpAdapter=Ce;exports.jwt=fe;exports.llm=ae;exports.llmPlugin=pe;exports.mcp=ue;exports.mcpPlugin=Pe;exports.validateLlmPluginOptions=j;exports.validateWithSchema=we;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|