@routecraft/ai 0.4.0-canary.9 → 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.d.ts CHANGED
@@ -369,20 +369,44 @@ declare const MCP_STDIO_MANAGERS: unique symbol;
369
369
  declare module "@routecraft/routecraft" {
370
370
  interface StoreRegistry {
371
371
  [MCP_PLUGIN_REGISTERED]: boolean;
372
- [ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig$1 | McpClientStdioConfig | string>;
372
+ [ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig | McpClientStdioConfig | string>;
373
373
  [MCP_TOOL_REGISTRY]: McpToolRegistry;
374
374
  [MCP_STDIO_MANAGERS]: Map<string, {
375
375
  callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
376
376
  }>;
377
377
  }
378
+ interface RoutecraftHeaders {
379
+ /** The MCP tool name that triggered this exchange. */
380
+ "routecraft.mcp.tool"?: string;
381
+ /** The MCP session identifier. */
382
+ "routecraft.mcp.session"?: string;
383
+ /** Authenticated subject (from AuthPrincipal). */
384
+ "routecraft.auth.subject"?: string;
385
+ /** Authentication scheme used. */
386
+ "routecraft.auth.scheme"?: string;
387
+ /** Roles assigned to the authenticated principal. */
388
+ "routecraft.auth.roles"?: string[];
389
+ /** Scopes granted to the authenticated principal. */
390
+ "routecraft.auth.scopes"?: string[];
391
+ /** Email of the authenticated principal. */
392
+ "routecraft.auth.email"?: string;
393
+ /** Display name of the authenticated principal. */
394
+ "routecraft.auth.name"?: string;
395
+ /** Token issuer (JWT `iss`). */
396
+ "routecraft.auth.issuer"?: string;
397
+ /** Intended audience (JWT `aud`). */
398
+ "routecraft.auth.audience"?: string[];
399
+ }
378
400
  }
379
401
  /**
380
402
  * HTTP client config for a remote MCP server (Streamable HTTP).
381
403
  * Used in mcpPlugin({ clients: { name: config } }).
382
404
  */
383
- interface McpClientHttpConfig$1 {
405
+ interface McpClientHttpConfig {
384
406
  transport?: "streamable-http";
385
407
  url: string;
408
+ /** Auth credentials sent on every request to this server. */
409
+ auth?: McpClientAuthOptions;
386
410
  }
387
411
  /**
388
412
  * Stdio client config for a local MCP server subprocess.
@@ -401,7 +425,155 @@ interface McpClientStdioConfig {
401
425
  cwd?: string;
402
426
  }
403
427
  /** Union of client configs accepted by mcpPlugin({ clients }). */
404
- type McpClientServerConfig = McpClientHttpConfig$1 | McpClientStdioConfig;
428
+ type McpClientServerConfig = McpClientHttpConfig | McpClientStdioConfig;
429
+ /**
430
+ * Header keys set on exchanges created by the MCP server.
431
+ * Use these with `exchange.headers[McpHeadersKeys.AUTH_SUBJECT]` for type-safe access.
432
+ *
433
+ * @example
434
+ * ```ts
435
+ * import { McpHeadersKeys } from '@routecraft/ai'
436
+ *
437
+ * .process((ex) => {
438
+ * const user = ex.headers[McpHeadersKeys.AUTH_SUBJECT]
439
+ * const tool = ex.headers[McpHeadersKeys.TOOL]
440
+ * })
441
+ * ```
442
+ */
443
+ declare enum McpHeadersKeys {
444
+ /** The MCP tool name that triggered this exchange. */
445
+ TOOL = "routecraft.mcp.tool",
446
+ /** The MCP session identifier. */
447
+ SESSION = "routecraft.mcp.session",
448
+ /** Authenticated subject (from AuthPrincipal). */
449
+ AUTH_SUBJECT = "routecraft.auth.subject",
450
+ /** Authentication scheme used. */
451
+ AUTH_SCHEME = "routecraft.auth.scheme",
452
+ /** Roles assigned to the authenticated principal. */
453
+ AUTH_ROLES = "routecraft.auth.roles",
454
+ /** Scopes granted to the authenticated principal. */
455
+ AUTH_SCOPES = "routecraft.auth.scopes",
456
+ /** Email of the authenticated principal. */
457
+ AUTH_EMAIL = "routecraft.auth.email",
458
+ /** Display name of the authenticated principal. */
459
+ AUTH_NAME = "routecraft.auth.name",
460
+ /** Token issuer (JWT `iss`). */
461
+ AUTH_ISSUER = "routecraft.auth.issuer",
462
+ /** Intended audience (JWT `aud`). */
463
+ AUTH_AUDIENCE = "routecraft.auth.audience"
464
+ }
465
+ /**
466
+ * Authenticated user principal resolved from an incoming request.
467
+ * Returned by the auth validator to populate exchange headers for logging,
468
+ * filtering, and access control inside routes.
469
+ *
470
+ * `subject` and `scheme` are always required; everything else is
471
+ * scheme-dependent and may be absent.
472
+ *
473
+ * @experimental
474
+ */
475
+ interface AuthPrincipal {
476
+ /** Unique user/client identifier (JWT `sub`, username, API key ID, etc.). */
477
+ subject: string;
478
+ /** Authentication scheme that produced this principal. */
479
+ scheme: "bearer" | "basic" | "api-key" | (string & {});
480
+ /** Role names granted to this principal. */
481
+ roles?: string[];
482
+ /** OAuth 2.0 / JWT scopes. */
483
+ scopes?: string[];
484
+ /** Email address, if known. */
485
+ email?: string;
486
+ /** Display name, if known. */
487
+ name?: string;
488
+ /** Token issuer (JWT `iss`). */
489
+ issuer?: string;
490
+ /** Intended audiences (JWT `aud`). */
491
+ audience?: string[];
492
+ /** Expiry as Unix epoch seconds (JWT `exp`, session expiry). */
493
+ expiresAt?: number;
494
+ /** Arbitrary extra claims. For JWTs this holds the full decoded payload. */
495
+ claims?: Record<string, unknown>;
496
+ }
497
+ /**
498
+ * Authentication options for the MCP HTTP server.
499
+ * Only applies when `transport` is `"http"`. Ignored for stdio.
500
+ *
501
+ * The validator receives the raw bearer token from the `Authorization` header
502
+ * on every request and must return an {@link AuthPrincipal} on success or
503
+ * `null` / `false` to reject with 401. Use the built-in `jwt()` helper for
504
+ * HMAC-signed JWTs or supply a custom function for other schemes.
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * // Built-in JWT helper (HMAC / HS256)
509
+ * import { jwt } from "@routecraft/ai";
510
+ * auth: jwt({ secret: process.env.JWT_SECRET! })
511
+ *
512
+ * // Custom validator
513
+ * auth: {
514
+ * validator: async (token) => {
515
+ * const user = await lookupApiKey(token);
516
+ * if (!user) return null;
517
+ * return { subject: user.id, scheme: "api-key", roles: user.roles };
518
+ * }
519
+ * }
520
+ * ```
521
+ *
522
+ * @experimental
523
+ */
524
+ interface McpHttpAuthOptions {
525
+ /**
526
+ * Validator function called with the raw bearer token on every request.
527
+ *
528
+ * Return an {@link AuthPrincipal} to allow access; return `null` or `false`
529
+ * to reject with 401. May be async.
530
+ *
531
+ * If the function throws, the server responds with 500.
532
+ * Validators should catch expected failures (e.g. JWT expiry) and return `null`.
533
+ */
534
+ validator: McpAuthValidator;
535
+ }
536
+ /**
537
+ * A function that validates a bearer token and resolves the authenticated principal.
538
+ * Called on every incoming HTTP request.
539
+ * May be synchronous or asynchronous.
540
+ *
541
+ * Return an {@link AuthPrincipal} to allow access, or `null` / `false` to reject with 401.
542
+ * If the function throws, the server responds with 500.
543
+ *
544
+ * @experimental
545
+ */
546
+ type McpAuthValidator = (token: string) => AuthPrincipal | null | false | Promise<AuthPrincipal | null | false>;
547
+ /**
548
+ * A function that provides a bearer token for outbound requests.
549
+ * Called on every request; may be synchronous or asynchronous.
550
+ * Useful for dynamic tokens (JWT refresh, rotating API keys, etc.).
551
+ *
552
+ * @experimental
553
+ */
554
+ type McpClientTokenProvider = () => string | Promise<string>;
555
+ /**
556
+ * Auth config for an outbound MCP HTTP client connection.
557
+ * Passed as request headers on every connection to the remote server.
558
+ *
559
+ * @experimental
560
+ */
561
+ interface McpClientAuthOptions {
562
+ /**
563
+ * Bearer token(s) or provider for the `Authorization` header.
564
+ * Builds `Authorization: Bearer <token>`.
565
+ *
566
+ * - `string` -- single static token.
567
+ * - `string[]` -- array of tokens; one is selected per request (round-robin).
568
+ * - `() => string | Promise<string>` -- called per request for dynamic tokens.
569
+ */
570
+ token?: string | string[] | McpClientTokenProvider;
571
+ /**
572
+ * Additional headers to include on every request to the remote server.
573
+ * If `Authorization` is set here, it overrides `token`.
574
+ */
575
+ headers?: Record<string, string>;
576
+ }
405
577
  /**
406
578
  * Options for the MCP plugin (mcpPlugin).
407
579
  * One plugin per adapter: this is the single options type for the MCP plugin.
@@ -417,6 +589,17 @@ interface McpPluginOptions {
417
589
  port?: number;
418
590
  /** Host to bind to. Default: "localhost" (only used with transport: "http") */
419
591
  host?: string;
592
+ /**
593
+ * Authentication for the HTTP transport. When set, every request to `/mcp` must
594
+ * include a valid `Authorization: Bearer <token>` header. Ignored for stdio.
595
+ *
596
+ * @example
597
+ * ```ts
598
+ * import { jwt } from "@routecraft/ai";
599
+ * auth: jwt({ secret: process.env.JWT_SECRET! })
600
+ * ```
601
+ */
602
+ auth?: McpHttpAuthOptions;
420
603
  /**
421
604
  * Filter which tools to expose. Default: all mcp() routes.
422
605
  * Can be an array of endpoint names or a filter function.
@@ -428,7 +611,7 @@ interface McpPluginOptions {
428
611
  * Stdio clients are managed as subprocesses with auto-restart.
429
612
  * HTTP clients are used for ephemeral tool calls.
430
613
  */
431
- clients?: Record<string, McpClientHttpConfig$1 | McpClientStdioConfig>;
614
+ clients?: Record<string, McpClientHttpConfig | McpClientStdioConfig>;
432
615
  /**
433
616
  * Max auto-restart attempts for stdio clients before giving up.
434
617
  * Applies to all stdio clients. Default: 5.
@@ -485,6 +668,13 @@ interface McpClientOptions {
485
668
  * Default: body as object -> use as args; otherwise { input: body }.
486
669
  */
487
670
  args?: McpArgsExtractor$1;
671
+ /**
672
+ * Auth credentials for the outbound HTTP connection.
673
+ * When using `serverId`, auth flows automatically from `mcpPlugin({ clients })`
674
+ * so this field is rarely needed. Use it to override registered auth or to
675
+ * supply credentials when using inline `url`.
676
+ */
677
+ auth?: McpClientAuthOptions;
488
678
  }
489
679
  /**
490
680
  * Represents a tool exposed via MCP
@@ -534,14 +724,6 @@ type McpMessage<S extends StandardSchemaV1 | undefined> = S extends StandardSche
534
724
  * Extracts MCP tool arguments from an exchange. Default implementation uses exchange.body.
535
725
  */
536
726
  type McpArgsExtractor = (exchange: Exchange<unknown>) => Record<string, unknown>;
537
- /**
538
- * HTTP client config for a remote MCP server (Streamable HTTP).
539
- * Used in mcpPlugin({ clients: { name: config } }).
540
- */
541
- interface McpClientHttpConfig {
542
- transport?: "streamable-http";
543
- url: string;
544
- }
545
727
 
546
728
  /**
547
729
  * Brand symbol for MCP adapter type checking.
@@ -598,6 +780,109 @@ declare function mcp(shorthand: RegisteredMcpShorthand, options?: {
598
780
  args?: McpArgsExtractor$1;
599
781
  }): Destination<unknown, unknown>;
600
782
 
783
+ /**
784
+ * Supported HMAC algorithms for JWT signature verification.
785
+ * Maps JWT `alg` header values to Node.js `crypto.createHmac` digest names.
786
+ */
787
+ declare const HMAC_ALGORITHMS: {
788
+ readonly HS256: "sha256";
789
+ readonly HS384: "sha384";
790
+ readonly HS512: "sha512";
791
+ };
792
+ /**
793
+ * Supported RSA algorithms for JWT signature verification.
794
+ * Maps JWT `alg` header values to Node.js `crypto.createVerify` algorithm names.
795
+ */
796
+ declare const RSA_ALGORITHMS: {
797
+ readonly RS256: "RSA-SHA256";
798
+ readonly RS384: "RSA-SHA384";
799
+ readonly RS512: "RSA-SHA512";
800
+ };
801
+ type HmacAlgorithm = keyof typeof HMAC_ALGORITHMS;
802
+ type RsaAlgorithm = keyof typeof RSA_ALGORITHMS;
803
+ /**
804
+ * HMAC (symmetric) JWT options.
805
+ * Uses a shared secret to sign and verify tokens.
806
+ *
807
+ * @experimental
808
+ */
809
+ interface JwtHmacOptions {
810
+ /** Shared secret used to verify HMAC signatures. */
811
+ secret: string;
812
+ /**
813
+ * HMAC algorithm to accept. Default: `"HS256"`.
814
+ * The helper rejects tokens whose `alg` header does not match.
815
+ */
816
+ algorithm?: HmacAlgorithm;
817
+ /**
818
+ * Clock skew tolerance in seconds for `exp` and `nbf` checks.
819
+ * Default: `0` (no tolerance).
820
+ */
821
+ clockToleranceSec?: number;
822
+ }
823
+ /**
824
+ * RSA (asymmetric) JWT options.
825
+ * Uses a public key to verify signatures. Only the public key is needed
826
+ * since the server only verifies, never signs.
827
+ *
828
+ * @experimental
829
+ */
830
+ interface JwtRsaOptions {
831
+ /** PEM-encoded public key or certificate used to verify RSA signatures. */
832
+ publicKey: string;
833
+ /**
834
+ * RSA algorithm to accept. Default: `"RS256"`.
835
+ * The helper rejects tokens whose `alg` header does not match.
836
+ */
837
+ algorithm?: RsaAlgorithm;
838
+ /**
839
+ * Clock skew tolerance in seconds for `exp` and `nbf` checks.
840
+ * Default: `0` (no tolerance).
841
+ */
842
+ clockToleranceSec?: number;
843
+ }
844
+ /**
845
+ * Options for the built-in JWT auth helper.
846
+ * Supports HMAC (symmetric) and RSA (asymmetric) signing.
847
+ *
848
+ * Discriminated by key: pass `secret` for HMAC, `publicKey` for RSA.
849
+ *
850
+ * @experimental
851
+ */
852
+ type JwtAuthOptions = JwtHmacOptions | JwtRsaOptions;
853
+ /**
854
+ * Built-in JWT authentication helper for MCP HTTP servers.
855
+ * Verifies HMAC or RSA signed JWTs using only `node:crypto` (no external
856
+ * dependencies).
857
+ *
858
+ * Returns an {@link McpHttpAuthOptions} that can be passed directly to
859
+ * `mcpPlugin({ auth: jwt({ ... }) })`.
860
+ *
861
+ * On success the validator returns an {@link AuthPrincipal} populated from
862
+ * standard JWT claims (`sub`, `iss`, `aud`, `exp`, `scope`, etc.) with the
863
+ * full decoded payload available in `claims`.
864
+ *
865
+ * @example
866
+ * ```ts
867
+ * import { mcpPlugin, jwt } from "@routecraft/ai";
868
+ *
869
+ * // HMAC (symmetric) - shared secret
870
+ * mcpPlugin({
871
+ * transport: "http",
872
+ * auth: jwt({ secret: process.env.JWT_SECRET! }),
873
+ * });
874
+ *
875
+ * // RSA (asymmetric) - public key only
876
+ * mcpPlugin({
877
+ * transport: "http",
878
+ * auth: jwt({ algorithm: "RS256", publicKey: process.env.JWT_PUBLIC_KEY! }),
879
+ * });
880
+ * ```
881
+ *
882
+ * @experimental
883
+ */
884
+ declare function jwt(options: JwtAuthOptions): McpHttpAuthOptions;
885
+
601
886
  /**
602
887
  * MCP plugin: one plugin per adapter. Starts the MCP server during plugin apply (before routes start) so startup failures fail context build. Exposes mcp() routes to external MCP clients.
603
888
  * Optional clients: register named remote MCP servers so routes can use .to(mcp("name:tool")) without passing url.
@@ -625,6 +910,8 @@ declare class McpServer {
625
910
  private transport;
626
911
  /** Node HTTP server when transport is http; used to listen on port and close on stop. */
627
912
  private httpServer;
913
+ /** Active HTTP sessions keyed by session ID (each session has its own server+transport pair). */
914
+ private httpSessions;
628
915
  private running;
629
916
  private toolsListLogged;
630
917
  constructor(context: CraftContext, options?: McpPluginOptions);
@@ -638,8 +925,10 @@ declare class McpServer {
638
925
  private startStdio;
639
926
  /**
640
927
  * Start HTTP transport (streamable-http).
641
- * The SDK transport does not create or listen on a port; we create a Node HTTP server
642
- * and call transport.handleRequest(req, res) for each request to /mcp.
928
+ * Creates a Node HTTP server that manages per-session MCP server+transport pairs.
929
+ * Each client session (identified by the `mcp-session-id` header) gets its own
930
+ * Server and StreamableHTTPServerTransport so that reconnecting clients can
931
+ * establish fresh sessions without restarting the process.
643
932
  */
644
933
  private startHttp;
645
934
  /**
@@ -647,10 +936,19 @@ declare class McpServer {
647
936
  */
648
937
  getHttpPort(): number | undefined;
649
938
  /**
650
- * Set up request handlers (shared by both transports).
651
- * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).
939
+ * Validate the Authorization header using the configured validator.
940
+ * Returns the authenticated principal on success, or `null` to reject with 401.
941
+ */
942
+ private validateAuth;
943
+ /**
944
+ * Set up request handlers on this.server (used by stdio transport).
652
945
  */
653
946
  private setupRequestHandlers;
947
+ /**
948
+ * Set up request handlers on the given server instance.
949
+ * Uses SDK request schemas so setRequestHandler receives a proper schema (method literal).
950
+ */
951
+ private setupRequestHandlersOn;
654
952
  /**
655
953
  * Stop the MCP server
656
954
  */
@@ -857,4 +1155,4 @@ declare function embeddingPlugin(options?: EmbeddingPluginOptions): CraftPlugin;
857
1155
  */
858
1156
  declare function disposeEmbeddingPipelineCache(): Promise<void>;
859
1157
 
860
- export { ADAPTER_LLM_OPTIONS, ADAPTER_LLM_PROVIDERS, ADAPTER_MCP_CLIENT_SERVERS, AgentDestinationAdapter, type AgentModelId, type AgentOptions, type AgentPromptSource, type AgentResult, BRAND, BRAND_MCP_ADAPTER, EmbeddingDestinationAdapter, type EmbeddingModelConfig, type EmbeddingModelConfigHuggingFace, type EmbeddingModelConfigOllama, type EmbeddingModelConfigOpenAI, type EmbeddingModelId, type EmbeddingOptions, type EmbeddingPluginOptions, type EmbeddingPluginProviders, type EmbeddingProviderType, type EmbeddingResult, type LlmAnthropicProviderOptions, LlmDestinationAdapter, type LlmGeminiProviderOptions, type LlmModelConfig, type LlmModelConfigAnthropic, type LlmModelConfigGemini, type LlmModelConfigOllama, type LlmModelConfigOpenAI, type LlmModelConfigOpenRouter, type LlmModelId, type LlmOllamaProviderOptions, type LlmOpenAIProviderOptions, type LlmOpenRouterProviderOptions, type LlmOptions, type LlmPluginOptions, type LlmPluginProviders, type LlmPromptSource, type LlmProviderRegistry, type LlmProviderType, type LlmResult, type LlmUsage, MCP_PLUGIN_REGISTERED, MCP_STDIO_MANAGERS, MCP_TOOL_REGISTRY, type McpArgsExtractor, type McpClientHttpConfig, type McpClientOptions, type McpClientServerConfig, type McpClientStdioConfig, type McpMessage, type McpOptions, type McpPluginOptions, McpServer, type McpServerOptions, type McpServerRegistry, type McpTool, McpToolRegistry, type McpToolRegistryEntry, type McpToolResult, type RegisteredLlmModelId, type RegisteredMcpServer, type RegisteredMcpShorthand, agent, defaultArgs, disposeEmbeddingPipelineCache, embedding, embeddingPlugin, isMcpAdapter, llm, llmPlugin, mcp, mcpPlugin, validateLlmPluginOptions, validateWithSchema };
1158
+ export { ADAPTER_LLM_OPTIONS, ADAPTER_LLM_PROVIDERS, ADAPTER_MCP_CLIENT_SERVERS, AgentDestinationAdapter, type AgentModelId, type AgentOptions, type AgentPromptSource, type AgentResult, type AuthPrincipal, BRAND, BRAND_MCP_ADAPTER, EmbeddingDestinationAdapter, type EmbeddingModelConfig, type EmbeddingModelConfigHuggingFace, type EmbeddingModelConfigOllama, type EmbeddingModelConfigOpenAI, type EmbeddingModelId, type EmbeddingOptions, type EmbeddingPluginOptions, type EmbeddingPluginProviders, type EmbeddingProviderType, type EmbeddingResult, type JwtAuthOptions, type JwtHmacOptions, type JwtRsaOptions, type LlmAnthropicProviderOptions, LlmDestinationAdapter, type LlmGeminiProviderOptions, type LlmModelConfig, type LlmModelConfigAnthropic, type LlmModelConfigGemini, type LlmModelConfigOllama, type LlmModelConfigOpenAI, type LlmModelConfigOpenRouter, type LlmModelId, type LlmOllamaProviderOptions, type LlmOpenAIProviderOptions, type LlmOpenRouterProviderOptions, type LlmOptions, type LlmPluginOptions, type LlmPluginProviders, type LlmPromptSource, type LlmProviderRegistry, type LlmProviderType, type LlmResult, type LlmUsage, MCP_PLUGIN_REGISTERED, MCP_STDIO_MANAGERS, MCP_TOOL_REGISTRY, type McpArgsExtractor, type McpAuthValidator, type McpClientAuthOptions, type McpClientHttpConfig, type McpClientOptions, type McpClientServerConfig, type McpClientStdioConfig, type McpClientTokenProvider, McpHeadersKeys, type McpHttpAuthOptions, type McpMessage, type McpOptions, type McpPluginOptions, McpServer, type McpServerOptions, type McpServerRegistry, type McpTool, McpToolRegistry, type McpToolRegistryEntry, type McpToolResult, type RegisteredLlmModelId, type RegisteredMcpServer, type RegisteredMcpShorthand, agent, defaultArgs, disposeEmbeddingPipelineCache, embedding, embeddingPlugin, isMcpAdapter, jwt, llm, llmPlugin, mcp, mcpPlugin, validateLlmPluginOptions, validateWithSchema };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import {getExchangeContext,rcError,isRoutecraftError,ADAPTER_DIRECT_REGISTRY,ADAPTER_DIRECT_STORE,DefaultExchange,direct}from'@routecraft/routecraft';import {jsonSchema,Output}from'ai';import {createServer}from'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=jsonSchema(t,{validate:n});return 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=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 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 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 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 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 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=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 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=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=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=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=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=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(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(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 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=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=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
- export{R as ADAPTER_LLM_OPTIONS,P as ADAPTER_LLM_PROVIDERS,w as ADAPTER_MCP_CLIENT_SERVERS,x as AgentDestinationAdapter,Y as BRAND,M as BRAND_MCP_ADAPTER,k as EmbeddingDestinationAdapter,E as LlmDestinationAdapter,S as MCP_PLUGIN_REGISTERED,T as MCP_STDIO_MANAGERS,j as MCP_TOOL_REGISTRY,b as McpServer,C as McpToolRegistry,de as agent,G as defaultArgs,K as disposeEmbeddingPipelineCache,me as embedding,ge as embeddingPlugin,ye as isMcpAdapter,re as llm,oe as llmPlugin,ie as mcp,pe as mcpPlugin,N as validateLlmPluginOptions,ae as validateWithSchema};//# sourceMappingURL=index.js.map
1
+ import {getExchangeContext,rcError,isRoutecraftError,ADAPTER_DIRECT_REGISTRY,ADAPTER_DIRECT_STORE,DefaultExchange,formatSchemaIssues,direct}from'@routecraft/routecraft';import {jsonSchema,Output}from'ai';import {createHmac,timingSafeEqual,createVerify}from'crypto';import {AsyncLocalStorage}from'async_hooks';import {createServer}from'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(formatSchemaIssues(a.issues))}:{success:true,value:a.value}}let o=jsonSchema(n,{validate:r});return 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=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 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 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 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 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 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=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 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=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||!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=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 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=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=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=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=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=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(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(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 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=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(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: ${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=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
+ export{S as ADAPTER_LLM_OPTIONS,M as ADAPTER_LLM_PROVIDERS,v as ADAPTER_MCP_CLIENT_SERVERS,O as AgentDestinationAdapter,te as BRAND,R as BRAND_MCP_ADAPTER,A as EmbeddingDestinationAdapter,E as LlmDestinationAdapter,b as MCP_PLUGIN_REGISTERED,T as MCP_STDIO_MANAGERS,U as MCP_TOOL_REGISTRY,N as McpHeadersKeys,x as McpServer,k as McpToolRegistry,Me as agent,V as defaultArgs,W as disposeEmbeddingPipelineCache,Ee as embedding,be as embeddingPlugin,Ce as isMcpAdapter,fe as jwt,ae as llm,pe as llmPlugin,ue as mcp,Pe as mcpPlugin,j as validateLlmPluginOptions,we as validateWithSchema};//# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map