@radaros/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +224 -1
- package/dist/index.js +401 -8
- package/package.json +6 -2
- package/src/a2a/a2a-remote-agent.ts +270 -0
- package/src/a2a/types.ts +142 -0
- package/src/index.ts +24 -0
- package/src/mcp/mcp-client.ts +264 -0
- package/src/tools/tool-executor.ts +17 -9
- package/src/tools/types.ts +2 -0
package/dist/index.d.ts
CHANGED
|
@@ -138,6 +138,8 @@ interface ToolDef {
|
|
|
138
138
|
description: string;
|
|
139
139
|
parameters: z.ZodObject<any>;
|
|
140
140
|
execute: (args: Record<string, unknown>, ctx: RunContext) => Promise<string | ToolResult>;
|
|
141
|
+
/** Raw JSON Schema to send to the LLM, bypassing Zod-to-JSON conversion (used by MCP tools). */
|
|
142
|
+
rawJsonSchema?: Record<string, unknown>;
|
|
141
143
|
}
|
|
142
144
|
interface ToolCallResult {
|
|
143
145
|
toolCallId: string;
|
|
@@ -884,4 +886,225 @@ declare class SessionManager {
|
|
|
884
886
|
deleteSession(sessionId: string): Promise<void>;
|
|
885
887
|
}
|
|
886
888
|
|
|
887
|
-
|
|
889
|
+
interface MCPToolProviderConfig {
|
|
890
|
+
name: string;
|
|
891
|
+
transport: "stdio" | "http";
|
|
892
|
+
/** For stdio transport: command to spawn */
|
|
893
|
+
command?: string;
|
|
894
|
+
/** For stdio transport: args for the command */
|
|
895
|
+
args?: string[];
|
|
896
|
+
/** For stdio transport: environment variables */
|
|
897
|
+
env?: Record<string, string>;
|
|
898
|
+
/** For http transport: server URL */
|
|
899
|
+
url?: string;
|
|
900
|
+
/** For http transport: custom headers */
|
|
901
|
+
headers?: Record<string, string>;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Connects to an MCP (Model Context Protocol) server and exposes its tools
|
|
905
|
+
* as native RadarOS ToolDef[] that any Agent can use.
|
|
906
|
+
*
|
|
907
|
+
* Supports stdio and HTTP (Streamable HTTP) transports.
|
|
908
|
+
* Requires: npm install @modelcontextprotocol/sdk
|
|
909
|
+
*/
|
|
910
|
+
declare class MCPToolProvider {
|
|
911
|
+
readonly name: string;
|
|
912
|
+
private config;
|
|
913
|
+
private client;
|
|
914
|
+
private transportInstance;
|
|
915
|
+
private tools;
|
|
916
|
+
private connected;
|
|
917
|
+
constructor(config: MCPToolProviderConfig);
|
|
918
|
+
connect(): Promise<void>;
|
|
919
|
+
private discoverTools;
|
|
920
|
+
private jsonSchemaToZod;
|
|
921
|
+
/**
|
|
922
|
+
* Returns tools from this MCP server as RadarOS ToolDef[].
|
|
923
|
+
* Optionally filter by tool names to reduce token usage.
|
|
924
|
+
*
|
|
925
|
+
* @param filter - Tool names to include (without the server name prefix).
|
|
926
|
+
* If omitted, returns all tools.
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* // All tools
|
|
930
|
+
* await mcp.getTools()
|
|
931
|
+
*
|
|
932
|
+
* // Only specific tools (pass the original MCP tool names, not prefixed)
|
|
933
|
+
* await mcp.getTools({ include: ["get_latest_release", "search_repositories"] })
|
|
934
|
+
*
|
|
935
|
+
* // Exclude specific tools
|
|
936
|
+
* await mcp.getTools({ exclude: ["push_files", "create_repository"] })
|
|
937
|
+
*/
|
|
938
|
+
getTools(filter?: {
|
|
939
|
+
include?: string[];
|
|
940
|
+
exclude?: string[];
|
|
941
|
+
}): Promise<ToolDef[]>;
|
|
942
|
+
/** Refresh the tool list from the MCP server. */
|
|
943
|
+
refresh(): Promise<void>;
|
|
944
|
+
/** Disconnect from the MCP server. */
|
|
945
|
+
close(): Promise<void>;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* A2A (Agent-to-Agent) Protocol types.
|
|
950
|
+
* Based on the A2A specification v0.2.
|
|
951
|
+
* https://google.github.io/A2A/specification/
|
|
952
|
+
*/
|
|
953
|
+
interface A2ATextPart {
|
|
954
|
+
kind: "text";
|
|
955
|
+
text: string;
|
|
956
|
+
}
|
|
957
|
+
interface A2AFilePart {
|
|
958
|
+
kind: "file";
|
|
959
|
+
file: {
|
|
960
|
+
name?: string;
|
|
961
|
+
mimeType?: string;
|
|
962
|
+
bytes?: string;
|
|
963
|
+
uri?: string;
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
interface A2ADataPart {
|
|
967
|
+
kind: "data";
|
|
968
|
+
data: Record<string, unknown>;
|
|
969
|
+
}
|
|
970
|
+
type A2APart = A2ATextPart | A2AFilePart | A2ADataPart;
|
|
971
|
+
interface A2AMessage {
|
|
972
|
+
role: "user" | "agent";
|
|
973
|
+
parts: A2APart[];
|
|
974
|
+
messageId?: string;
|
|
975
|
+
taskId?: string;
|
|
976
|
+
referenceTaskIds?: string[];
|
|
977
|
+
metadata?: Record<string, unknown>;
|
|
978
|
+
}
|
|
979
|
+
type A2ATaskState = "submitted" | "working" | "input-required" | "completed" | "failed" | "canceled";
|
|
980
|
+
interface A2AArtifact {
|
|
981
|
+
artifactId: string;
|
|
982
|
+
name?: string;
|
|
983
|
+
description?: string;
|
|
984
|
+
parts: A2APart[];
|
|
985
|
+
metadata?: Record<string, unknown>;
|
|
986
|
+
}
|
|
987
|
+
interface A2ATask {
|
|
988
|
+
id: string;
|
|
989
|
+
sessionId?: string;
|
|
990
|
+
status: {
|
|
991
|
+
state: A2ATaskState;
|
|
992
|
+
message?: A2AMessage;
|
|
993
|
+
timestamp?: string;
|
|
994
|
+
};
|
|
995
|
+
artifacts?: A2AArtifact[];
|
|
996
|
+
history?: A2AMessage[];
|
|
997
|
+
metadata?: Record<string, unknown>;
|
|
998
|
+
}
|
|
999
|
+
interface A2ASkill {
|
|
1000
|
+
id: string;
|
|
1001
|
+
name: string;
|
|
1002
|
+
description?: string;
|
|
1003
|
+
tags?: string[];
|
|
1004
|
+
examples?: string[];
|
|
1005
|
+
}
|
|
1006
|
+
interface A2AAgentCard {
|
|
1007
|
+
name: string;
|
|
1008
|
+
description?: string;
|
|
1009
|
+
url: string;
|
|
1010
|
+
version?: string;
|
|
1011
|
+
provider?: {
|
|
1012
|
+
organization: string;
|
|
1013
|
+
url?: string;
|
|
1014
|
+
};
|
|
1015
|
+
capabilities?: {
|
|
1016
|
+
streaming?: boolean;
|
|
1017
|
+
pushNotifications?: boolean;
|
|
1018
|
+
stateTransitionHistory?: boolean;
|
|
1019
|
+
};
|
|
1020
|
+
authentication?: {
|
|
1021
|
+
schemes?: string[];
|
|
1022
|
+
credentials?: string;
|
|
1023
|
+
};
|
|
1024
|
+
skills?: A2ASkill[];
|
|
1025
|
+
defaultInputModes?: string[];
|
|
1026
|
+
defaultOutputModes?: string[];
|
|
1027
|
+
supportedInputModes?: string[];
|
|
1028
|
+
supportedOutputModes?: string[];
|
|
1029
|
+
}
|
|
1030
|
+
interface A2AJsonRpcRequest {
|
|
1031
|
+
jsonrpc: "2.0";
|
|
1032
|
+
id: string | number;
|
|
1033
|
+
method: string;
|
|
1034
|
+
params?: Record<string, unknown>;
|
|
1035
|
+
}
|
|
1036
|
+
interface A2AJsonRpcResponse {
|
|
1037
|
+
jsonrpc: "2.0";
|
|
1038
|
+
id: string | number;
|
|
1039
|
+
result?: unknown;
|
|
1040
|
+
error?: {
|
|
1041
|
+
code: number;
|
|
1042
|
+
message: string;
|
|
1043
|
+
data?: unknown;
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
interface A2ASendParams {
|
|
1047
|
+
message: A2AMessage;
|
|
1048
|
+
configuration?: {
|
|
1049
|
+
acceptedOutputModes?: string[];
|
|
1050
|
+
blocking?: boolean;
|
|
1051
|
+
};
|
|
1052
|
+
metadata?: Record<string, unknown>;
|
|
1053
|
+
}
|
|
1054
|
+
interface A2ATaskQueryParams {
|
|
1055
|
+
id: string;
|
|
1056
|
+
historyLength?: number;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
interface A2ARemoteAgentConfig {
|
|
1060
|
+
url: string;
|
|
1061
|
+
/** Custom headers for every request (e.g. auth tokens). */
|
|
1062
|
+
headers?: Record<string, string>;
|
|
1063
|
+
/** Override the discovered name. */
|
|
1064
|
+
name?: string;
|
|
1065
|
+
/** Request timeout in ms (default 60000). */
|
|
1066
|
+
timeoutMs?: number;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* A2ARemoteAgent wraps a remote A2A-compliant agent.
|
|
1070
|
+
* It can be used as a tool, a Team member, or called directly.
|
|
1071
|
+
*/
|
|
1072
|
+
declare class A2ARemoteAgent {
|
|
1073
|
+
readonly url: string;
|
|
1074
|
+
name: string;
|
|
1075
|
+
instructions: string;
|
|
1076
|
+
skills: Array<{
|
|
1077
|
+
id: string;
|
|
1078
|
+
name: string;
|
|
1079
|
+
description?: string;
|
|
1080
|
+
}>;
|
|
1081
|
+
private headers;
|
|
1082
|
+
private timeoutMs;
|
|
1083
|
+
private card;
|
|
1084
|
+
private rpcId;
|
|
1085
|
+
get tools(): ToolDef[];
|
|
1086
|
+
get modelId(): string;
|
|
1087
|
+
get providerId(): string;
|
|
1088
|
+
get hasStructuredOutput(): boolean;
|
|
1089
|
+
constructor(config: A2ARemoteAgentConfig);
|
|
1090
|
+
/**
|
|
1091
|
+
* Fetch the Agent Card from /.well-known/agent.json and populate metadata.
|
|
1092
|
+
*/
|
|
1093
|
+
discover(): Promise<A2AAgentCard>;
|
|
1094
|
+
/**
|
|
1095
|
+
* Synchronous run: sends message/send and returns RunOutput.
|
|
1096
|
+
*/
|
|
1097
|
+
run(input: string, opts?: RunOpts): Promise<RunOutput>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Streaming run: sends message/stream and yields StreamChunks from SSE.
|
|
1100
|
+
*/
|
|
1101
|
+
stream(input: string, opts?: RunOpts): AsyncGenerator<StreamChunk>;
|
|
1102
|
+
/**
|
|
1103
|
+
* Wrap this remote agent as a ToolDef so it can be used by an orchestrator agent.
|
|
1104
|
+
*/
|
|
1105
|
+
asTool(): ToolDef;
|
|
1106
|
+
getAgentCard(): A2AAgentCard | null;
|
|
1107
|
+
private partsToText;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
export { type A2AAgentCard, type A2AArtifact, type A2ADataPart, type A2AFilePart, type A2AJsonRpcRequest, type A2AJsonRpcResponse, type A2AMessage, type A2APart, A2ARemoteAgent, type A2ARemoteAgentConfig, type A2ASendParams, type A2ASkill, type A2ATask, type A2ATaskQueryParams, type A2ATaskState, type A2ATextPart, Agent, type AgentConfig, type AgentEventMap, type AgentHooks, type AgentStep, AnthropicProvider, type Artifact, type AudioPart, BaseVectorStore, type ChatMessage, type ConditionStep, type ContentPart, type EmbeddingProvider, EventBus, type FilePart, type FunctionStep, GoogleEmbedding, type GoogleEmbeddingConfig, GoogleProvider, type GuardrailResult, type ImagePart, InMemoryStorage, InMemoryVectorStore, type InputGuardrail, KnowledgeBase, type KnowledgeBaseConfig, type KnowledgeBaseToolConfig, LLMLoop, type LogLevel, Logger, type LoggerConfig, MCPToolProvider, type MCPToolProviderConfig, Memory, type MemoryConfig, type MemoryEntry, type MessageContent, type MessageRole, type ModelConfig, type ModelProvider, ModelRegistry, type ModelResponse, MongoDBStorage, type MongoDBVectorConfig, MongoDBVectorStore, OllamaProvider, OpenAIEmbedding, type OpenAIEmbeddingConfig, OpenAIProvider, type OutputGuardrail, type ParallelStep, type PgVectorConfig, PgVectorStore, PostgresStorage, type QdrantConfig, QdrantVectorStore, RunContext, type RunOpts, type RunOutput, type Session, SessionManager, SqliteStorage, type StepDef, type StepResult, type StorageDriver, type StreamChunk, Team, type TeamConfig, TeamMode, type TextPart, type TokenUsage, type ToolCall, type ToolCallResult, type ToolDef, type ToolDefinition, ToolExecutor, type ToolResult, type VectorDocument, type VectorSearchOptions, type VectorSearchResult, type VectorStore, Workflow, type WorkflowConfig, type WorkflowResult, anthropic, defineTool, getTextContent, google, isMultiModal, ollama, openai, registry };
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
}
|
|
@@ -3422,7 +3430,391 @@ ${summaries.join("\n")}`;
|
|
|
3422
3430
|
await this.storage.delete(SHORT_TERM_NS, sessionId);
|
|
3423
3431
|
}
|
|
3424
3432
|
};
|
|
3433
|
+
|
|
3434
|
+
// src/mcp/mcp-client.ts
|
|
3435
|
+
var MCPToolProvider = class {
|
|
3436
|
+
name;
|
|
3437
|
+
config;
|
|
3438
|
+
client = null;
|
|
3439
|
+
transportInstance = null;
|
|
3440
|
+
tools = [];
|
|
3441
|
+
connected = false;
|
|
3442
|
+
constructor(config) {
|
|
3443
|
+
this.name = config.name;
|
|
3444
|
+
this.config = config;
|
|
3445
|
+
}
|
|
3446
|
+
async connect() {
|
|
3447
|
+
if (this.connected) return;
|
|
3448
|
+
let ClientClass;
|
|
3449
|
+
try {
|
|
3450
|
+
const mod = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
3451
|
+
ClientClass = mod.Client;
|
|
3452
|
+
} catch {
|
|
3453
|
+
throw new Error(
|
|
3454
|
+
"@modelcontextprotocol/sdk is required for MCPToolProvider. Install it: npm install @modelcontextprotocol/sdk"
|
|
3455
|
+
);
|
|
3456
|
+
}
|
|
3457
|
+
this.client = new ClientClass(
|
|
3458
|
+
{ name: `radaros-${this.name}`, version: "1.0.0" },
|
|
3459
|
+
{ capabilities: {} }
|
|
3460
|
+
);
|
|
3461
|
+
if (this.config.transport === "stdio") {
|
|
3462
|
+
if (!this.config.command) {
|
|
3463
|
+
throw new Error("MCPToolProvider: 'command' is required for stdio transport");
|
|
3464
|
+
}
|
|
3465
|
+
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
3466
|
+
this.transportInstance = new StdioClientTransport({
|
|
3467
|
+
command: this.config.command,
|
|
3468
|
+
args: this.config.args ?? [],
|
|
3469
|
+
env: { ...process.env, ...this.config.env ?? {} }
|
|
3470
|
+
});
|
|
3471
|
+
} else if (this.config.transport === "http") {
|
|
3472
|
+
if (!this.config.url) {
|
|
3473
|
+
throw new Error("MCPToolProvider: 'url' is required for http transport");
|
|
3474
|
+
}
|
|
3475
|
+
let TransportClass;
|
|
3476
|
+
try {
|
|
3477
|
+
const mod = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
3478
|
+
TransportClass = mod.StreamableHTTPClientTransport;
|
|
3479
|
+
} catch {
|
|
3480
|
+
const mod = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
3481
|
+
TransportClass = mod.SSEClientTransport;
|
|
3482
|
+
}
|
|
3483
|
+
this.transportInstance = new TransportClass(
|
|
3484
|
+
new URL(this.config.url),
|
|
3485
|
+
{ requestInit: { headers: this.config.headers ?? {} } }
|
|
3486
|
+
);
|
|
3487
|
+
} else {
|
|
3488
|
+
throw new Error(`MCPToolProvider: unsupported transport '${this.config.transport}'`);
|
|
3489
|
+
}
|
|
3490
|
+
await this.client.connect(this.transportInstance);
|
|
3491
|
+
this.connected = true;
|
|
3492
|
+
await this.discoverTools();
|
|
3493
|
+
}
|
|
3494
|
+
async discoverTools() {
|
|
3495
|
+
const { z: z3 } = await import("zod");
|
|
3496
|
+
const result = await this.client.listTools();
|
|
3497
|
+
const mcpTools = result.tools ?? [];
|
|
3498
|
+
this.tools = mcpTools.map((mcpTool) => {
|
|
3499
|
+
const toolName = mcpTool.name;
|
|
3500
|
+
const description = mcpTool.description ?? "";
|
|
3501
|
+
const inputSchema = mcpTool.inputSchema ?? { type: "object", properties: {} };
|
|
3502
|
+
const parameters = this.jsonSchemaToZod(inputSchema, z3);
|
|
3503
|
+
const execute = async (args, _ctx) => {
|
|
3504
|
+
const callResult = await this.client.callTool({
|
|
3505
|
+
name: toolName,
|
|
3506
|
+
arguments: args
|
|
3507
|
+
});
|
|
3508
|
+
const contents = callResult.content ?? [];
|
|
3509
|
+
const textParts = contents.filter((c) => c.type === "text").map((c) => c.text);
|
|
3510
|
+
const text = textParts.join("\n") || JSON.stringify(callResult);
|
|
3511
|
+
const artifacts = contents.filter((c) => c.type !== "text").map((c) => ({
|
|
3512
|
+
type: c.type,
|
|
3513
|
+
data: c.data ?? c.blob ?? c.text,
|
|
3514
|
+
mimeType: c.mimeType
|
|
3515
|
+
}));
|
|
3516
|
+
if (artifacts.length > 0) {
|
|
3517
|
+
return { content: text, artifacts };
|
|
3518
|
+
}
|
|
3519
|
+
return text;
|
|
3520
|
+
};
|
|
3521
|
+
return {
|
|
3522
|
+
name: `${this.name}__${toolName}`,
|
|
3523
|
+
description: `[${this.name}] ${description}`,
|
|
3524
|
+
parameters,
|
|
3525
|
+
execute,
|
|
3526
|
+
rawJsonSchema: inputSchema
|
|
3527
|
+
};
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
jsonSchemaToZod(schema, z3) {
|
|
3531
|
+
if (!schema || !schema.properties) {
|
|
3532
|
+
return z3.object({}).passthrough();
|
|
3533
|
+
}
|
|
3534
|
+
const shape = {};
|
|
3535
|
+
const required = schema.required ?? [];
|
|
3536
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
3537
|
+
let field;
|
|
3538
|
+
switch (prop.type) {
|
|
3539
|
+
case "string":
|
|
3540
|
+
field = z3.string();
|
|
3541
|
+
if (prop.enum) field = z3.enum(prop.enum);
|
|
3542
|
+
break;
|
|
3543
|
+
case "number":
|
|
3544
|
+
case "integer":
|
|
3545
|
+
field = z3.number();
|
|
3546
|
+
break;
|
|
3547
|
+
case "boolean":
|
|
3548
|
+
field = z3.boolean();
|
|
3549
|
+
break;
|
|
3550
|
+
case "array":
|
|
3551
|
+
field = z3.array(z3.any());
|
|
3552
|
+
break;
|
|
3553
|
+
case "object":
|
|
3554
|
+
field = z3.record(z3.any());
|
|
3555
|
+
break;
|
|
3556
|
+
default:
|
|
3557
|
+
field = z3.any();
|
|
3558
|
+
}
|
|
3559
|
+
if (prop.description) {
|
|
3560
|
+
field = field.describe(prop.description);
|
|
3561
|
+
}
|
|
3562
|
+
if (!required.includes(key)) {
|
|
3563
|
+
field = field.optional();
|
|
3564
|
+
}
|
|
3565
|
+
shape[key] = field;
|
|
3566
|
+
}
|
|
3567
|
+
return z3.object(shape);
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* Returns tools from this MCP server as RadarOS ToolDef[].
|
|
3571
|
+
* Optionally filter by tool names to reduce token usage.
|
|
3572
|
+
*
|
|
3573
|
+
* @param filter - Tool names to include (without the server name prefix).
|
|
3574
|
+
* If omitted, returns all tools.
|
|
3575
|
+
*
|
|
3576
|
+
* @example
|
|
3577
|
+
* // All tools
|
|
3578
|
+
* await mcp.getTools()
|
|
3579
|
+
*
|
|
3580
|
+
* // Only specific tools (pass the original MCP tool names, not prefixed)
|
|
3581
|
+
* await mcp.getTools({ include: ["get_latest_release", "search_repositories"] })
|
|
3582
|
+
*
|
|
3583
|
+
* // Exclude specific tools
|
|
3584
|
+
* await mcp.getTools({ exclude: ["push_files", "create_repository"] })
|
|
3585
|
+
*/
|
|
3586
|
+
async getTools(filter) {
|
|
3587
|
+
if (!this.connected) {
|
|
3588
|
+
await this.connect();
|
|
3589
|
+
}
|
|
3590
|
+
if (!filter) {
|
|
3591
|
+
return [...this.tools];
|
|
3592
|
+
}
|
|
3593
|
+
const prefix = `${this.name}__`;
|
|
3594
|
+
return this.tools.filter((tool) => {
|
|
3595
|
+
const shortName = tool.name.startsWith(prefix) ? tool.name.slice(prefix.length) : tool.name;
|
|
3596
|
+
if (filter.include) {
|
|
3597
|
+
return filter.include.includes(shortName);
|
|
3598
|
+
}
|
|
3599
|
+
if (filter.exclude) {
|
|
3600
|
+
return !filter.exclude.includes(shortName);
|
|
3601
|
+
}
|
|
3602
|
+
return true;
|
|
3603
|
+
});
|
|
3604
|
+
}
|
|
3605
|
+
/** Refresh the tool list from the MCP server. */
|
|
3606
|
+
async refresh() {
|
|
3607
|
+
if (!this.connected) {
|
|
3608
|
+
throw new Error("MCPToolProvider: not connected. Call connect() first.");
|
|
3609
|
+
}
|
|
3610
|
+
await this.discoverTools();
|
|
3611
|
+
}
|
|
3612
|
+
/** Disconnect from the MCP server. */
|
|
3613
|
+
async close() {
|
|
3614
|
+
if (this.client && this.connected) {
|
|
3615
|
+
try {
|
|
3616
|
+
await this.client.close();
|
|
3617
|
+
} catch {
|
|
3618
|
+
}
|
|
3619
|
+
this.connected = false;
|
|
3620
|
+
this.tools = [];
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
};
|
|
3624
|
+
|
|
3625
|
+
// src/a2a/a2a-remote-agent.ts
|
|
3626
|
+
import { z as z2 } from "zod";
|
|
3627
|
+
var A2ARemoteAgent = class {
|
|
3628
|
+
url;
|
|
3629
|
+
name;
|
|
3630
|
+
instructions;
|
|
3631
|
+
skills = [];
|
|
3632
|
+
headers;
|
|
3633
|
+
timeoutMs;
|
|
3634
|
+
card = null;
|
|
3635
|
+
rpcId = 0;
|
|
3636
|
+
get tools() {
|
|
3637
|
+
return [];
|
|
3638
|
+
}
|
|
3639
|
+
get modelId() {
|
|
3640
|
+
return "a2a-remote";
|
|
3641
|
+
}
|
|
3642
|
+
get providerId() {
|
|
3643
|
+
return "a2a";
|
|
3644
|
+
}
|
|
3645
|
+
get hasStructuredOutput() {
|
|
3646
|
+
return false;
|
|
3647
|
+
}
|
|
3648
|
+
constructor(config) {
|
|
3649
|
+
this.url = config.url.replace(/\/$/, "");
|
|
3650
|
+
this.name = config.name ?? "remote-agent";
|
|
3651
|
+
this.instructions = "";
|
|
3652
|
+
this.headers = config.headers ?? {};
|
|
3653
|
+
this.timeoutMs = config.timeoutMs ?? 6e4;
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Fetch the Agent Card from /.well-known/agent.json and populate metadata.
|
|
3657
|
+
*/
|
|
3658
|
+
async discover() {
|
|
3659
|
+
const res = await fetch(`${this.url}/.well-known/agent.json`, {
|
|
3660
|
+
headers: this.headers,
|
|
3661
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
3662
|
+
});
|
|
3663
|
+
if (!res.ok) {
|
|
3664
|
+
throw new Error(
|
|
3665
|
+
`A2A discover failed: ${res.status} ${res.statusText} from ${this.url}`
|
|
3666
|
+
);
|
|
3667
|
+
}
|
|
3668
|
+
this.card = await res.json();
|
|
3669
|
+
this.name = this.card.name;
|
|
3670
|
+
this.instructions = this.card.description ?? "";
|
|
3671
|
+
this.skills = this.card.skills?.map((s) => ({
|
|
3672
|
+
id: s.id,
|
|
3673
|
+
name: s.name,
|
|
3674
|
+
description: s.description
|
|
3675
|
+
})) ?? [];
|
|
3676
|
+
return this.card;
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* Synchronous run: sends message/send and returns RunOutput.
|
|
3680
|
+
*/
|
|
3681
|
+
async run(input, opts) {
|
|
3682
|
+
const message = {
|
|
3683
|
+
role: "user",
|
|
3684
|
+
parts: [{ kind: "text", text: input }]
|
|
3685
|
+
};
|
|
3686
|
+
const rpcReq = {
|
|
3687
|
+
jsonrpc: "2.0",
|
|
3688
|
+
id: ++this.rpcId,
|
|
3689
|
+
method: "message/send",
|
|
3690
|
+
params: {
|
|
3691
|
+
message,
|
|
3692
|
+
...opts?.sessionId ? { sessionId: opts.sessionId } : {}
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
const startMs = Date.now();
|
|
3696
|
+
const res = await fetch(this.url, {
|
|
3697
|
+
method: "POST",
|
|
3698
|
+
headers: {
|
|
3699
|
+
"Content-Type": "application/json",
|
|
3700
|
+
...this.headers
|
|
3701
|
+
},
|
|
3702
|
+
body: JSON.stringify(rpcReq),
|
|
3703
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
3704
|
+
});
|
|
3705
|
+
if (!res.ok) {
|
|
3706
|
+
throw new Error(`A2A message/send failed: ${res.status} ${res.statusText}`);
|
|
3707
|
+
}
|
|
3708
|
+
const rpcRes = await res.json();
|
|
3709
|
+
if (rpcRes.error) {
|
|
3710
|
+
throw new Error(`A2A error: ${rpcRes.error.message}`);
|
|
3711
|
+
}
|
|
3712
|
+
const task = rpcRes.result;
|
|
3713
|
+
const agentMsg = task.status?.message;
|
|
3714
|
+
const text = agentMsg ? this.partsToText(agentMsg.parts) : "";
|
|
3715
|
+
return {
|
|
3716
|
+
text,
|
|
3717
|
+
toolCalls: [],
|
|
3718
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
3719
|
+
durationMs: Date.now() - startMs
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
/**
|
|
3723
|
+
* Streaming run: sends message/stream and yields StreamChunks from SSE.
|
|
3724
|
+
*/
|
|
3725
|
+
async *stream(input, opts) {
|
|
3726
|
+
const message = {
|
|
3727
|
+
role: "user",
|
|
3728
|
+
parts: [{ kind: "text", text: input }]
|
|
3729
|
+
};
|
|
3730
|
+
const rpcReq = {
|
|
3731
|
+
jsonrpc: "2.0",
|
|
3732
|
+
id: ++this.rpcId,
|
|
3733
|
+
method: "message/stream",
|
|
3734
|
+
params: {
|
|
3735
|
+
message,
|
|
3736
|
+
...opts?.sessionId ? { sessionId: opts.sessionId } : {}
|
|
3737
|
+
}
|
|
3738
|
+
};
|
|
3739
|
+
const res = await fetch(this.url, {
|
|
3740
|
+
method: "POST",
|
|
3741
|
+
headers: {
|
|
3742
|
+
"Content-Type": "application/json",
|
|
3743
|
+
...this.headers
|
|
3744
|
+
},
|
|
3745
|
+
body: JSON.stringify(rpcReq),
|
|
3746
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
3747
|
+
});
|
|
3748
|
+
if (!res.ok) {
|
|
3749
|
+
throw new Error(`A2A message/stream failed: ${res.status} ${res.statusText}`);
|
|
3750
|
+
}
|
|
3751
|
+
if (!res.body) {
|
|
3752
|
+
throw new Error("A2A message/stream: no response body for SSE");
|
|
3753
|
+
}
|
|
3754
|
+
const reader = res.body.getReader();
|
|
3755
|
+
const decoder = new TextDecoder();
|
|
3756
|
+
let buffer = "";
|
|
3757
|
+
while (true) {
|
|
3758
|
+
const { done, value } = await reader.read();
|
|
3759
|
+
if (done) break;
|
|
3760
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3761
|
+
const lines = buffer.split("\n");
|
|
3762
|
+
buffer = lines.pop();
|
|
3763
|
+
for (const line of lines) {
|
|
3764
|
+
if (!line.startsWith("data: ")) continue;
|
|
3765
|
+
const jsonStr = line.slice(6).trim();
|
|
3766
|
+
if (!jsonStr) continue;
|
|
3767
|
+
try {
|
|
3768
|
+
const event = JSON.parse(jsonStr);
|
|
3769
|
+
const task = event.result;
|
|
3770
|
+
if (!task) continue;
|
|
3771
|
+
if (task.status?.state === "working" && task.status.message?.parts?.length) {
|
|
3772
|
+
for (const part of task.status.message.parts) {
|
|
3773
|
+
if (part.kind === "text") {
|
|
3774
|
+
yield { type: "text", text: part.text };
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
if (task.status?.state === "completed") {
|
|
3779
|
+
yield {
|
|
3780
|
+
type: "finish",
|
|
3781
|
+
finishReason: "stop",
|
|
3782
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 }
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
} catch {
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
/**
|
|
3791
|
+
* Wrap this remote agent as a ToolDef so it can be used by an orchestrator agent.
|
|
3792
|
+
*/
|
|
3793
|
+
asTool() {
|
|
3794
|
+
const self = this;
|
|
3795
|
+
return {
|
|
3796
|
+
name: `a2a_${this.name.replace(/[^a-zA-Z0-9_]/g, "_")}`,
|
|
3797
|
+
description: this.instructions || `Remote A2A agent: ${this.name}`,
|
|
3798
|
+
parameters: z2.object({
|
|
3799
|
+
message: z2.string().describe("The message to send to the remote agent")
|
|
3800
|
+
}),
|
|
3801
|
+
async execute(args, _ctx) {
|
|
3802
|
+
const result = await self.run(args.message);
|
|
3803
|
+
return result.text;
|
|
3804
|
+
}
|
|
3805
|
+
};
|
|
3806
|
+
}
|
|
3807
|
+
getAgentCard() {
|
|
3808
|
+
return this.card;
|
|
3809
|
+
}
|
|
3810
|
+
partsToText(parts) {
|
|
3811
|
+
return parts.filter(
|
|
3812
|
+
(p) => p.kind === "text"
|
|
3813
|
+
).map((p) => p.text).join("\n");
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3425
3816
|
export {
|
|
3817
|
+
A2ARemoteAgent,
|
|
3426
3818
|
Agent,
|
|
3427
3819
|
AnthropicProvider,
|
|
3428
3820
|
BaseVectorStore,
|
|
@@ -3434,6 +3826,7 @@ export {
|
|
|
3434
3826
|
KnowledgeBase,
|
|
3435
3827
|
LLMLoop,
|
|
3436
3828
|
Logger,
|
|
3829
|
+
MCPToolProvider,
|
|
3437
3830
|
Memory,
|
|
3438
3831
|
ModelRegistry,
|
|
3439
3832
|
MongoDBStorage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radaros/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"mongodb": "^6.0.0",
|
|
34
34
|
"ollama": "^0.5.0",
|
|
35
35
|
"openai": "^4.0.0 || ^5.0.0 || ^6.0.0",
|
|
36
|
-
"pg": "^8.0.0"
|
|
36
|
+
"pg": "^8.0.0",
|
|
37
|
+
"@modelcontextprotocol/sdk": ">=1.0.0"
|
|
37
38
|
},
|
|
38
39
|
"peerDependenciesMeta": {
|
|
39
40
|
"openai": {
|
|
@@ -59,6 +60,9 @@
|
|
|
59
60
|
},
|
|
60
61
|
"@qdrant/js-client-rest": {
|
|
61
62
|
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"@modelcontextprotocol/sdk": {
|
|
65
|
+
"optional": true
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
}
|