@radaros/core 0.1.0 → 0.3.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.js CHANGED
@@ -199,14 +199,22 @@ var ToolExecutor = class {
199
199
  const { zodToJsonSchema } = _require("zod-to-json-schema");
200
200
  const defs = [];
201
201
  for (const tool of this.tools.values()) {
202
- const jsonSchema = zodToJsonSchema(tool.parameters, {
203
- target: "openApi3"
204
- });
205
- defs.push({
206
- name: tool.name,
207
- description: tool.description,
208
- parameters: jsonSchema
209
- });
202
+ if (tool.rawJsonSchema) {
203
+ defs.push({
204
+ name: tool.name,
205
+ description: tool.description,
206
+ parameters: tool.rawJsonSchema
207
+ });
208
+ } else {
209
+ const jsonSchema = zodToJsonSchema(tool.parameters, {
210
+ target: "openApi3"
211
+ });
212
+ defs.push({
213
+ name: tool.name,
214
+ description: tool.description,
215
+ parameters: jsonSchema
216
+ });
217
+ }
210
218
  }
211
219
  return defs;
212
220
  }
@@ -2359,6 +2367,291 @@ var OllamaProvider = class {
2359
2367
  }
2360
2368
  };
2361
2369
 
2370
+ // src/models/providers/vertex.ts
2371
+ import { createRequire as createRequire7 } from "module";
2372
+ var _require7 = createRequire7(import.meta.url);
2373
+ var VertexAIProvider = class {
2374
+ providerId = "vertex";
2375
+ modelId;
2376
+ ai = null;
2377
+ GoogleGenAICtor;
2378
+ project;
2379
+ location;
2380
+ constructor(modelId, config) {
2381
+ this.modelId = modelId;
2382
+ this.project = config?.project ?? process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCLOUD_PROJECT ?? "";
2383
+ this.location = config?.location ?? process.env.GOOGLE_CLOUD_LOCATION ?? process.env.GOOGLE_CLOUD_REGION ?? "us-central1";
2384
+ if (!this.project) {
2385
+ throw new Error(
2386
+ "VertexAIProvider: 'project' is required. Pass it in config or set GOOGLE_CLOUD_PROJECT env var."
2387
+ );
2388
+ }
2389
+ try {
2390
+ const { GoogleGenAI } = _require7("@google/genai");
2391
+ this.GoogleGenAICtor = GoogleGenAI;
2392
+ } catch {
2393
+ throw new Error(
2394
+ "@google/genai is required for VertexAIProvider. Install it: npm install @google/genai"
2395
+ );
2396
+ }
2397
+ }
2398
+ getClient() {
2399
+ if (this.ai) return this.ai;
2400
+ this.ai = new this.GoogleGenAICtor({
2401
+ vertexai: true,
2402
+ project: this.project,
2403
+ location: this.location
2404
+ });
2405
+ return this.ai;
2406
+ }
2407
+ async generate(messages, options) {
2408
+ const { systemInstruction, contents } = this.toGoogleMessages(messages);
2409
+ const config = {};
2410
+ if (options?.temperature !== void 0)
2411
+ config.temperature = options.temperature;
2412
+ if (options?.maxTokens !== void 0)
2413
+ config.maxOutputTokens = options.maxTokens;
2414
+ if (options?.topP !== void 0) config.topP = options.topP;
2415
+ if (options?.stop) config.stopSequences = options.stop;
2416
+ if (options?.responseFormat) {
2417
+ config.responseMimeType = "application/json";
2418
+ const rf = options.responseFormat;
2419
+ if (typeof rf === "object" && rf !== null && "type" in rf && rf.type === "json_schema" && "schema" in rf && rf.schema) {
2420
+ config.responseSchema = this.cleanJsonSchema(
2421
+ rf.schema
2422
+ );
2423
+ }
2424
+ }
2425
+ const params = {
2426
+ model: this.modelId,
2427
+ contents,
2428
+ config
2429
+ };
2430
+ if (systemInstruction) params.systemInstruction = systemInstruction;
2431
+ if (options?.tools?.length) {
2432
+ params.tools = [
2433
+ { functionDeclarations: this.toGoogleTools(options.tools) }
2434
+ ];
2435
+ }
2436
+ const client = this.getClient();
2437
+ const response = await client.models.generateContent(params);
2438
+ return this.normalizeResponse(response);
2439
+ }
2440
+ async *stream(messages, options) {
2441
+ const { systemInstruction, contents } = this.toGoogleMessages(messages);
2442
+ const config = {};
2443
+ if (options?.temperature !== void 0)
2444
+ config.temperature = options.temperature;
2445
+ if (options?.maxTokens !== void 0)
2446
+ config.maxOutputTokens = options.maxTokens;
2447
+ if (options?.topP !== void 0) config.topP = options.topP;
2448
+ if (options?.stop) config.stopSequences = options.stop;
2449
+ const params = {
2450
+ model: this.modelId,
2451
+ contents,
2452
+ config
2453
+ };
2454
+ if (systemInstruction) params.systemInstruction = systemInstruction;
2455
+ if (options?.tools?.length) {
2456
+ params.tools = [
2457
+ { functionDeclarations: this.toGoogleTools(options.tools) }
2458
+ ];
2459
+ }
2460
+ const client = this.getClient();
2461
+ const streamResult = await client.models.generateContentStream(params);
2462
+ let toolCallCounter = 0;
2463
+ for await (const chunk of streamResult) {
2464
+ const candidate = chunk.candidates?.[0];
2465
+ if (!candidate?.content?.parts) continue;
2466
+ for (const part of candidate.content.parts) {
2467
+ if (part.text) {
2468
+ yield { type: "text", text: part.text };
2469
+ }
2470
+ if (part.functionCall) {
2471
+ const id = `vertex_tc_${toolCallCounter++}`;
2472
+ yield {
2473
+ type: "tool_call_start",
2474
+ toolCall: { id, name: part.functionCall.name }
2475
+ };
2476
+ yield {
2477
+ type: "tool_call_delta",
2478
+ toolCallId: id,
2479
+ argumentsDelta: JSON.stringify(part.functionCall.args ?? {})
2480
+ };
2481
+ yield { type: "tool_call_end", toolCallId: id };
2482
+ }
2483
+ }
2484
+ if (candidate.finishReason) {
2485
+ let finishReason = "stop";
2486
+ if (candidate.finishReason === "STOP" || candidate.finishReason === "END_TURN")
2487
+ finishReason = "stop";
2488
+ else if (candidate.finishReason === "MAX_TOKENS")
2489
+ finishReason = "length";
2490
+ else if (candidate.finishReason === "SAFETY")
2491
+ finishReason = "content_filter";
2492
+ const hasToolCalls = candidate.content?.parts?.some(
2493
+ (p) => p.functionCall
2494
+ );
2495
+ if (hasToolCalls) finishReason = "tool_calls";
2496
+ yield {
2497
+ type: "finish",
2498
+ finishReason,
2499
+ usage: chunk.usageMetadata ? {
2500
+ promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
2501
+ completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
2502
+ totalTokens: chunk.usageMetadata.totalTokenCount ?? 0
2503
+ } : void 0
2504
+ };
2505
+ }
2506
+ }
2507
+ }
2508
+ // ── Message conversion (identical to GoogleProvider) ─────────────────────
2509
+ toGoogleMessages(messages) {
2510
+ let systemInstruction;
2511
+ const contents = [];
2512
+ for (const msg of messages) {
2513
+ if (msg.role === "system") {
2514
+ systemInstruction = getTextContent(msg.content) || void 0;
2515
+ continue;
2516
+ }
2517
+ if (msg.role === "user") {
2518
+ if (isMultiModal(msg.content)) {
2519
+ contents.push({
2520
+ role: "user",
2521
+ parts: msg.content.map((p) => this.partToGoogle(p))
2522
+ });
2523
+ } else {
2524
+ contents.push({
2525
+ role: "user",
2526
+ parts: [{ text: msg.content ?? "" }]
2527
+ });
2528
+ }
2529
+ continue;
2530
+ }
2531
+ if (msg.role === "assistant") {
2532
+ const parts = [];
2533
+ if (msg.content) parts.push({ text: msg.content });
2534
+ if (msg.toolCalls) {
2535
+ for (const tc of msg.toolCalls) {
2536
+ parts.push({
2537
+ functionCall: { name: tc.name, args: tc.arguments }
2538
+ });
2539
+ }
2540
+ }
2541
+ if (parts.length === 0) parts.push({ text: "" });
2542
+ contents.push({ role: "model", parts });
2543
+ continue;
2544
+ }
2545
+ if (msg.role === "tool") {
2546
+ contents.push({
2547
+ role: "function",
2548
+ parts: [
2549
+ {
2550
+ functionResponse: {
2551
+ name: msg.name ?? "unknown",
2552
+ response: { result: msg.content ?? "" }
2553
+ }
2554
+ }
2555
+ ]
2556
+ });
2557
+ continue;
2558
+ }
2559
+ }
2560
+ return { systemInstruction, contents };
2561
+ }
2562
+ partToGoogle(part) {
2563
+ switch (part.type) {
2564
+ case "text":
2565
+ return { text: part.text };
2566
+ case "image":
2567
+ case "audio":
2568
+ case "file": {
2569
+ const isUrl = part.data.startsWith("http://") || part.data.startsWith("https://");
2570
+ if (isUrl) {
2571
+ return {
2572
+ fileData: {
2573
+ fileUri: part.data,
2574
+ mimeType: part.mimeType ?? (part.type === "image" ? "image/png" : "application/octet-stream")
2575
+ }
2576
+ };
2577
+ }
2578
+ return {
2579
+ inlineData: {
2580
+ data: part.data,
2581
+ mimeType: part.mimeType ?? (part.type === "image" ? "image/png" : part.type === "audio" ? "audio/mp3" : "application/octet-stream")
2582
+ }
2583
+ };
2584
+ }
2585
+ }
2586
+ }
2587
+ toGoogleTools(tools) {
2588
+ return tools.map((t) => ({
2589
+ name: t.name,
2590
+ description: t.description,
2591
+ parameters: t.parameters
2592
+ }));
2593
+ }
2594
+ cleanJsonSchema(schema) {
2595
+ const cleaned = { ...schema };
2596
+ delete cleaned["$schema"];
2597
+ delete cleaned["$ref"];
2598
+ delete cleaned["additionalProperties"];
2599
+ if (cleaned.properties && typeof cleaned.properties === "object") {
2600
+ const props = {};
2601
+ for (const [key, val] of Object.entries(
2602
+ cleaned.properties
2603
+ )) {
2604
+ props[key] = typeof val === "object" && val ? this.cleanJsonSchema(val) : val;
2605
+ }
2606
+ cleaned.properties = props;
2607
+ }
2608
+ if (cleaned.items && typeof cleaned.items === "object") {
2609
+ cleaned.items = this.cleanJsonSchema(
2610
+ cleaned.items
2611
+ );
2612
+ }
2613
+ return cleaned;
2614
+ }
2615
+ normalizeResponse(response) {
2616
+ const candidate = response.candidates?.[0];
2617
+ const parts = candidate?.content?.parts ?? [];
2618
+ let textContent = "";
2619
+ const toolCalls = [];
2620
+ let toolCallCounter = 0;
2621
+ for (const part of parts) {
2622
+ if (part.text) textContent += part.text;
2623
+ if (part.functionCall) {
2624
+ toolCalls.push({
2625
+ id: `vertex_tc_${toolCallCounter++}`,
2626
+ name: part.functionCall.name,
2627
+ arguments: part.functionCall.args ?? {}
2628
+ });
2629
+ }
2630
+ }
2631
+ const usage = {
2632
+ promptTokens: response.usageMetadata?.promptTokenCount ?? 0,
2633
+ completionTokens: response.usageMetadata?.candidatesTokenCount ?? 0,
2634
+ totalTokens: response.usageMetadata?.totalTokenCount ?? 0
2635
+ };
2636
+ let finishReason = "stop";
2637
+ if (toolCalls.length > 0) finishReason = "tool_calls";
2638
+ else if (candidate?.finishReason === "MAX_TOKENS")
2639
+ finishReason = "length";
2640
+ else if (candidate?.finishReason === "SAFETY")
2641
+ finishReason = "content_filter";
2642
+ return {
2643
+ message: {
2644
+ role: "assistant",
2645
+ content: textContent || null,
2646
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0
2647
+ },
2648
+ usage,
2649
+ finishReason,
2650
+ raw: response
2651
+ };
2652
+ }
2653
+ };
2654
+
2362
2655
  // src/models/registry.ts
2363
2656
  var ModelRegistry = class {
2364
2657
  factories = /* @__PURE__ */ new Map();
@@ -2407,6 +2700,16 @@ function google(modelId, config) {
2407
2700
  function ollama(modelId, config) {
2408
2701
  return registry.resolve("ollama", modelId, config);
2409
2702
  }
2703
+ registry.register(
2704
+ "vertex",
2705
+ (modelId, config) => new VertexAIProvider(
2706
+ modelId,
2707
+ config
2708
+ )
2709
+ );
2710
+ function vertex(modelId, config) {
2711
+ return registry.resolve("vertex", modelId, config);
2712
+ }
2410
2713
 
2411
2714
  // src/tools/define-tool.ts
2412
2715
  function defineTool(config) {
@@ -2419,13 +2722,13 @@ function defineTool(config) {
2419
2722
  }
2420
2723
 
2421
2724
  // src/storage/sqlite.ts
2422
- import { createRequire as createRequire7 } from "module";
2423
- var _require7 = createRequire7(import.meta.url);
2725
+ import { createRequire as createRequire8 } from "module";
2726
+ var _require8 = createRequire8(import.meta.url);
2424
2727
  var SqliteStorage = class {
2425
2728
  db;
2426
2729
  constructor(dbPath) {
2427
2730
  try {
2428
- const Database = _require7("better-sqlite3");
2731
+ const Database = _require8("better-sqlite3");
2429
2732
  this.db = new Database(dbPath);
2430
2733
  this.db.pragma("journal_mode = WAL");
2431
2734
  this.db.exec(`
@@ -2474,13 +2777,13 @@ var SqliteStorage = class {
2474
2777
  };
2475
2778
 
2476
2779
  // src/storage/postgres.ts
2477
- import { createRequire as createRequire8 } from "module";
2478
- var _require8 = createRequire8(import.meta.url);
2780
+ import { createRequire as createRequire9 } from "module";
2781
+ var _require9 = createRequire9(import.meta.url);
2479
2782
  var PostgresStorage = class {
2480
2783
  pool;
2481
2784
  constructor(connectionString) {
2482
2785
  try {
2483
- const { Pool } = _require8("pg");
2786
+ const { Pool } = _require9("pg");
2484
2787
  this.pool = new Pool({ connectionString });
2485
2788
  } catch {
2486
2789
  throw new Error(
@@ -2541,15 +2844,15 @@ var PostgresStorage = class {
2541
2844
  };
2542
2845
 
2543
2846
  // src/storage/mongodb.ts
2544
- import { createRequire as createRequire9 } from "module";
2545
- var _require9 = createRequire9(import.meta.url);
2847
+ import { createRequire as createRequire10 } from "module";
2848
+ var _require10 = createRequire10(import.meta.url);
2546
2849
  var MongoDBStorage = class {
2547
2850
  constructor(uri, dbName = "radaros", collectionName = "kv_store") {
2548
2851
  this.uri = uri;
2549
2852
  this.dbName = dbName;
2550
2853
  this.collectionName = collectionName;
2551
2854
  try {
2552
- const { MongoClient } = _require9("mongodb");
2855
+ const { MongoClient } = _require10("mongodb");
2553
2856
  this.client = new MongoClient(uri);
2554
2857
  } catch {
2555
2858
  throw new Error(
@@ -2709,8 +3012,8 @@ var InMemoryVectorStore = class extends BaseVectorStore {
2709
3012
  };
2710
3013
 
2711
3014
  // src/vector/pgvector.ts
2712
- import { createRequire as createRequire10 } from "module";
2713
- var _require10 = createRequire10(import.meta.url);
3015
+ import { createRequire as createRequire11 } from "module";
3016
+ var _require11 = createRequire11(import.meta.url);
2714
3017
  var PgVectorStore = class extends BaseVectorStore {
2715
3018
  pool;
2716
3019
  dimensions;
@@ -2719,7 +3022,7 @@ var PgVectorStore = class extends BaseVectorStore {
2719
3022
  super(embedder);
2720
3023
  this.dimensions = config.dimensions ?? embedder?.dimensions ?? 1536;
2721
3024
  try {
2722
- const { Pool } = _require10("pg");
3025
+ const { Pool } = _require11("pg");
2723
3026
  this.pool = new Pool({ connectionString: config.connectionString });
2724
3027
  } catch {
2725
3028
  throw new Error(
@@ -2838,9 +3141,9 @@ var PgVectorStore = class extends BaseVectorStore {
2838
3141
  };
2839
3142
 
2840
3143
  // src/vector/qdrant.ts
2841
- import { createRequire as createRequire11 } from "module";
3144
+ import { createRequire as createRequire12 } from "module";
2842
3145
  import { createHash } from "crypto";
2843
- var _require11 = createRequire11(import.meta.url);
3146
+ var _require12 = createRequire12(import.meta.url);
2844
3147
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2845
3148
  function stringToUUID(str) {
2846
3149
  const hex = createHash("md5").update(str).digest("hex");
@@ -2865,7 +3168,7 @@ var QdrantVectorStore = class extends BaseVectorStore {
2865
3168
  super(embedder);
2866
3169
  this.dimensions = config.dimensions ?? embedder?.dimensions ?? 1536;
2867
3170
  try {
2868
- const { QdrantClient } = _require11("@qdrant/js-client-rest");
3171
+ const { QdrantClient } = _require12("@qdrant/js-client-rest");
2869
3172
  this.client = new QdrantClient({
2870
3173
  url: config.url ?? "http://localhost:6333",
2871
3174
  apiKey: config.apiKey,
@@ -2998,8 +3301,8 @@ var QdrantVectorStore = class extends BaseVectorStore {
2998
3301
  };
2999
3302
 
3000
3303
  // src/vector/mongodb.ts
3001
- import { createRequire as createRequire12 } from "module";
3002
- var _require12 = createRequire12(import.meta.url);
3304
+ import { createRequire as createRequire13 } from "module";
3305
+ var _require13 = createRequire13(import.meta.url);
3003
3306
  var MongoDBVectorStore = class extends BaseVectorStore {
3004
3307
  client;
3005
3308
  db;
@@ -3011,7 +3314,7 @@ var MongoDBVectorStore = class extends BaseVectorStore {
3011
3314
  this.indexName = config.indexName ?? "vector_index";
3012
3315
  this.dbName = config.dbName ?? "radaros_vectors";
3013
3316
  try {
3014
- const { MongoClient } = _require12("mongodb");
3317
+ const { MongoClient } = _require13("mongodb");
3015
3318
  this.client = new MongoClient(config.uri);
3016
3319
  } catch {
3017
3320
  throw new Error(
@@ -3182,8 +3485,8 @@ function cosine(a, b) {
3182
3485
  }
3183
3486
 
3184
3487
  // src/vector/embeddings/openai.ts
3185
- import { createRequire as createRequire13 } from "module";
3186
- var _require13 = createRequire13(import.meta.url);
3488
+ import { createRequire as createRequire14 } from "module";
3489
+ var _require14 = createRequire14(import.meta.url);
3187
3490
  var MODEL_DIMENSIONS = {
3188
3491
  "text-embedding-3-small": 1536,
3189
3492
  "text-embedding-3-large": 3072,
@@ -3197,7 +3500,7 @@ var OpenAIEmbedding = class {
3197
3500
  this.model = config.model ?? "text-embedding-3-small";
3198
3501
  this.dimensions = config.dimensions ?? MODEL_DIMENSIONS[this.model] ?? 1536;
3199
3502
  try {
3200
- const mod = _require13("openai");
3503
+ const mod = _require14("openai");
3201
3504
  const OpenAI = mod.default ?? mod;
3202
3505
  this.client = new OpenAI({
3203
3506
  apiKey: config.apiKey ?? process.env.OPENAI_API_KEY,
@@ -3228,8 +3531,8 @@ var OpenAIEmbedding = class {
3228
3531
  };
3229
3532
 
3230
3533
  // src/vector/embeddings/google.ts
3231
- import { createRequire as createRequire14 } from "module";
3232
- var _require14 = createRequire14(import.meta.url);
3534
+ import { createRequire as createRequire15 } from "module";
3535
+ var _require15 = createRequire15(import.meta.url);
3233
3536
  var MODEL_DIMENSIONS2 = {
3234
3537
  "text-embedding-004": 768,
3235
3538
  "embedding-001": 768
@@ -3242,7 +3545,7 @@ var GoogleEmbedding = class {
3242
3545
  this.model = config.model ?? "text-embedding-004";
3243
3546
  this.dimensions = config.dimensions ?? MODEL_DIMENSIONS2[this.model] ?? 768;
3244
3547
  try {
3245
- const { GoogleGenAI } = _require14("@google/genai");
3548
+ const { GoogleGenAI } = _require15("@google/genai");
3246
3549
  this.ai = new GoogleGenAI({
3247
3550
  apiKey: config.apiKey ?? process.env.GOOGLE_API_KEY
3248
3551
  });
@@ -3422,7 +3725,391 @@ ${summaries.join("\n")}`;
3422
3725
  await this.storage.delete(SHORT_TERM_NS, sessionId);
3423
3726
  }
3424
3727
  };
3728
+
3729
+ // src/mcp/mcp-client.ts
3730
+ var MCPToolProvider = class {
3731
+ name;
3732
+ config;
3733
+ client = null;
3734
+ transportInstance = null;
3735
+ tools = [];
3736
+ connected = false;
3737
+ constructor(config) {
3738
+ this.name = config.name;
3739
+ this.config = config;
3740
+ }
3741
+ async connect() {
3742
+ if (this.connected) return;
3743
+ let ClientClass;
3744
+ try {
3745
+ const mod = await import("@modelcontextprotocol/sdk/client/index.js");
3746
+ ClientClass = mod.Client;
3747
+ } catch {
3748
+ throw new Error(
3749
+ "@modelcontextprotocol/sdk is required for MCPToolProvider. Install it: npm install @modelcontextprotocol/sdk"
3750
+ );
3751
+ }
3752
+ this.client = new ClientClass(
3753
+ { name: `radaros-${this.name}`, version: "1.0.0" },
3754
+ { capabilities: {} }
3755
+ );
3756
+ if (this.config.transport === "stdio") {
3757
+ if (!this.config.command) {
3758
+ throw new Error("MCPToolProvider: 'command' is required for stdio transport");
3759
+ }
3760
+ const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
3761
+ this.transportInstance = new StdioClientTransport({
3762
+ command: this.config.command,
3763
+ args: this.config.args ?? [],
3764
+ env: { ...process.env, ...this.config.env ?? {} }
3765
+ });
3766
+ } else if (this.config.transport === "http") {
3767
+ if (!this.config.url) {
3768
+ throw new Error("MCPToolProvider: 'url' is required for http transport");
3769
+ }
3770
+ let TransportClass;
3771
+ try {
3772
+ const mod = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
3773
+ TransportClass = mod.StreamableHTTPClientTransport;
3774
+ } catch {
3775
+ const mod = await import("@modelcontextprotocol/sdk/client/sse.js");
3776
+ TransportClass = mod.SSEClientTransport;
3777
+ }
3778
+ this.transportInstance = new TransportClass(
3779
+ new URL(this.config.url),
3780
+ { requestInit: { headers: this.config.headers ?? {} } }
3781
+ );
3782
+ } else {
3783
+ throw new Error(`MCPToolProvider: unsupported transport '${this.config.transport}'`);
3784
+ }
3785
+ await this.client.connect(this.transportInstance);
3786
+ this.connected = true;
3787
+ await this.discoverTools();
3788
+ }
3789
+ async discoverTools() {
3790
+ const { z: z3 } = await import("zod");
3791
+ const result = await this.client.listTools();
3792
+ const mcpTools = result.tools ?? [];
3793
+ this.tools = mcpTools.map((mcpTool) => {
3794
+ const toolName = mcpTool.name;
3795
+ const description = mcpTool.description ?? "";
3796
+ const inputSchema = mcpTool.inputSchema ?? { type: "object", properties: {} };
3797
+ const parameters = this.jsonSchemaToZod(inputSchema, z3);
3798
+ const execute = async (args, _ctx) => {
3799
+ const callResult = await this.client.callTool({
3800
+ name: toolName,
3801
+ arguments: args
3802
+ });
3803
+ const contents = callResult.content ?? [];
3804
+ const textParts = contents.filter((c) => c.type === "text").map((c) => c.text);
3805
+ const text = textParts.join("\n") || JSON.stringify(callResult);
3806
+ const artifacts = contents.filter((c) => c.type !== "text").map((c) => ({
3807
+ type: c.type,
3808
+ data: c.data ?? c.blob ?? c.text,
3809
+ mimeType: c.mimeType
3810
+ }));
3811
+ if (artifacts.length > 0) {
3812
+ return { content: text, artifacts };
3813
+ }
3814
+ return text;
3815
+ };
3816
+ return {
3817
+ name: `${this.name}__${toolName}`,
3818
+ description: `[${this.name}] ${description}`,
3819
+ parameters,
3820
+ execute,
3821
+ rawJsonSchema: inputSchema
3822
+ };
3823
+ });
3824
+ }
3825
+ jsonSchemaToZod(schema, z3) {
3826
+ if (!schema || !schema.properties) {
3827
+ return z3.object({}).passthrough();
3828
+ }
3829
+ const shape = {};
3830
+ const required = schema.required ?? [];
3831
+ for (const [key, prop] of Object.entries(schema.properties)) {
3832
+ let field;
3833
+ switch (prop.type) {
3834
+ case "string":
3835
+ field = z3.string();
3836
+ if (prop.enum) field = z3.enum(prop.enum);
3837
+ break;
3838
+ case "number":
3839
+ case "integer":
3840
+ field = z3.number();
3841
+ break;
3842
+ case "boolean":
3843
+ field = z3.boolean();
3844
+ break;
3845
+ case "array":
3846
+ field = z3.array(z3.any());
3847
+ break;
3848
+ case "object":
3849
+ field = z3.record(z3.any());
3850
+ break;
3851
+ default:
3852
+ field = z3.any();
3853
+ }
3854
+ if (prop.description) {
3855
+ field = field.describe(prop.description);
3856
+ }
3857
+ if (!required.includes(key)) {
3858
+ field = field.optional();
3859
+ }
3860
+ shape[key] = field;
3861
+ }
3862
+ return z3.object(shape);
3863
+ }
3864
+ /**
3865
+ * Returns tools from this MCP server as RadarOS ToolDef[].
3866
+ * Optionally filter by tool names to reduce token usage.
3867
+ *
3868
+ * @param filter - Tool names to include (without the server name prefix).
3869
+ * If omitted, returns all tools.
3870
+ *
3871
+ * @example
3872
+ * // All tools
3873
+ * await mcp.getTools()
3874
+ *
3875
+ * // Only specific tools (pass the original MCP tool names, not prefixed)
3876
+ * await mcp.getTools({ include: ["get_latest_release", "search_repositories"] })
3877
+ *
3878
+ * // Exclude specific tools
3879
+ * await mcp.getTools({ exclude: ["push_files", "create_repository"] })
3880
+ */
3881
+ async getTools(filter) {
3882
+ if (!this.connected) {
3883
+ await this.connect();
3884
+ }
3885
+ if (!filter) {
3886
+ return [...this.tools];
3887
+ }
3888
+ const prefix = `${this.name}__`;
3889
+ return this.tools.filter((tool) => {
3890
+ const shortName = tool.name.startsWith(prefix) ? tool.name.slice(prefix.length) : tool.name;
3891
+ if (filter.include) {
3892
+ return filter.include.includes(shortName);
3893
+ }
3894
+ if (filter.exclude) {
3895
+ return !filter.exclude.includes(shortName);
3896
+ }
3897
+ return true;
3898
+ });
3899
+ }
3900
+ /** Refresh the tool list from the MCP server. */
3901
+ async refresh() {
3902
+ if (!this.connected) {
3903
+ throw new Error("MCPToolProvider: not connected. Call connect() first.");
3904
+ }
3905
+ await this.discoverTools();
3906
+ }
3907
+ /** Disconnect from the MCP server. */
3908
+ async close() {
3909
+ if (this.client && this.connected) {
3910
+ try {
3911
+ await this.client.close();
3912
+ } catch {
3913
+ }
3914
+ this.connected = false;
3915
+ this.tools = [];
3916
+ }
3917
+ }
3918
+ };
3919
+
3920
+ // src/a2a/a2a-remote-agent.ts
3921
+ import { z as z2 } from "zod";
3922
+ var A2ARemoteAgent = class {
3923
+ url;
3924
+ name;
3925
+ instructions;
3926
+ skills = [];
3927
+ headers;
3928
+ timeoutMs;
3929
+ card = null;
3930
+ rpcId = 0;
3931
+ get tools() {
3932
+ return [];
3933
+ }
3934
+ get modelId() {
3935
+ return "a2a-remote";
3936
+ }
3937
+ get providerId() {
3938
+ return "a2a";
3939
+ }
3940
+ get hasStructuredOutput() {
3941
+ return false;
3942
+ }
3943
+ constructor(config) {
3944
+ this.url = config.url.replace(/\/$/, "");
3945
+ this.name = config.name ?? "remote-agent";
3946
+ this.instructions = "";
3947
+ this.headers = config.headers ?? {};
3948
+ this.timeoutMs = config.timeoutMs ?? 6e4;
3949
+ }
3950
+ /**
3951
+ * Fetch the Agent Card from /.well-known/agent.json and populate metadata.
3952
+ */
3953
+ async discover() {
3954
+ const res = await fetch(`${this.url}/.well-known/agent.json`, {
3955
+ headers: this.headers,
3956
+ signal: AbortSignal.timeout(this.timeoutMs)
3957
+ });
3958
+ if (!res.ok) {
3959
+ throw new Error(
3960
+ `A2A discover failed: ${res.status} ${res.statusText} from ${this.url}`
3961
+ );
3962
+ }
3963
+ this.card = await res.json();
3964
+ this.name = this.card.name;
3965
+ this.instructions = this.card.description ?? "";
3966
+ this.skills = this.card.skills?.map((s) => ({
3967
+ id: s.id,
3968
+ name: s.name,
3969
+ description: s.description
3970
+ })) ?? [];
3971
+ return this.card;
3972
+ }
3973
+ /**
3974
+ * Synchronous run: sends message/send and returns RunOutput.
3975
+ */
3976
+ async run(input, opts) {
3977
+ const message = {
3978
+ role: "user",
3979
+ parts: [{ kind: "text", text: input }]
3980
+ };
3981
+ const rpcReq = {
3982
+ jsonrpc: "2.0",
3983
+ id: ++this.rpcId,
3984
+ method: "message/send",
3985
+ params: {
3986
+ message,
3987
+ ...opts?.sessionId ? { sessionId: opts.sessionId } : {}
3988
+ }
3989
+ };
3990
+ const startMs = Date.now();
3991
+ const res = await fetch(this.url, {
3992
+ method: "POST",
3993
+ headers: {
3994
+ "Content-Type": "application/json",
3995
+ ...this.headers
3996
+ },
3997
+ body: JSON.stringify(rpcReq),
3998
+ signal: AbortSignal.timeout(this.timeoutMs)
3999
+ });
4000
+ if (!res.ok) {
4001
+ throw new Error(`A2A message/send failed: ${res.status} ${res.statusText}`);
4002
+ }
4003
+ const rpcRes = await res.json();
4004
+ if (rpcRes.error) {
4005
+ throw new Error(`A2A error: ${rpcRes.error.message}`);
4006
+ }
4007
+ const task = rpcRes.result;
4008
+ const agentMsg = task.status?.message;
4009
+ const text = agentMsg ? this.partsToText(agentMsg.parts) : "";
4010
+ return {
4011
+ text,
4012
+ toolCalls: [],
4013
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
4014
+ durationMs: Date.now() - startMs
4015
+ };
4016
+ }
4017
+ /**
4018
+ * Streaming run: sends message/stream and yields StreamChunks from SSE.
4019
+ */
4020
+ async *stream(input, opts) {
4021
+ const message = {
4022
+ role: "user",
4023
+ parts: [{ kind: "text", text: input }]
4024
+ };
4025
+ const rpcReq = {
4026
+ jsonrpc: "2.0",
4027
+ id: ++this.rpcId,
4028
+ method: "message/stream",
4029
+ params: {
4030
+ message,
4031
+ ...opts?.sessionId ? { sessionId: opts.sessionId } : {}
4032
+ }
4033
+ };
4034
+ const res = await fetch(this.url, {
4035
+ method: "POST",
4036
+ headers: {
4037
+ "Content-Type": "application/json",
4038
+ ...this.headers
4039
+ },
4040
+ body: JSON.stringify(rpcReq),
4041
+ signal: AbortSignal.timeout(this.timeoutMs)
4042
+ });
4043
+ if (!res.ok) {
4044
+ throw new Error(`A2A message/stream failed: ${res.status} ${res.statusText}`);
4045
+ }
4046
+ if (!res.body) {
4047
+ throw new Error("A2A message/stream: no response body for SSE");
4048
+ }
4049
+ const reader = res.body.getReader();
4050
+ const decoder = new TextDecoder();
4051
+ let buffer = "";
4052
+ while (true) {
4053
+ const { done, value } = await reader.read();
4054
+ if (done) break;
4055
+ buffer += decoder.decode(value, { stream: true });
4056
+ const lines = buffer.split("\n");
4057
+ buffer = lines.pop();
4058
+ for (const line of lines) {
4059
+ if (!line.startsWith("data: ")) continue;
4060
+ const jsonStr = line.slice(6).trim();
4061
+ if (!jsonStr) continue;
4062
+ try {
4063
+ const event = JSON.parse(jsonStr);
4064
+ const task = event.result;
4065
+ if (!task) continue;
4066
+ if (task.status?.state === "working" && task.status.message?.parts?.length) {
4067
+ for (const part of task.status.message.parts) {
4068
+ if (part.kind === "text") {
4069
+ yield { type: "text", text: part.text };
4070
+ }
4071
+ }
4072
+ }
4073
+ if (task.status?.state === "completed") {
4074
+ yield {
4075
+ type: "finish",
4076
+ finishReason: "stop",
4077
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
4078
+ };
4079
+ }
4080
+ } catch {
4081
+ }
4082
+ }
4083
+ }
4084
+ }
4085
+ /**
4086
+ * Wrap this remote agent as a ToolDef so it can be used by an orchestrator agent.
4087
+ */
4088
+ asTool() {
4089
+ const self = this;
4090
+ return {
4091
+ name: `a2a_${this.name.replace(/[^a-zA-Z0-9_]/g, "_")}`,
4092
+ description: this.instructions || `Remote A2A agent: ${this.name}`,
4093
+ parameters: z2.object({
4094
+ message: z2.string().describe("The message to send to the remote agent")
4095
+ }),
4096
+ async execute(args, _ctx) {
4097
+ const result = await self.run(args.message);
4098
+ return result.text;
4099
+ }
4100
+ };
4101
+ }
4102
+ getAgentCard() {
4103
+ return this.card;
4104
+ }
4105
+ partsToText(parts) {
4106
+ return parts.filter(
4107
+ (p) => p.kind === "text"
4108
+ ).map((p) => p.text).join("\n");
4109
+ }
4110
+ };
3425
4111
  export {
4112
+ A2ARemoteAgent,
3426
4113
  Agent,
3427
4114
  AnthropicProvider,
3428
4115
  BaseVectorStore,
@@ -3434,6 +4121,7 @@ export {
3434
4121
  KnowledgeBase,
3435
4122
  LLMLoop,
3436
4123
  Logger,
4124
+ MCPToolProvider,
3437
4125
  Memory,
3438
4126
  ModelRegistry,
3439
4127
  MongoDBStorage,
@@ -3450,6 +4138,7 @@ export {
3450
4138
  Team,
3451
4139
  TeamMode,
3452
4140
  ToolExecutor,
4141
+ VertexAIProvider,
3453
4142
  Workflow,
3454
4143
  anthropic,
3455
4144
  defineTool,
@@ -3458,5 +4147,6 @@ export {
3458
4147
  isMultiModal,
3459
4148
  ollama,
3460
4149
  openai,
3461
- registry
4150
+ registry,
4151
+ vertex
3462
4152
  };