@routecraft/ai 0.3.0 → 0.4.0-canary.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -22
- package/dist/index.d.ts +258 -22
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { ResolveKey, Exchange, Destination, MergedOptions, CraftContext, CraftPlugin, DirectServerOptions, DirectRouteMetadata, Source } from '@routecraft/routecraft';
|
|
1
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
-
import { Exchange, Destination, MergedOptions, CraftContext, CraftPlugin, DirectServerOptions, DirectRouteMetadata, Source } from '@routecraft/routecraft';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Cross-instance identity for @routecraft/ai: Symbol.for() keys and type guards.
|
|
@@ -10,9 +10,92 @@ declare const BRAND: {
|
|
|
10
10
|
};
|
|
11
11
|
declare function isMcpAdapter(obj: unknown): boolean;
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Type registries for AI adapter compile-time safety via declaration merging.
|
|
15
|
+
*
|
|
16
|
+
* Users populate these empty interfaces to constrain `llm()` and `mcp()`
|
|
17
|
+
* adapters to only accept configured providers and servers. When a registry
|
|
18
|
+
* is empty, the adapter falls back to accepting any string.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* declare module '@routecraft/ai' {
|
|
23
|
+
* interface LlmProviderRegistry {
|
|
24
|
+
* openai: true;
|
|
25
|
+
* anthropic: true;
|
|
26
|
+
* ollama: true;
|
|
27
|
+
* }
|
|
28
|
+
* interface McpServerRegistry {
|
|
29
|
+
* github: true;
|
|
30
|
+
* 'local-postgres': true;
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* // Now llm('openai:gpt-5') autocompletes the provider prefix.
|
|
35
|
+
* // llm('qwen:model') shows a red line if qwen is not registered.
|
|
36
|
+
* // mcp('github:create_issue') autocompletes server names.
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Registry for configured LLM providers.
|
|
42
|
+
*
|
|
43
|
+
* Keys are provider names (e.g. 'openai', 'anthropic', 'ollama'),
|
|
44
|
+
* values should be `true`. Populate via declaration merging to constrain
|
|
45
|
+
* the `llm()` adapter's model ID prefix.
|
|
46
|
+
*
|
|
47
|
+
* @experimental
|
|
48
|
+
*/
|
|
49
|
+
interface LlmProviderRegistry {
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Registry for configured MCP servers.
|
|
53
|
+
*
|
|
54
|
+
* Keys are server names (e.g. 'github', 'local-postgres'),
|
|
55
|
+
* values should be `true`. Populate via declaration merging to constrain
|
|
56
|
+
* the `mcp()` adapter's shorthand syntax.
|
|
57
|
+
*
|
|
58
|
+
* @experimental
|
|
59
|
+
*/
|
|
60
|
+
interface McpServerRegistry {
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolved LLM model ID type.
|
|
64
|
+
* When `LlmProviderRegistry` is populated, constrains to `'provider:modelName'`
|
|
65
|
+
* where provider must be a registered key. Falls back to `string` when empty.
|
|
66
|
+
*
|
|
67
|
+
* @experimental
|
|
68
|
+
*/
|
|
69
|
+
type RegisteredLlmModelId = keyof LlmProviderRegistry extends never ? string : `${Extract<keyof LlmProviderRegistry, string>}:${string}`;
|
|
70
|
+
/**
|
|
71
|
+
* Resolved MCP server key type.
|
|
72
|
+
* When `McpServerRegistry` is populated, resolves to the union of registered server keys
|
|
73
|
+
* (e.g. `'github' | 'local-postgres'`) via `ResolveKey<McpServerRegistry>`.
|
|
74
|
+
* Falls back to `string` when empty.
|
|
75
|
+
*
|
|
76
|
+
* For the `'server:tool'` shorthand format, see `RegisteredMcpShorthand`.
|
|
77
|
+
*
|
|
78
|
+
* @experimental
|
|
79
|
+
*/
|
|
80
|
+
type RegisteredMcpServer = ResolveKey<McpServerRegistry>;
|
|
81
|
+
/**
|
|
82
|
+
* Resolved MCP shorthand type for 'server:tool' syntax.
|
|
83
|
+
* When `McpServerRegistry` is populated, constrains the server prefix.
|
|
84
|
+
* Falls back to `` `${string}:${string}` `` when empty.
|
|
85
|
+
*
|
|
86
|
+
* @experimental
|
|
87
|
+
*/
|
|
88
|
+
type RegisteredMcpShorthand = keyof McpServerRegistry extends never ? `${string}:${string}` : `${Extract<keyof McpServerRegistry, string>}:${string}`;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Store key for plugin-registered providers (provider id -> LlmModelConfig).
|
|
92
|
+
* @experimental
|
|
93
|
+
*/
|
|
14
94
|
declare const ADAPTER_LLM_PROVIDERS: unique symbol;
|
|
15
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Store key for context-level default LLM options.
|
|
97
|
+
* @experimental
|
|
98
|
+
*/
|
|
16
99
|
declare const ADAPTER_LLM_OPTIONS: unique symbol;
|
|
17
100
|
declare module "@routecraft/routecraft" {
|
|
18
101
|
interface StoreRegistry {
|
|
@@ -152,10 +235,11 @@ interface LlmPluginOptions {
|
|
|
152
235
|
* registered via llmPlugin({ providers: { ollama: { provider: "ollama" }, ... } }).
|
|
153
236
|
* When options.outputSchema is provided, the result type narrows so body.output is typed downstream.
|
|
154
237
|
*
|
|
238
|
+
* @experimental
|
|
155
239
|
* @param modelId - "providerId:modelName"; the provider is resolved from the plugin, the model name is sent to the provider.
|
|
156
240
|
* @param options - Optional overrides (systemPrompt, userPrompt, temperature, maxTokens, outputSchema, etc.). User prompt defaults to exchange.body.
|
|
157
241
|
*/
|
|
158
|
-
declare function llm<S extends StandardSchemaV1 | undefined = undefined>(modelId:
|
|
242
|
+
declare function llm<S extends StandardSchemaV1 | undefined = undefined>(modelId: RegisteredLlmModelId, options?: Partial<LlmOptions> & {
|
|
159
243
|
outputSchema?: S;
|
|
160
244
|
}): Destination<unknown, LlmResultWithOutput<S>>;
|
|
161
245
|
|
|
@@ -164,6 +248,7 @@ declare function llm<S extends StandardSchemaV1 | undefined = undefined>(modelId
|
|
|
164
248
|
* resolves the provider from the plugin store, merges options, and calls the provider with the model name.
|
|
165
249
|
* Use with .enrich(llm("providerId:modelName", options)) or .to(llm(...)).
|
|
166
250
|
*
|
|
251
|
+
* @experimental
|
|
167
252
|
* @template S - Output schema type when outputSchema is provided; narrows result.output for downstream typing.
|
|
168
253
|
*/
|
|
169
254
|
declare class LlmDestinationAdapter<S extends StandardSchemaV1 | undefined = undefined> implements Destination<unknown, LlmResultWithOutput<S>>, MergedOptions<LlmOptionsMerged> {
|
|
@@ -187,6 +272,8 @@ declare class LlmDestinationAdapter<S extends StandardSchemaV1 | undefined = und
|
|
|
187
272
|
*
|
|
188
273
|
* Advanced users can set the store directly: context.setStore(ADAPTER_LLM_PROVIDERS, map)
|
|
189
274
|
* and context.setStore(ADAPTER_LLM_OPTIONS, partialOptions) without using this plugin.
|
|
275
|
+
*
|
|
276
|
+
* @experimental
|
|
190
277
|
*/
|
|
191
278
|
declare function llmPlugin(options?: LlmPluginOptions): CraftPlugin;
|
|
192
279
|
|
|
@@ -196,14 +283,97 @@ declare function llmPlugin(options?: LlmPluginOptions): CraftPlugin;
|
|
|
196
283
|
*/
|
|
197
284
|
declare function validateLlmPluginOptions(options: LlmPluginOptions): void;
|
|
198
285
|
|
|
199
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Central registry of all MCP tools from all sources.
|
|
288
|
+
* Stored in context store under MCP_TOOL_REGISTRY for agent adapter discovery.
|
|
289
|
+
*
|
|
290
|
+
* Sources:
|
|
291
|
+
* - "local": mcp() routes from ADAPTER_DIRECT_REGISTRY (tools exposed by this context)
|
|
292
|
+
* - stdio clients: long-lived subprocess MCP servers
|
|
293
|
+
* - HTTP clients: remote HTTP MCP servers (tools refreshed periodically)
|
|
294
|
+
*
|
|
295
|
+
* @experimental
|
|
296
|
+
*/
|
|
297
|
+
declare class McpToolRegistry {
|
|
298
|
+
/** Nested Map: source -> toolName -> McpToolRegistryEntry */
|
|
299
|
+
private tools;
|
|
300
|
+
/**
|
|
301
|
+
* Set all tools for a given source (replaces previous tools from that source).
|
|
302
|
+
* Called on initial tool listing and on re-listing after restart/refresh.
|
|
303
|
+
*
|
|
304
|
+
* @param source - Identifier for the tool source (e.g. server ID or "local")
|
|
305
|
+
* @param transport - How the tools are reached ("stdio", "http", or "local")
|
|
306
|
+
* @param tools - Tool definitions to register for this source
|
|
307
|
+
*/
|
|
308
|
+
setToolsForSource(source: string, transport: "stdio" | "http" | "local", tools: Array<{
|
|
309
|
+
name: string;
|
|
310
|
+
description?: string;
|
|
311
|
+
inputSchema: Record<string, unknown>;
|
|
312
|
+
}>): void;
|
|
313
|
+
/**
|
|
314
|
+
* Remove all tools for a source (when a client is permanently stopped).
|
|
315
|
+
*
|
|
316
|
+
* @param source - Source identifier whose tools should be removed
|
|
317
|
+
*/
|
|
318
|
+
removeSource(source: string): void;
|
|
319
|
+
/**
|
|
320
|
+
* Get all tools across all sources.
|
|
321
|
+
*
|
|
322
|
+
* @returns Array of every registered tool entry
|
|
323
|
+
*/
|
|
324
|
+
getTools(): McpToolRegistryEntry[];
|
|
325
|
+
/**
|
|
326
|
+
* Get tools from a specific source/server.
|
|
327
|
+
*
|
|
328
|
+
* @param serverId - Source identifier to filter by
|
|
329
|
+
* @returns Array of tool entries belonging to that source
|
|
330
|
+
*/
|
|
331
|
+
getToolsByServer(serverId: string): McpToolRegistryEntry[];
|
|
332
|
+
/**
|
|
333
|
+
* Get a specific tool by name. Returns first match if name exists in multiple sources.
|
|
334
|
+
*
|
|
335
|
+
* @param name - Tool name to search for
|
|
336
|
+
* @returns The first matching entry, or undefined
|
|
337
|
+
*/
|
|
338
|
+
getTool(name: string): McpToolRegistryEntry | undefined;
|
|
339
|
+
/**
|
|
340
|
+
* Get a specific tool by source and name.
|
|
341
|
+
*
|
|
342
|
+
* @param source - Source identifier
|
|
343
|
+
* @param name - Tool name
|
|
344
|
+
* @returns The matching entry, or undefined
|
|
345
|
+
*/
|
|
346
|
+
getToolBySource(source: string, name: string): McpToolRegistryEntry | undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Store key set by mcpPlugin() when applied; routes using .from(mcp(...)) require it.
|
|
351
|
+
* @internal
|
|
352
|
+
*/
|
|
200
353
|
declare const MCP_PLUGIN_REGISTERED: unique symbol;
|
|
201
|
-
/**
|
|
354
|
+
/**
|
|
355
|
+
* Store key for named remote MCP servers (mcpPlugin({ clients })). Used by McpClient to resolve serverId.
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
202
358
|
declare const ADAPTER_MCP_CLIENT_SERVERS: unique symbol;
|
|
359
|
+
/**
|
|
360
|
+
* Store key for the unified MCP tool registry. Used by agent adapter for tool discovery.
|
|
361
|
+
* @internal
|
|
362
|
+
*/
|
|
363
|
+
declare const MCP_TOOL_REGISTRY: unique symbol;
|
|
364
|
+
/**
|
|
365
|
+
* Store key for stdio client managers. Used by destination adapter to call tools on stdio clients.
|
|
366
|
+
* @internal
|
|
367
|
+
*/
|
|
368
|
+
declare const MCP_STDIO_MANAGERS: unique symbol;
|
|
203
369
|
declare module "@routecraft/routecraft" {
|
|
204
370
|
interface StoreRegistry {
|
|
205
371
|
[MCP_PLUGIN_REGISTERED]: boolean;
|
|
206
|
-
[ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig$1 | string>;
|
|
372
|
+
[ADAPTER_MCP_CLIENT_SERVERS]: Map<string, McpClientHttpConfig$1 | McpClientStdioConfig | string>;
|
|
373
|
+
[MCP_TOOL_REGISTRY]: McpToolRegistry;
|
|
374
|
+
[MCP_STDIO_MANAGERS]: Map<string, {
|
|
375
|
+
callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
376
|
+
}>;
|
|
207
377
|
}
|
|
208
378
|
}
|
|
209
379
|
/**
|
|
@@ -215,15 +385,22 @@ interface McpClientHttpConfig$1 {
|
|
|
215
385
|
url: string;
|
|
216
386
|
}
|
|
217
387
|
/**
|
|
218
|
-
* Stdio client config for a
|
|
219
|
-
*
|
|
388
|
+
* Stdio client config for a local MCP server subprocess.
|
|
389
|
+
* Used in mcpPlugin({ clients: { name: config } }).
|
|
390
|
+
* The plugin spawns the process, manages its lifecycle, and auto-restarts on crash.
|
|
220
391
|
*/
|
|
221
392
|
interface McpClientStdioConfig {
|
|
222
393
|
transport: "stdio";
|
|
394
|
+
/** The executable to run (e.g. "npx", "node", "python"). */
|
|
223
395
|
command: string;
|
|
396
|
+
/** Command line arguments to pass to the executable. */
|
|
224
397
|
args?: string[];
|
|
398
|
+
/** Environment variables for the child process. Defaults to a safe subset of the parent env. */
|
|
399
|
+
env?: Record<string, string>;
|
|
400
|
+
/** Working directory for the child process. Defaults to the current working directory. */
|
|
401
|
+
cwd?: string;
|
|
225
402
|
}
|
|
226
|
-
/** Union of client configs
|
|
403
|
+
/** Union of client configs accepted by mcpPlugin({ clients }). */
|
|
227
404
|
type McpClientServerConfig = McpClientHttpConfig$1 | McpClientStdioConfig;
|
|
228
405
|
/**
|
|
229
406
|
* Options for the MCP plugin (mcpPlugin).
|
|
@@ -246,10 +423,32 @@ interface McpPluginOptions {
|
|
|
246
423
|
*/
|
|
247
424
|
tools?: string[] | ((meta: DirectRouteMetadata) => boolean);
|
|
248
425
|
/**
|
|
249
|
-
* Named remote MCP servers for .to(mcp("name:tool")).
|
|
250
|
-
*
|
|
426
|
+
* Named remote MCP servers for .to(mcp("name:tool")).
|
|
427
|
+
* Keys are server names; values are HTTP or stdio config.
|
|
428
|
+
* Stdio clients are managed as subprocesses with auto-restart.
|
|
429
|
+
* HTTP clients are used for ephemeral tool calls.
|
|
251
430
|
*/
|
|
252
|
-
clients?: Record<string, McpClientHttpConfig$1>;
|
|
431
|
+
clients?: Record<string, McpClientHttpConfig$1 | McpClientStdioConfig>;
|
|
432
|
+
/**
|
|
433
|
+
* Max auto-restart attempts for stdio clients before giving up.
|
|
434
|
+
* Applies to all stdio clients. Default: 5.
|
|
435
|
+
*/
|
|
436
|
+
maxRestarts?: number;
|
|
437
|
+
/**
|
|
438
|
+
* Base delay in ms before the first restart attempt.
|
|
439
|
+
* Subsequent attempts use exponential backoff. Default: 1000.
|
|
440
|
+
*/
|
|
441
|
+
restartDelayMs?: number;
|
|
442
|
+
/**
|
|
443
|
+
* Multiplier for exponential backoff between restart attempts.
|
|
444
|
+
* Delay = restartDelayMs * (restartBackoffMultiplier ^ restartCount). Default: 2.
|
|
445
|
+
*/
|
|
446
|
+
restartBackoffMultiplier?: number;
|
|
447
|
+
/**
|
|
448
|
+
* Interval in ms to re-list tools from HTTP clients.
|
|
449
|
+
* Set to 0 to disable periodic refresh. Default: 60000 (60s).
|
|
450
|
+
*/
|
|
451
|
+
toolRefreshIntervalMs?: number;
|
|
253
452
|
}
|
|
254
453
|
/**
|
|
255
454
|
* Options for mcp() when used as a server in .from().
|
|
@@ -268,9 +467,11 @@ type McpArgsExtractor$1 = (exchange: Exchange<unknown>) => Record<string, unknow
|
|
|
268
467
|
* Options for mcp() when used as a Client in .to() to call a remote MCP server.
|
|
269
468
|
* Provide either url (inline HTTP) or serverId (from plugin/store); tool is required.
|
|
270
469
|
*
|
|
271
|
-
*
|
|
272
|
-
* HTTP endpoint or `serverId` for a named backend
|
|
273
|
-
*
|
|
470
|
+
* Supported transports:
|
|
471
|
+
* - **HTTP:** use `url` for an inline endpoint or `serverId` for a named backend.
|
|
472
|
+
* - **Stdio:** use `serverId` referencing a stdio client from mcpPlugin({ clients }).
|
|
473
|
+
* The destination adapter resolves the manager from the context store and calls
|
|
474
|
+
* tools directly on the subprocess -- no HTTP involved.
|
|
274
475
|
*/
|
|
275
476
|
interface McpClientOptions {
|
|
276
477
|
/** URL of the remote MCP server (HTTP/HTTPS only). Omit when using serverId. */
|
|
@@ -281,7 +482,7 @@ interface McpClientOptions {
|
|
|
281
482
|
serverId?: string;
|
|
282
483
|
/**
|
|
283
484
|
* Extract tool arguments from the exchange. Receives the full exchange.
|
|
284
|
-
* Default: body as object
|
|
485
|
+
* Default: body as object -> use as args; otherwise { input: body }.
|
|
285
486
|
*/
|
|
286
487
|
args?: McpArgsExtractor$1;
|
|
287
488
|
}
|
|
@@ -298,6 +499,22 @@ interface McpTool {
|
|
|
298
499
|
[key: string]: unknown;
|
|
299
500
|
};
|
|
300
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* A tool entry in the unified MCP tool registry.
|
|
504
|
+
* Combines local mcp() route tools and remote client tools (stdio and HTTP).
|
|
505
|
+
*/
|
|
506
|
+
interface McpToolRegistryEntry {
|
|
507
|
+
/** Tool name (unique within a source, may collide across sources). */
|
|
508
|
+
name: string;
|
|
509
|
+
/** Human-readable description of the tool. */
|
|
510
|
+
description?: string;
|
|
511
|
+
/** JSON Schema for tool input. */
|
|
512
|
+
inputSchema: Record<string, unknown>;
|
|
513
|
+
/** Source server ID. Local mcp() routes use "local". */
|
|
514
|
+
source: string;
|
|
515
|
+
/** Transport type of the source. */
|
|
516
|
+
transport: "stdio" | "http" | "local";
|
|
517
|
+
}
|
|
301
518
|
/**
|
|
302
519
|
* MCP tool call result
|
|
303
520
|
*/
|
|
@@ -329,6 +546,7 @@ interface McpClientHttpConfig {
|
|
|
329
546
|
/**
|
|
330
547
|
* Brand symbol for MCP adapter type checking.
|
|
331
548
|
* Used internally to identify MCP adapters.
|
|
549
|
+
* @experimental
|
|
332
550
|
*/
|
|
333
551
|
declare const BRAND_MCP_ADAPTER: unique symbol;
|
|
334
552
|
|
|
@@ -358,6 +576,8 @@ declare const defaultArgs: McpArgsExtractor$1;
|
|
|
358
576
|
* @param options - Server options (source) or args extractor (destination)
|
|
359
577
|
* @returns Source when called with two arguments; Destination when called with one argument
|
|
360
578
|
*
|
|
579
|
+
* @experimental
|
|
580
|
+
*
|
|
361
581
|
* @example
|
|
362
582
|
* ```typescript
|
|
363
583
|
* // Source route (MCP tool)
|
|
@@ -374,23 +594,29 @@ declare function mcp<S extends StandardSchemaV1 | undefined = undefined>(endpoin
|
|
|
374
594
|
schema?: S;
|
|
375
595
|
}): Source<McpMessage<S>>;
|
|
376
596
|
declare function mcp(clientOptions: McpClientOptions): Destination<unknown, unknown>;
|
|
377
|
-
declare function mcp(shorthand:
|
|
597
|
+
declare function mcp(shorthand: RegisteredMcpShorthand, options?: {
|
|
378
598
|
args?: McpArgsExtractor$1;
|
|
379
599
|
}): Destination<unknown, unknown>;
|
|
380
600
|
|
|
381
601
|
/**
|
|
382
602
|
* 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.
|
|
383
603
|
* Optional clients: register named remote MCP servers so routes can use .to(mcp("name:tool")) without passing url.
|
|
604
|
+
* Stdio clients are spawned as subprocesses with auto-restart; HTTP clients are used for ephemeral tool calls.
|
|
605
|
+
* All discovered external tools (stdio, HTTP) are stored in a unified McpToolRegistry for agent adapter discovery.
|
|
384
606
|
* Required when any route uses .from(mcp(...)); the route will fail at start if this plugin is not applied.
|
|
607
|
+
*
|
|
608
|
+
* @experimental
|
|
385
609
|
*/
|
|
386
610
|
declare function mcpPlugin(options?: McpPluginOptions): CraftPlugin;
|
|
387
611
|
|
|
388
612
|
/**
|
|
389
|
-
* McpServer wraps the MCP SDK and bridges it to
|
|
613
|
+
* McpServer wraps the MCP SDK and bridges it to Routecraft's DirectChannel infrastructure.
|
|
390
614
|
* It reads the MCP route registry lazily (on first tools/list request) to ensure routes have subscribed.
|
|
391
615
|
*
|
|
392
616
|
* Note: Uses dynamic imports to avoid TypeScript compatibility issues with the MCP SDK.
|
|
393
617
|
* Supports both stdio and streamable-http transports.
|
|
618
|
+
*
|
|
619
|
+
* @experimental
|
|
394
620
|
*/
|
|
395
621
|
declare class McpServer {
|
|
396
622
|
private context;
|
|
@@ -439,7 +665,7 @@ declare class McpServer {
|
|
|
439
665
|
*/
|
|
440
666
|
getAvailableTools(): Array<Record<string, unknown>>;
|
|
441
667
|
/**
|
|
442
|
-
* Convert
|
|
668
|
+
* Convert Routecraft mcp() route metadata to MCP tool format
|
|
443
669
|
*/
|
|
444
670
|
private metadataToMcpTool;
|
|
445
671
|
/**
|
|
@@ -503,6 +729,7 @@ interface AgentResult {
|
|
|
503
729
|
/**
|
|
504
730
|
* Create an agent destination.
|
|
505
731
|
*
|
|
732
|
+
* @experimental
|
|
506
733
|
* @param options Agent options (modelId, systemPrompt, allowedRoutes, etc.)
|
|
507
734
|
* @returns Destination that produces AgentResult
|
|
508
735
|
*/
|
|
@@ -511,6 +738,8 @@ declare function agent(options: AgentOptions): Destination<unknown, AgentResult>
|
|
|
511
738
|
/**
|
|
512
739
|
* Agent destination adapter.
|
|
513
740
|
* Use via agent(); do not instantiate AgentRunner directly.
|
|
741
|
+
*
|
|
742
|
+
* @experimental
|
|
514
743
|
*/
|
|
515
744
|
declare class AgentDestinationAdapter implements Destination<unknown, AgentResult> {
|
|
516
745
|
readonly adapterId = "routecraft.adapter.agent";
|
|
@@ -583,6 +812,8 @@ type EmbeddingModelId = "huggingface:all-MiniLM-L6-v2" | "huggingface:sentence-t
|
|
|
583
812
|
* Embedding destination adapter. Expects model id as "providerId:modelName"
|
|
584
813
|
* (e.g. huggingface:all-MiniLM-L6-v2), resolves the provider from the plugin store,
|
|
585
814
|
* and returns { embedding: number[] }. Use with .enrich(embedding("provider:model", { using: ... })).
|
|
815
|
+
*
|
|
816
|
+
* @experimental
|
|
586
817
|
*/
|
|
587
818
|
declare class EmbeddingDestinationAdapter<T = unknown> implements Destination<T, EmbeddingResult>, MergedOptions<EmbeddingOptions> {
|
|
588
819
|
private readonly modelId;
|
|
@@ -598,6 +829,7 @@ declare class EmbeddingDestinationAdapter<T = unknown> implements Destination<T,
|
|
|
598
829
|
* Use with .enrich(). Pass model id as "providerId:modelName" (e.g. huggingface:all-MiniLM-L6-v2).
|
|
599
830
|
* The provider must be registered via embeddingPlugin({ providers: { huggingface: {} } }).
|
|
600
831
|
*
|
|
832
|
+
* @experimental
|
|
601
833
|
* @param modelId - "providerId:modelName"; the provider is resolved from the plugin.
|
|
602
834
|
* @param options - using(exchange) returns the string (or string[]) to embed.
|
|
603
835
|
*/
|
|
@@ -608,6 +840,8 @@ declare function embedding<T = unknown>(modelId: EmbeddingModelId, options?: Par
|
|
|
608
840
|
* cache when the context stops (releases native/ONNX resources).
|
|
609
841
|
* Use embedding("providerId:modelName", { using: ... }),
|
|
610
842
|
* e.g. embedding("huggingface:all-MiniLM-L6-v2", { using: (e) => e.body.title }).
|
|
843
|
+
*
|
|
844
|
+
* @experimental
|
|
611
845
|
*/
|
|
612
846
|
declare function embeddingPlugin(options?: EmbeddingPluginOptions): CraftPlugin;
|
|
613
847
|
|
|
@@ -615,10 +849,12 @@ declare function embeddingPlugin(options?: EmbeddingPluginOptions): CraftPlugin;
|
|
|
615
849
|
* Dispose all cached HuggingFace pipelines and clear the cache. Call on context
|
|
616
850
|
* shutdown so pipeline.dispose() releases native/ONNX resources.
|
|
617
851
|
*
|
|
618
|
-
* Callers must avoid process.exit() after this
|
|
852
|
+
* Callers must avoid process.exit() after this -- let the event loop drain
|
|
619
853
|
* naturally so ONNX Runtime's C++ statics aren't torn down prematurely
|
|
620
854
|
* (onnxruntime#25038: "mutex lock failed: Invalid argument").
|
|
855
|
+
*
|
|
856
|
+
* @experimental
|
|
621
857
|
*/
|
|
622
858
|
declare function disposeEmbeddingPipelineCache(): Promise<void>;
|
|
623
859
|
|
|
624
|
-
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 LlmProviderType, type LlmResult, type LlmUsage, MCP_PLUGIN_REGISTERED, type McpArgsExtractor, type McpClientHttpConfig, type McpClientOptions, type McpClientServerConfig, type McpClientStdioConfig, type McpMessage, type McpOptions, type McpPluginOptions, McpServer, type McpServerOptions, type McpTool, type McpToolResult, agent, defaultArgs, disposeEmbeddingPipelineCache, embedding, embeddingPlugin, isMcpAdapter, llm, llmPlugin, mcp, mcpPlugin, validateLlmPluginOptions, validateWithSchema };
|
|
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 };
|
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 V={McpAdapter:Symbol.for("routecraft.ai.McpAdapter")};function ae(t,e){return typeof t=="object"&&t!==null&&t[e]===true}function pe(t){return ae(t,V.McpAdapter)}function L(t,e){throw new Error(`The ${e} LLM provider requires the "${t}" package. Install it with: pnpm add ${t}`)}function O(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 de={ollama:{baseURL:"http://localhost:11434/api"}};function C(t){return {...t.inputTokens!==void 0&&{inputTokens:t.inputTokens},...t.outputTokens!==void 0&&{outputTokens:t.outputTokens},...t.totalTokens!==void 0&&{totalTokens:t.totalTokens}}}function k(t){try{if("output"in t&&t.output!==void 0)return t.output}catch{}}function K(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 F(t){let{config:e,modelId:n,options:r,systemPrompt:o,userPrompt:i,output:s}=t;switch(e.provider){case "openai":return le(e,n,r,o,i,s);case "anthropic":return ce(e,n,r,o,i,s);case "gemini":return ue(e,n,r,o,i,s);case "openrouter":return me(e,n,r,o,i,s);case "ollama":return ge(e,n,r,o,i,s);default:{let d=e;throw new Error(`LLM provider not implemented: ${d.provider}`)}}}async function le(t,e,n,r,o,i){let s;try{s=(await import('@ai-sdk/openai')).createOpenAI;}catch(y){throw O(y,"@ai-sdk/openai")&&L("@ai-sdk/openai","OpenAI"),y}let{generateText:d}=await import('ai'),c={apiKey:t.apiKey};t.baseURL!==void 0&&(c.baseURL=t.baseURL);let l={model:s(c)(e),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 p=i!==void 0?{...l,output:i}:l,g=await d(p),u={text:g.text??"",raw:g};g.usage&&(u.usage=C(g.usage));let m=k(g);return m!==void 0&&(u.output=m),u}async function ce(t,e,n,r,o,i){let s;try{s=(await import('@ai-sdk/anthropic')).createAnthropic;}catch(m){throw O(m,"@ai-sdk/anthropic")&&L("@ai-sdk/anthropic","Anthropic"),m}let{generateText:d}=await import('ai'),a={model:s({apiKey:t.apiKey})(e),prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(a.system=r),n.topP!==void 0&&(a.topP=n.topP),n.frequencyPenalty!==void 0&&(a.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(a.presencePenalty=n.presencePenalty);let l=i!==void 0?{...a,output:i}:a,p=await d(l),g={text:p.text??"",raw:p};p.usage&&(g.usage=C(p.usage));let u=k(p);return u!==void 0&&(g.output=u),g}async function ue(t,e,n,r,o,i){let s;try{s=(await import('@ai-sdk/google')).createGoogleGenerativeAI;}catch(m){throw O(m,"@ai-sdk/google")&&L("@ai-sdk/google","Gemini"),m}let{generateText:d}=await import('ai'),a={model:s({apiKey:t.apiKey})(e),prompt:o,...n.maxTokens!==void 0&&{maxOutputTokens:n.maxTokens},temperature:n.temperature};r&&(a.system=r),n.topP!==void 0&&(a.topP=n.topP),n.frequencyPenalty!==void 0&&(a.frequencyPenalty=n.frequencyPenalty),n.presencePenalty!==void 0&&(a.presencePenalty=n.presencePenalty);let l=i!==void 0?{...a,output:i}:a,p=await d(l),g={text:p.text??"",raw:p};p.usage&&(g.usage=C(p.usage));let u=k(p);return u!==void 0&&(g.output=u),g}async function me(t,e,n,r,o,i){let s;try{s=(await import('@openrouter/ai-sdk-provider')).createOpenRouter;}catch(P){throw O(P,"@openrouter/ai-sdk-provider")&&L("@openrouter/ai-sdk-provider","OpenRouter"),P}let{generateText:d}=await import('ai'),c=s({apiKey:t.apiKey}),f=t.modelId??e,a=c.chat(f);K(a,"OpenRouter",f);let p={model:a,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 g=i!==void 0?{...p,output:i}:p,u=await d(g),m={text:u.text??"",raw:u};u.usage&&(m.usage=C(u.usage));let y=k(u);return y!==void 0&&(m.output=y),m}async function ge(t,e,n,r,o,i){let s;try{s=(await import('ollama-ai-provider-v2')).createOllama;}catch(P){throw O(P,"ollama-ai-provider-v2")&&L("ollama-ai-provider-v2","Ollama"),P}let{generateText:d}=await import('ai'),c=s({baseURL:t.baseURL??de.ollama.baseURL}),f=t.modelId??e,a=c(f);K(a,"Ollama",f);let p={model:a,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 g=i!==void 0?{...p,output:i}:p,u=await d(g),m={text:u.text??"",raw:u};u.usage&&(m.usage=C(u.usage));let y=k(u);return y!==void 0&&(m.output=y),m}function B(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 s;try{s=e.validate(i);}catch(c){return {success:false,error:c instanceof Error?c:new Error(String(c))}}return s instanceof Promise?{success:false,error:new Error("Async output schema is not supported for LLM structured output")}:s.issues!=null&&(Array.isArray(s.issues)?s.issues.length>0:typeof s.issues=="object"&&s.issues!==null?Object.keys(s.issues).length>0:!!s.issues)?{success:false,error:new Error(typeof s.issues=="string"?s.issues:JSON.stringify(s.issues))}:{success:true,value:s.value}}let o=jsonSchema(n,{validate:r});return Output.object({schema:o})}var w=Symbol.for("routecraft.adapter.llm.providers"),M=Symbol.for("routecraft.adapter.llm.options");async function Pe(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 we=0,ve=1024;function W(t,e){return t===void 0||t===""?"":typeof t=="function"?t(e):t}function Me(t){let e=t.body;return typeof e=="string"?e:e==null?"":typeof e=="object"?JSON.stringify(e):String(e)}function J(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 Ee(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 "${w.description}" can be read.`);let n=e.getStore(w);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}=J(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(M);return {temperature:we,maxTokens:ve,...n,...this.options}}async send(e){let n=getExchangeContext(e),{config:r,modelName:o}=Ee(this.modelId,n),i=this.mergedOptions(n),s=W(i.systemPrompt,e),d=W(i.userPrompt,e)||Me(e),c={temperature:i.temperature,maxTokens:i.maxTokens};i.topP!==void 0&&(c.topP=i.topP),i.frequencyPenalty!==void 0&&(c.frequencyPenalty=i.frequencyPenalty),i.presencePenalty!==void 0&&(c.presencePenalty=i.presencePenalty);let f=i.outputSchema!==void 0?B(i.outputSchema):void 0,a=await F({config:r,modelId:o,options:c,systemPrompt:s,userPrompt:d,output:f});if(a.output===void 0&&a.text&&i.outputSchema!==void 0){let l=await Pe(a.text,i.outputSchema);l!==void 0&&(a.output=l);}return a}getMetadata(e){let n=e,{providerId:r}=J(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 z(t,e){return new E(t,e)}var X=["openai","anthropic","openrouter","ollama","gemini"];function be(t){return X.includes(t)}function I(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: ${X.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 xe=["openai","anthropic","openrouter","ollama","gemini"];function Re(t,e){return {provider:t,...e}}function Y(t={providers:{}}){return I(t),{apply(e){let n=new Map;for(let r of xe){let o=t.providers[r];o!==void 0&&n.set(r,Re(r,o));}e.setStore(w,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore(M,t.defaultOptions);}}}var b=Symbol.for("routecraft.mcp.plugin.registered"),h=Symbol.for("routecraft.mcp.client.servers");var v=Symbol.for("routecraft.mcp.adapter");var _=class{adapterId="routecraft.adapter.mcp";endpoint;options;constructor(e,n){if(this[v]=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 Oe(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 Ce(t,e){if(t.url)return Oe(t.url),t.url;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(h)}" can be read.`);if(t.serverId&&e){let r=e.getStore(h)?.get(t.serverId);if(!r)throw new Error(`MCP client: serverId "${t.serverId}" not found in context store. Register it with context store key "${String(h)}".`);return typeof r=="string"?r:r.url}throw new Error("MCP client: either url or serverId must be provided in McpClientOptions.")}var N=t=>typeof t.body=="object"&&t.body!==null?t.body:{input:t.body},T=class{constructor(e){this.options=e;if(this[v]=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=Ce(this.options,n),o=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(!o)throw new Error("MCP client: tool name required. Set options.tool or exchange.body.tool.");let s=(this.options.args??N)(e),d=await this.callRemoteTool(r,o,s);return d&&typeof d=="object"&&(d.metadata={toolName:o,url:r,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:"http"}}async callRemoteTool(e,n,r){let o,i;try{o=await import('@modelcontextprotocol/sdk/client/index.js'),i=await import('@modelcontextprotocol/sdk/client/streamableHttp.js');}catch{throw new Error('MCP client requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk')}let s=o.Client,d=i.StreamableHTTPClientTransport,c=new URL(e),f=new d(c),a={name:"routecraft-mcp-client",version:"1.0.0"},l=new s(a,{capabilities:{}});try{await l.connect.call(l,f);let u=await l.callTool.call(l,{name:n,arguments:r}),m=u?.content;if(Array.isArray(m)&&m.length>0){let y=m[0];if(y&&typeof y=="object"&&"text"in y)return y.text;if(y&&typeof y=="object"&&"data"in y)return y.data}return u}finally{let p=l,g=p.close??p.disconnect;if(typeof g=="function")try{await Promise.resolve(g.call(l));}catch{}let u=f,m=u.close??u.destroy;if(typeof m=="function")try{await Promise.resolve(m.call(f));}catch{}}}};function Q(t,e){if(typeof t=="object"&&t!==null&&("url"in t||"serverId"in t))return new T(t);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),s=t.slice(o+1),d={serverId:i,tool:s};return e!==void 0&&typeof e=="object"&&"args"in e&&e.args!==void 0&&(d.args=e.args),new T(d)}let r=t;if(e!==void 0)return new _(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 H='MCP server requires "@modelcontextprotocol/sdk". Install it with: pnpm add @modelcontextprotocol/sdk',x=class{context;options;server=null;transport=null;httpServer=null;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(H)}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(H)}let r=(await import('@modelcontextprotocol/sdk/server/streamableHttp.js').catch(()=>null))?.StreamableHTTPServerTransport;if(!r)throw new Error("StreamableHTTPServerTransport not found in MCP SDK - ensure @modelcontextprotocol/sdk v1.26.0+ is installed");this.server=new e({name:this.options.name,version:this.options.version},{capabilities:{tools:{}}}),await this.setupRequestHandlers();let o=this.options.port,i=this.options.host;this.transport=new r({sessionIdGenerator:()=>crypto.randomUUID(),enableJsonResponse:true}),await this.server.connect(this.transport);let d=this.transport.handleRequest;if(typeof d!="function")throw new Error("StreamableHTTPServerTransport.handleRequest not found - SDK may have changed");this.httpServer=createServer(async(f,a)=>{let l=f.url?.split("?")[0]??"";if(l!=="/mcp"&&l!=="/mcp/"){a.writeHead(404,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Not Found",path:l}));return}try{await d.call(this.transport,f,a);}catch(p){let g=isRouteCraftError(p)?p.meta.message:p instanceof Error?p.message:"MCP HTTP request error";this.context.logger.error({err:p},g),a.headersSent||(a.writeHead(500,{"Content-Type":"application/json"}),a.end(JSON.stringify({error:"Internal Server Error"})));}}),await new Promise((f,a)=>{this.httpServer.listen(o,i,()=>f()),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),a(l);});});let c=this.getHttpPort()??o;this.context.logger.info({host:i,port:c,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(H)}let n=e,r=n.ListToolsRequestSchema,o=n.CallToolRequestSchema;if(!r||!o)throw new Error("MCP SDK types missing ListToolsRequestSchema or CallToolRequestSchema - ensure @modelcontextprotocol/sdk is installed");let i=this.server;i.setRequestHandler(r,async()=>{let s=this.getAvailableTools();return this.logExposedToolsOnce(),{tools:s}}),i.setRequestHandler(o,async s=>{let c=s.params;return await this.handleToolCall(c.name||"",c.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 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(r=>r.name??"?");this.context.logger.info({tools:n,count:n.length},"Exposing MCP tools"),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 a=new Error("No direct channels available");return this.context.emit("error",{error:a}),{content:[{type:"text",text:"Error: No direct channels available"}]}}let o=r.get(e);if(!o){let a=new Error(`Tool not found: ${e}`);return this.context.emit("error",{error:a}),{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 s=new DefaultExchange(this.context,{body:i,headers:{"routecraft.mcp.tool":e,"routecraft.mcp.session":`mcp-${Date.now()}`}}),c=await o.send(e,s);return {content:[{type:"text",text:typeof c.body=="string"?c.body:JSON.stringify(c.body)}]}}catch(r){let o=isRouteCraftError(r)?r.meta.message:r instanceof Error?r.message:String(r);return this.context.logger.error({tool:e,err:r},o),this.context.emit("error",{error:r}),{content:[{type:"text",text:`Error: ${o}`}]}}}};function Z(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")}}async function ee(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: ${JSON.stringify(r.issues)}`);if(r.value===void 0)throw new Error("mcpPlugin options validation failed: no value returned");return r.value}function te(t={}){Z(t);let e=null;return {async apply(n){if(n.setStore(b,true),t.clients&&Object.keys(t.clients).length>0){let r=new Map(Object.entries(t.clients));n.setStore(h,r);}e=new x(n,t),await e.start();},async teardown(n){if(e){try{await e.stop();}catch(r){n.logger.error(r,"Error stopping MCP server plugin");}e=null;}}}}var U=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 _e(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 R=class{adapterId="routecraft.adapter.agent";runner;constructor(e){_e(e.modelId),this.runner=new U(e);}async send(e){return this.runner.run(e)}};function ne(t){return new R(t)}var j=new Map;async function q(){let t=[...j.entries()];j.clear(),t.length!==0&&await Promise.all(t.map(async([,e])=>{try{typeof e.dispose=="function"&&await e.dispose();}catch{}}));}function Ne(t){return t.includes("/")?t:`Xenova/${t}`}var Ue='The Hugging Face embedding provider requires "@huggingface/transformers". Install it with: pnpm add @huggingface/transformers';function je(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 qe(t){let e=Ne(t),n=j.get(e);if(n)return n.run;let r;try{r=(await import('@huggingface/transformers')).pipeline;}catch(d){throw je(d,"@huggingface/transformers")?new Error(Ue):d}let o=await r("feature-extraction",e,{dtype:"fp32"}),i=(d,c)=>o(d,{pooling:c?.pooling??"mean",normalize:c?.normalize??true}),s={run:i,dispose:typeof o.dispose=="function"?()=>Promise.resolve(o.dispose()):void 0};return j.set(e,s),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 Ge(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 oe(t){let{config:e,modelName:n,text:r}=t;if(e.provider==="mock"){let i=[];for(let s=0;s<8;s++)i.push((r.length+s)%100/100);return i}if(e.provider==="huggingface"){let i=await(await qe(n))(r,{pooling:"mean",normalize:true}),s=i&&typeof i=="object"&&"data"in i?i.data:i;return Ge(s)}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 G=Symbol.for("routecraft.adapter.embedding.providers"),$=Symbol.for("routecraft.adapter.embedding.options");function He(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 Ve(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(G);if(!n)throw new Error("Embedding provider not found: no providers registered. Add embeddingPlugin({ providers: { huggingface: {} } }) to your config.");let{providerId:r,modelName:o}=He(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 Ke(t){return e=>{let n=t(e);return Array.isArray(n)?n.filter(Boolean).join(" | "):n}}var S=class{constructor(e,n={}){this.modelId=e;this.options=n;}adapterId="routecraft.adapter.embedding";options;mergedOptions(e){return {...e.getStore($),...this.options}}async send(e){let n=getExchangeContext(e),{config:r,modelName:o}=Ve(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 d=Ke(i.using)(e);return {embedding:await oe({config:r,modelName:o,text:d})}}};function ie(t,e){return new S(t,e)}function Fe(t,e){return {...e,provider:t}}function se(t={providers:{}}){return {apply(e){let n=new Map;for(let[r,o]of Object.entries(t.providers))o!==void 0&&n.set(r,Fe(r,o));e.setStore(G,n),t.defaultOptions&&Object.keys(t.defaultOptions).length>0&&e.setStore($,t.defaultOptions);},async teardown(){await q();}}}
|
|
2
|
-
export{
|
|
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
|
|
3
3
|
//# sourceMappingURL=index.js.map
|