@contractspec/lib.ai-agent 2.5.0 → 2.7.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/README.md +33 -2
- package/dist/agent/agent-factory.d.ts +5 -0
- package/dist/agent/agent-factory.js +221 -11
- package/dist/agent/contract-spec-agent.d.ts +8 -0
- package/dist/agent/contract-spec-agent.js +210 -10
- package/dist/agent/index.js +334 -39
- package/dist/agent/json-runner.js +210 -10
- package/dist/agent/unified-agent.d.ts +4 -0
- package/dist/agent/unified-agent.js +334 -39
- package/dist/exporters/claude-agent-exporter.d.ts +1 -0
- package/dist/exporters/claude-agent-exporter.js +11 -1
- package/dist/exporters/index.js +11 -1
- package/dist/exporters/types.d.ts +3 -10
- package/dist/node/agent/agent-factory.js +221 -11
- package/dist/node/agent/contract-spec-agent.js +210 -10
- package/dist/node/agent/index.js +334 -39
- package/dist/node/agent/json-runner.js +210 -10
- package/dist/node/agent/unified-agent.js +334 -39
- package/dist/node/exporters/claude-agent-exporter.js +11 -1
- package/dist/node/exporters/index.js +11 -1
- package/dist/node/providers/claude-agent-sdk/adapter.js +260 -23
- package/dist/node/providers/claude-agent-sdk/index.js +260 -23
- package/dist/node/providers/index.js +260 -23
- package/dist/node/tools/index.js +154 -18
- package/dist/node/tools/mcp-client-helpers.js +106 -0
- package/dist/node/tools/mcp-client.js +155 -18
- package/dist/providers/claude-agent-sdk/adapter.d.ts +4 -0
- package/dist/providers/claude-agent-sdk/adapter.js +260 -23
- package/dist/providers/claude-agent-sdk/index.d.ts +8 -0
- package/dist/providers/claude-agent-sdk/index.js +260 -23
- package/dist/providers/index.js +260 -23
- package/dist/providers/types.d.ts +1 -1
- package/dist/tools/index.js +154 -18
- package/dist/tools/mcp-client-helpers.d.ts +12 -0
- package/dist/tools/mcp-client-helpers.js +106 -0
- package/dist/tools/mcp-client.d.ts +55 -3
- package/dist/tools/mcp-client.js +155 -18
- package/dist/tools/mcp-client.test.d.ts +1 -0
- package/package.json +24 -12
|
@@ -2400,6 +2400,171 @@ var init_knowledge_tool = __esm(() => {
|
|
|
2400
2400
|
init_i18n();
|
|
2401
2401
|
});
|
|
2402
2402
|
|
|
2403
|
+
// src/tools/mcp-client-helpers.ts
|
|
2404
|
+
import {
|
|
2405
|
+
Experimental_StdioMCPTransport as StdioClientTransport
|
|
2406
|
+
} from "@ai-sdk/mcp/mcp-stdio";
|
|
2407
|
+
function buildMcpTransport(config) {
|
|
2408
|
+
const transport = resolveTransportType(config);
|
|
2409
|
+
if (transport === "stdio") {
|
|
2410
|
+
const stdioConfig = resolveStdioConfig(config);
|
|
2411
|
+
return new StdioClientTransport(stdioConfig);
|
|
2412
|
+
}
|
|
2413
|
+
const remoteConfig = config;
|
|
2414
|
+
const headers = resolveRemoteHeaders(remoteConfig);
|
|
2415
|
+
const remoteTransport = {
|
|
2416
|
+
type: transport,
|
|
2417
|
+
url: requireNonEmptyString(remoteConfig.url, "url", config.name)
|
|
2418
|
+
};
|
|
2419
|
+
if (headers) {
|
|
2420
|
+
remoteTransport.headers = headers;
|
|
2421
|
+
}
|
|
2422
|
+
if (remoteConfig.authProvider) {
|
|
2423
|
+
remoteTransport.authProvider = remoteConfig.authProvider;
|
|
2424
|
+
}
|
|
2425
|
+
return remoteTransport;
|
|
2426
|
+
}
|
|
2427
|
+
function prefixToolNames(config, tools) {
|
|
2428
|
+
const prefix = config.toolPrefix?.trim();
|
|
2429
|
+
if (!prefix) {
|
|
2430
|
+
return tools;
|
|
2431
|
+
}
|
|
2432
|
+
const prefixedTools = {};
|
|
2433
|
+
for (const [toolName, tool3] of Object.entries(tools)) {
|
|
2434
|
+
prefixedTools[`${prefix}_${toolName}`] = tool3;
|
|
2435
|
+
}
|
|
2436
|
+
return prefixedTools;
|
|
2437
|
+
}
|
|
2438
|
+
function getErrorMessage(error) {
|
|
2439
|
+
if (error instanceof Error) {
|
|
2440
|
+
return error.message;
|
|
2441
|
+
}
|
|
2442
|
+
return String(error);
|
|
2443
|
+
}
|
|
2444
|
+
function resolveTransportType(config) {
|
|
2445
|
+
return config.transport ?? "stdio";
|
|
2446
|
+
}
|
|
2447
|
+
function resolveStdioConfig(config) {
|
|
2448
|
+
const stdioConfig = config;
|
|
2449
|
+
return {
|
|
2450
|
+
command: requireNonEmptyString(stdioConfig.command, "command", config.name),
|
|
2451
|
+
args: stdioConfig.args,
|
|
2452
|
+
env: stdioConfig.env,
|
|
2453
|
+
cwd: stdioConfig.cwd
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
function resolveRemoteHeaders(config) {
|
|
2457
|
+
const headers = {
|
|
2458
|
+
...config.headers ?? {}
|
|
2459
|
+
};
|
|
2460
|
+
const accessToken = config.accessToken ?? resolveEnvToken(config.accessTokenEnvVar);
|
|
2461
|
+
if (accessToken && headers.Authorization === undefined) {
|
|
2462
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
2463
|
+
}
|
|
2464
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
2465
|
+
}
|
|
2466
|
+
function resolveEnvToken(envVarName) {
|
|
2467
|
+
if (!envVarName) {
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
const value = process.env[envVarName];
|
|
2471
|
+
if (!value) {
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2474
|
+
const trimmed = value.trim();
|
|
2475
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
2476
|
+
}
|
|
2477
|
+
function requireNonEmptyString(value, field, serverName) {
|
|
2478
|
+
if (!value) {
|
|
2479
|
+
throw new Error(`MCP server "${serverName}" is missing required "${field}".`);
|
|
2480
|
+
}
|
|
2481
|
+
const trimmed = value.trim();
|
|
2482
|
+
if (trimmed.length === 0) {
|
|
2483
|
+
throw new Error(`MCP server "${serverName}" has an empty "${field}".`);
|
|
2484
|
+
}
|
|
2485
|
+
return trimmed;
|
|
2486
|
+
}
|
|
2487
|
+
var init_mcp_client_helpers = () => {};
|
|
2488
|
+
|
|
2489
|
+
// src/tools/mcp-client.ts
|
|
2490
|
+
import {
|
|
2491
|
+
experimental_createMCPClient
|
|
2492
|
+
} from "@ai-sdk/mcp";
|
|
2493
|
+
async function mcpServerToTools(config) {
|
|
2494
|
+
let client = null;
|
|
2495
|
+
try {
|
|
2496
|
+
const transport = buildMcpTransport(config);
|
|
2497
|
+
client = await experimental_createMCPClient({
|
|
2498
|
+
transport,
|
|
2499
|
+
name: config.clientName,
|
|
2500
|
+
version: config.clientVersion
|
|
2501
|
+
});
|
|
2502
|
+
const tools = await client.tools();
|
|
2503
|
+
const prefixedTools = prefixToolNames(config, tools);
|
|
2504
|
+
const connectedClient = client;
|
|
2505
|
+
return {
|
|
2506
|
+
tools: prefixedTools,
|
|
2507
|
+
cleanup: () => connectedClient.close(),
|
|
2508
|
+
serverToolNames: {
|
|
2509
|
+
[config.name]: Object.keys(prefixedTools)
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
if (client) {
|
|
2514
|
+
await client.close().catch(() => {
|
|
2515
|
+
return;
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
throw new Error(`[MCP:${config.name}] Failed to connect tools: ${getErrorMessage(error)}`);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
async function createMcpToolsets(configs, options = {}) {
|
|
2522
|
+
const connected = [];
|
|
2523
|
+
try {
|
|
2524
|
+
for (const config of configs) {
|
|
2525
|
+
const result = await mcpServerToTools(config);
|
|
2526
|
+
connected.push(result);
|
|
2527
|
+
}
|
|
2528
|
+
} catch (error) {
|
|
2529
|
+
await Promise.allSettled(connected.map((result) => result.cleanup()));
|
|
2530
|
+
throw error;
|
|
2531
|
+
}
|
|
2532
|
+
const combinedTools = {};
|
|
2533
|
+
const serverToolNames = {};
|
|
2534
|
+
const collisionStrategy = options.onNameCollision ?? "overwrite";
|
|
2535
|
+
try {
|
|
2536
|
+
for (const result of connected) {
|
|
2537
|
+
for (const [serverName, toolNames] of Object.entries(result.serverToolNames)) {
|
|
2538
|
+
serverToolNames[serverName] = toolNames;
|
|
2539
|
+
}
|
|
2540
|
+
for (const [toolName, tool3] of Object.entries(result.tools)) {
|
|
2541
|
+
const hasCollision = combinedTools[toolName] !== undefined;
|
|
2542
|
+
if (hasCollision && collisionStrategy === "error") {
|
|
2543
|
+
throw new Error(`Duplicate MCP tool name "${toolName}" detected. Use "toolPrefix" or set onNameCollision to "overwrite".`);
|
|
2544
|
+
}
|
|
2545
|
+
combinedTools[toolName] = tool3;
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
} catch (error) {
|
|
2549
|
+
await Promise.allSettled(connected.map((result) => result.cleanup()));
|
|
2550
|
+
throw error;
|
|
2551
|
+
}
|
|
2552
|
+
return {
|
|
2553
|
+
tools: combinedTools,
|
|
2554
|
+
serverToolNames,
|
|
2555
|
+
cleanup: async () => {
|
|
2556
|
+
const cleanupResults = await Promise.allSettled(connected.map((result) => result.cleanup()));
|
|
2557
|
+
const failures = cleanupResults.filter((result) => result.status === "rejected");
|
|
2558
|
+
if (failures.length > 0) {
|
|
2559
|
+
throw new Error(`Failed to cleanup ${failures.length} MCP client connection(s).`);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
}
|
|
2564
|
+
var init_mcp_client = __esm(() => {
|
|
2565
|
+
init_mcp_client_helpers();
|
|
2566
|
+
});
|
|
2567
|
+
|
|
2403
2568
|
// src/knowledge/injector.ts
|
|
2404
2569
|
async function injectStaticKnowledge(instructions, knowledgeRefs, retriever, locale) {
|
|
2405
2570
|
if (!retriever)
|
|
@@ -2909,25 +3074,59 @@ class ContractSpecAgent {
|
|
|
2909
3074
|
tools;
|
|
2910
3075
|
config;
|
|
2911
3076
|
instructions;
|
|
3077
|
+
mcpCleanup;
|
|
2912
3078
|
activeStepContexts = new Map;
|
|
2913
|
-
constructor(config, instructions, tools) {
|
|
3079
|
+
constructor(config, instructions, tools, mcpCleanup) {
|
|
2914
3080
|
this.config = config;
|
|
2915
3081
|
this.spec = config.spec;
|
|
2916
3082
|
this.id = agentKey(config.spec.meta);
|
|
2917
3083
|
this.tools = tools;
|
|
2918
3084
|
this.instructions = instructions;
|
|
3085
|
+
this.mcpCleanup = mcpCleanup;
|
|
2919
3086
|
}
|
|
2920
3087
|
static async create(config) {
|
|
2921
3088
|
const effectiveConfig = config;
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
3089
|
+
let mcpToolset = null;
|
|
3090
|
+
if ((effectiveConfig.mcpServers?.length ?? 0) > 0) {
|
|
3091
|
+
mcpToolset = await createMcpToolsets(effectiveConfig.mcpServers ?? [], {
|
|
3092
|
+
onNameCollision: "error"
|
|
3093
|
+
});
|
|
3094
|
+
}
|
|
3095
|
+
try {
|
|
3096
|
+
const instructions = await injectStaticKnowledge(effectiveConfig.spec.instructions, effectiveConfig.spec.knowledge ?? [], effectiveConfig.knowledgeRetriever);
|
|
3097
|
+
const specTools = specToolsToAISDKTools(effectiveConfig.spec.tools, effectiveConfig.toolHandlers, { agentId: agentKey(effectiveConfig.spec.meta) });
|
|
3098
|
+
const knowledgeTool = effectiveConfig.knowledgeRetriever ? createKnowledgeQueryTool(effectiveConfig.knowledgeRetriever, effectiveConfig.spec.knowledge ?? []) : null;
|
|
3099
|
+
const reservedToolNames = new Set(Object.keys(specTools));
|
|
3100
|
+
if (knowledgeTool) {
|
|
3101
|
+
reservedToolNames.add("query_knowledge");
|
|
3102
|
+
}
|
|
3103
|
+
const conflictingMcpTools = Object.keys(mcpToolset?.tools ?? {}).filter((toolName) => reservedToolNames.has(toolName));
|
|
3104
|
+
if (conflictingMcpTools.length > 0) {
|
|
3105
|
+
throw new Error(`MCP tools conflict with agent tools: ${conflictingMcpTools.join(", ")}. Configure MCP toolPrefix values to avoid collisions.`);
|
|
3106
|
+
}
|
|
3107
|
+
const tools = {
|
|
3108
|
+
...specTools,
|
|
3109
|
+
...knowledgeTool ? { query_knowledge: knowledgeTool } : {},
|
|
3110
|
+
...mcpToolset?.tools ?? {},
|
|
3111
|
+
...effectiveConfig.additionalTools ?? {}
|
|
3112
|
+
};
|
|
3113
|
+
return new ContractSpecAgent(effectiveConfig, instructions, tools, mcpToolset?.cleanup);
|
|
3114
|
+
} catch (error) {
|
|
3115
|
+
if (mcpToolset) {
|
|
3116
|
+
await mcpToolset.cleanup().catch(() => {
|
|
3117
|
+
return;
|
|
3118
|
+
});
|
|
3119
|
+
}
|
|
3120
|
+
throw error;
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
async cleanup() {
|
|
3124
|
+
if (!this.mcpCleanup) {
|
|
3125
|
+
return;
|
|
3126
|
+
}
|
|
3127
|
+
const cleanup = this.mcpCleanup;
|
|
3128
|
+
this.mcpCleanup = undefined;
|
|
3129
|
+
await cleanup();
|
|
2931
3130
|
}
|
|
2932
3131
|
async generate(params) {
|
|
2933
3132
|
const sessionId = params.options?.sessionId ?? generateSessionId();
|
|
@@ -3102,6 +3301,7 @@ var init_contract_spec_agent = __esm(() => {
|
|
|
3102
3301
|
init_spec();
|
|
3103
3302
|
init_tool_adapter();
|
|
3104
3303
|
init_knowledge_tool();
|
|
3304
|
+
init_mcp_client();
|
|
3105
3305
|
init_injector();
|
|
3106
3306
|
init_adapter();
|
|
3107
3307
|
ContractSpecCallOptionsSchema = z3.object({
|
|
@@ -3375,6 +3575,8 @@ function summarizeSession(session) {
|
|
|
3375
3575
|
}
|
|
3376
3576
|
|
|
3377
3577
|
// src/providers/claude-agent-sdk/adapter.ts
|
|
3578
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3579
|
+
|
|
3378
3580
|
class ClaudeAgentSDKProvider {
|
|
3379
3581
|
name = "claude-agent-sdk";
|
|
3380
3582
|
version = "1.0.0";
|
|
@@ -3407,11 +3609,23 @@ class ClaudeAgentSDKProvider {
|
|
|
3407
3609
|
if (!this.isAvailable()) {
|
|
3408
3610
|
throw new ProviderNotAvailableError(this.name, createAgentI18n(this.config.locale).t("error.provider.sdkNotConfigured"));
|
|
3409
3611
|
}
|
|
3612
|
+
let mcpToolset = null;
|
|
3410
3613
|
try {
|
|
3411
3614
|
const toolSet = {};
|
|
3412
3615
|
for (const tool3 of spec.tools) {
|
|
3413
3616
|
toolSet[tool3.name] = specToolToExternalTool(tool3);
|
|
3414
3617
|
}
|
|
3618
|
+
if ((this.config.mcpServers?.length ?? 0) > 0) {
|
|
3619
|
+
mcpToolset = await createMcpToolsets(this.config.mcpServers ?? [], {
|
|
3620
|
+
onNameCollision: "error"
|
|
3621
|
+
});
|
|
3622
|
+
for (const [toolName, mcpTool] of Object.entries(mcpToolset.tools)) {
|
|
3623
|
+
if (toolSet[toolName]) {
|
|
3624
|
+
throw new Error(`MCP tool "${toolName}" collides with a ContractSpec tool. Configure MCP toolPrefix values to avoid collisions.`);
|
|
3625
|
+
}
|
|
3626
|
+
toolSet[toolName] = this.mcpToolToExternalTool(toolName, mcpTool);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3415
3629
|
const instructions = await injectStaticKnowledge(spec.instructions, spec.knowledge ?? [], undefined);
|
|
3416
3630
|
const contextId = `claude-${agentKey(spec.meta)}-${Date.now()}`;
|
|
3417
3631
|
const metadata = {
|
|
@@ -3419,6 +3633,7 @@ class ClaudeAgentSDKProvider {
|
|
|
3419
3633
|
extendedThinkingEnabled: this.config.extendedThinking ?? false,
|
|
3420
3634
|
mcpServerIds: this.config.mcpServers?.map((s) => s.name) ?? []
|
|
3421
3635
|
};
|
|
3636
|
+
const cleanupMcp = mcpToolset?.cleanup;
|
|
3422
3637
|
return {
|
|
3423
3638
|
id: contextId,
|
|
3424
3639
|
spec: {
|
|
@@ -3427,9 +3642,18 @@ class ClaudeAgentSDKProvider {
|
|
|
3427
3642
|
},
|
|
3428
3643
|
tools: toolSet,
|
|
3429
3644
|
metadata,
|
|
3430
|
-
cleanup: async () => {
|
|
3645
|
+
cleanup: async () => {
|
|
3646
|
+
if (cleanupMcp) {
|
|
3647
|
+
await cleanupMcp();
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3431
3650
|
};
|
|
3432
3651
|
} catch (error) {
|
|
3652
|
+
if (mcpToolset) {
|
|
3653
|
+
await mcpToolset.cleanup().catch(() => {
|
|
3654
|
+
return;
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3433
3657
|
throw new ContextCreationError(this.name, createAgentI18n(this.config.locale).t("error.provider.contextCreation", {
|
|
3434
3658
|
error: error instanceof Error ? error.message : String(error)
|
|
3435
3659
|
}), error instanceof Error ? error : undefined);
|
|
@@ -3444,7 +3668,7 @@ ${params.systemOverride}` : context.spec.instructions;
|
|
|
3444
3668
|
const claudeContext = buildClaudeAgentContext(params.options);
|
|
3445
3669
|
let session = createEmptyClaudeSession();
|
|
3446
3670
|
session = appendUserMessage(session, params.prompt);
|
|
3447
|
-
const claudeTools = this.prepareToolsForSDK(context
|
|
3671
|
+
const claudeTools = this.prepareToolsForSDK(context);
|
|
3448
3672
|
const rawResponse = await sdk.execute({
|
|
3449
3673
|
model: this.config.model,
|
|
3450
3674
|
system: systemPrompt,
|
|
@@ -3500,7 +3724,7 @@ ${params.systemOverride}` : context.spec.instructions;
|
|
|
3500
3724
|
|
|
3501
3725
|
${params.systemOverride}` : context.spec.instructions;
|
|
3502
3726
|
const claudeContext = buildClaudeAgentContext(params.options);
|
|
3503
|
-
const claudeTools = this.prepareToolsForSDK(context
|
|
3727
|
+
const claudeTools = this.prepareToolsForSDK(context);
|
|
3504
3728
|
const stream = await sdk.stream({
|
|
3505
3729
|
model: this.config.model,
|
|
3506
3730
|
system: systemPrompt,
|
|
@@ -3583,30 +3807,77 @@ ${params.systemOverride}` : context.spec.instructions;
|
|
|
3583
3807
|
throw new ProviderNotAvailableError(this.name, createAgentI18n(this.config.locale).t("error.provider.claudeSdkMissing"));
|
|
3584
3808
|
}
|
|
3585
3809
|
}
|
|
3586
|
-
prepareToolsForSDK(context
|
|
3587
|
-
const
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
if (externalTool
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3810
|
+
prepareToolsForSDK(context) {
|
|
3811
|
+
const i18n = createAgentI18n(this.config.locale);
|
|
3812
|
+
const toolsForSdk = [];
|
|
3813
|
+
for (const [toolName, externalTool] of Object.entries(context.tools)) {
|
|
3814
|
+
if (!externalTool.execute) {
|
|
3815
|
+
continue;
|
|
3816
|
+
}
|
|
3817
|
+
toolsForSdk.push({
|
|
3818
|
+
name: toolName,
|
|
3819
|
+
description: externalTool.description ?? i18n.t("tool.fallbackDescription", { name: toolName }),
|
|
3820
|
+
input_schema: this.normalizeToolSchemaForClaude(externalTool.inputSchema),
|
|
3821
|
+
requires_confirmation: externalTool.requiresApproval,
|
|
3822
|
+
execute: async (input) => {
|
|
3823
|
+
const result = await externalTool.execute?.(input);
|
|
3598
3824
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
3825
|
+
}
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
return toolsForSdk;
|
|
3829
|
+
}
|
|
3830
|
+
mcpToolToExternalTool(toolName, tool3) {
|
|
3831
|
+
return {
|
|
3832
|
+
name: toolName,
|
|
3833
|
+
description: tool3.description ?? createAgentI18n(this.config.locale).t("tool.fallbackDescription", {
|
|
3834
|
+
name: toolName
|
|
3835
|
+
}),
|
|
3836
|
+
inputSchema: this.normalizeExternalInputSchema(tool3.inputSchema),
|
|
3837
|
+
execute: async (input) => {
|
|
3838
|
+
if (!tool3.execute) {
|
|
3839
|
+
throw new Error(createAgentI18n(this.config.locale).t("error.toolNoExecuteHandler", {
|
|
3840
|
+
name: toolName
|
|
3841
|
+
}));
|
|
3842
|
+
}
|
|
3843
|
+
return tool3.execute(input, {
|
|
3844
|
+
toolCallId: `mcp-${randomUUID2()}`,
|
|
3845
|
+
messages: []
|
|
3599
3846
|
});
|
|
3600
3847
|
}
|
|
3848
|
+
};
|
|
3849
|
+
}
|
|
3850
|
+
normalizeExternalInputSchema(schema) {
|
|
3851
|
+
if (this.isRecord(schema)) {
|
|
3852
|
+
const type = schema["type"];
|
|
3853
|
+
if (type === "object" || schema["properties"]) {
|
|
3854
|
+
return schema;
|
|
3855
|
+
}
|
|
3601
3856
|
}
|
|
3602
|
-
return
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3857
|
+
return {
|
|
3858
|
+
type: "object",
|
|
3859
|
+
properties: {}
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
normalizeToolSchemaForClaude(schema) {
|
|
3863
|
+
if (schema.type === "object") {
|
|
3864
|
+
return {
|
|
3865
|
+
type: "object",
|
|
3866
|
+
properties: schema.properties,
|
|
3867
|
+
required: schema.required,
|
|
3868
|
+
additionalProperties: schema.additionalProperties
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
return {
|
|
3872
|
+
type: "object",
|
|
3873
|
+
properties: {
|
|
3874
|
+
value: schema
|
|
3875
|
+
},
|
|
3876
|
+
required: ["value"]
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
isRecord(value) {
|
|
3880
|
+
return typeof value === "object" && value !== null;
|
|
3610
3881
|
}
|
|
3611
3882
|
async executeTool(toolCall, context, _params) {
|
|
3612
3883
|
const tool3 = context.tools[toolCall.toolName];
|
|
@@ -3662,6 +3933,7 @@ var init_adapter2 = __esm(() => {
|
|
|
3662
3933
|
init_types();
|
|
3663
3934
|
init_tool_bridge();
|
|
3664
3935
|
init_injector();
|
|
3936
|
+
init_mcp_client();
|
|
3665
3937
|
init_i18n();
|
|
3666
3938
|
});
|
|
3667
3939
|
|
|
@@ -4320,6 +4592,7 @@ class UnifiedAgent {
|
|
|
4320
4592
|
} catch (error) {
|
|
4321
4593
|
this.state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
4322
4594
|
if (this.config.fallbackBackend && this.config.fallbackBackend !== backend) {
|
|
4595
|
+
await this.cleanupProviderContext();
|
|
4323
4596
|
console.warn(this.i18n().t("log.unifiedAgent.fallback", {
|
|
4324
4597
|
backend: String(backend),
|
|
4325
4598
|
fallback: String(this.config.fallbackBackend)
|
|
@@ -4390,16 +4663,24 @@ class UnifiedAgent {
|
|
|
4390
4663
|
}
|
|
4391
4664
|
async runWithAISDK(message, options) {
|
|
4392
4665
|
const { ContractSpecAgent: ContractSpecAgent2 } = await Promise.resolve().then(() => (init_contract_spec_agent(), exports_contract_spec_agent));
|
|
4666
|
+
const backendConfig = this.getAISDKConfig();
|
|
4393
4667
|
const model = await this.resolveAISDKModel();
|
|
4394
4668
|
const agent = await ContractSpecAgent2.create({
|
|
4395
4669
|
spec: this.spec,
|
|
4396
4670
|
model,
|
|
4397
|
-
toolHandlers: this.tools
|
|
4398
|
-
|
|
4399
|
-
return await agent.generate({
|
|
4400
|
-
prompt: message,
|
|
4401
|
-
options
|
|
4671
|
+
toolHandlers: this.tools,
|
|
4672
|
+
mcpServers: backendConfig?.mcpServers
|
|
4402
4673
|
});
|
|
4674
|
+
try {
|
|
4675
|
+
return await agent.generate({
|
|
4676
|
+
prompt: message,
|
|
4677
|
+
options
|
|
4678
|
+
});
|
|
4679
|
+
} finally {
|
|
4680
|
+
await agent.cleanup().catch(() => {
|
|
4681
|
+
return;
|
|
4682
|
+
});
|
|
4683
|
+
}
|
|
4403
4684
|
}
|
|
4404
4685
|
async runWithExternalProvider(message, options) {
|
|
4405
4686
|
if (!this.provider || !this.context) {
|
|
@@ -4507,6 +4788,7 @@ class UnifiedAgent {
|
|
|
4507
4788
|
if (backend === this.state.backend) {
|
|
4508
4789
|
return;
|
|
4509
4790
|
}
|
|
4791
|
+
await this.cleanupProviderContext();
|
|
4510
4792
|
this.state.backend = backend;
|
|
4511
4793
|
this.state.isReady = false;
|
|
4512
4794
|
this.provider = undefined;
|
|
@@ -4517,8 +4799,20 @@ class UnifiedAgent {
|
|
|
4517
4799
|
this.state.messageCount = 0;
|
|
4518
4800
|
this.state.sessionId = undefined;
|
|
4519
4801
|
this.state.lastError = undefined;
|
|
4802
|
+
this.cleanupProviderContext().catch(() => {
|
|
4803
|
+
return;
|
|
4804
|
+
});
|
|
4520
4805
|
this.context = undefined;
|
|
4521
4806
|
}
|
|
4807
|
+
async cleanupProviderContext() {
|
|
4808
|
+
const context = this.context;
|
|
4809
|
+
this.context = undefined;
|
|
4810
|
+
if (context) {
|
|
4811
|
+
await context.cleanup().catch(() => {
|
|
4812
|
+
return;
|
|
4813
|
+
});
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4522
4816
|
addTool(name, handler) {
|
|
4523
4817
|
this.tools.set(name, handler);
|
|
4524
4818
|
}
|
|
@@ -4538,7 +4832,8 @@ function createAISDKAgent(spec, options) {
|
|
|
4538
4832
|
modelInstance: options?.modelInstance,
|
|
4539
4833
|
provider: options?.provider,
|
|
4540
4834
|
temperature: options?.temperature,
|
|
4541
|
-
maxTokens: options?.maxTokens
|
|
4835
|
+
maxTokens: options?.maxTokens,
|
|
4836
|
+
mcpServers: options?.mcpServers
|
|
4542
4837
|
}
|
|
4543
4838
|
});
|
|
4544
4839
|
}
|
|
@@ -46,6 +46,7 @@ export declare class ClaudeAgentExporter implements Exporter<ClaudeAgentExportOp
|
|
|
46
46
|
* Generate CLAUDE.md content for Claude Code CLI integration.
|
|
47
47
|
*/
|
|
48
48
|
private generateClaudeMd;
|
|
49
|
+
private formatMcpServer;
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
52
|
* Export an AgentSpec to Claude Agent SDK format.
|
|
@@ -2366,7 +2366,7 @@ class ClaudeAgentExporter {
|
|
|
2366
2366
|
lines.push(i18n.t("export.mcpServers"));
|
|
2367
2367
|
lines.push("");
|
|
2368
2368
|
for (const server of options.mcpServers) {
|
|
2369
|
-
lines.push(`- **${server.name}**: \`${
|
|
2369
|
+
lines.push(`- **${server.name}**: \`${this.formatMcpServer(server)}\``);
|
|
2370
2370
|
}
|
|
2371
2371
|
lines.push("");
|
|
2372
2372
|
}
|
|
@@ -2376,6 +2376,16 @@ class ClaudeAgentExporter {
|
|
|
2376
2376
|
return lines.join(`
|
|
2377
2377
|
`);
|
|
2378
2378
|
}
|
|
2379
|
+
formatMcpServer(server) {
|
|
2380
|
+
if ("command" in server) {
|
|
2381
|
+
const args = server.args ? ` ${server.args.join(" ")}` : "";
|
|
2382
|
+
return `${server.command}${args}`;
|
|
2383
|
+
}
|
|
2384
|
+
if (server.transport === "http" || server.transport === "sse") {
|
|
2385
|
+
return `${server.transport} ${server.url}`;
|
|
2386
|
+
}
|
|
2387
|
+
return server.url;
|
|
2388
|
+
}
|
|
2379
2389
|
}
|
|
2380
2390
|
function exportToClaudeAgent(spec, options) {
|
|
2381
2391
|
const exporter = new ClaudeAgentExporter;
|
package/dist/exporters/index.js
CHANGED
|
@@ -2652,7 +2652,7 @@ class ClaudeAgentExporter {
|
|
|
2652
2652
|
lines.push(i18n.t("export.mcpServers"));
|
|
2653
2653
|
lines.push("");
|
|
2654
2654
|
for (const server of options.mcpServers) {
|
|
2655
|
-
lines.push(`- **${server.name}**: \`${
|
|
2655
|
+
lines.push(`- **${server.name}**: \`${this.formatMcpServer(server)}\``);
|
|
2656
2656
|
}
|
|
2657
2657
|
lines.push("");
|
|
2658
2658
|
}
|
|
@@ -2662,6 +2662,16 @@ class ClaudeAgentExporter {
|
|
|
2662
2662
|
return lines.join(`
|
|
2663
2663
|
`);
|
|
2664
2664
|
}
|
|
2665
|
+
formatMcpServer(server) {
|
|
2666
|
+
if ("command" in server) {
|
|
2667
|
+
const args = server.args ? ` ${server.args.join(" ")}` : "";
|
|
2668
|
+
return `${server.command}${args}`;
|
|
2669
|
+
}
|
|
2670
|
+
if (server.transport === "http" || server.transport === "sse") {
|
|
2671
|
+
return `${server.transport} ${server.url}`;
|
|
2672
|
+
}
|
|
2673
|
+
return server.url;
|
|
2674
|
+
}
|
|
2665
2675
|
}
|
|
2666
2676
|
function exportToClaudeAgent(spec, options) {
|
|
2667
2677
|
const exporter = new ClaudeAgentExporter;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentSpec } from '../spec/spec';
|
|
8
8
|
import type { OpenCodeAgentType } from '../providers/types';
|
|
9
|
+
import type { McpClientConfig } from '../tools/mcp-client';
|
|
9
10
|
/**
|
|
10
11
|
* Base export options.
|
|
11
12
|
*/
|
|
@@ -36,11 +37,7 @@ export interface ClaudeAgentExportOptions extends ExportOptions {
|
|
|
36
37
|
/** Enable extended thinking in exported config */
|
|
37
38
|
extendedThinking?: boolean;
|
|
38
39
|
/** MCP servers to include */
|
|
39
|
-
mcpServers?:
|
|
40
|
-
name: string;
|
|
41
|
-
command: string;
|
|
42
|
-
args?: string[];
|
|
43
|
-
}[];
|
|
40
|
+
mcpServers?: McpClientConfig[];
|
|
44
41
|
}
|
|
45
42
|
/**
|
|
46
43
|
* Claude Agent SDK export result.
|
|
@@ -67,11 +64,7 @@ export interface ClaudeAgentConfig {
|
|
|
67
64
|
max_turns?: number;
|
|
68
65
|
computer_use?: boolean;
|
|
69
66
|
extended_thinking?: boolean;
|
|
70
|
-
mcp_servers?:
|
|
71
|
-
name: string;
|
|
72
|
-
command: string;
|
|
73
|
-
args?: string[];
|
|
74
|
-
}[];
|
|
67
|
+
mcp_servers?: McpClientConfig[];
|
|
75
68
|
}
|
|
76
69
|
/**
|
|
77
70
|
* Claude Agent SDK tool definition.
|