@corbat-tech/coco 2.25.9 → 2.25.11
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/cli/index.js +457 -278
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +88 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -121,6 +121,7 @@ function createDefaultConfigObject(projectName, language = "typescript") {
|
|
|
121
121
|
temperature: 0,
|
|
122
122
|
timeout: 12e4
|
|
123
123
|
},
|
|
124
|
+
providerModels: {},
|
|
124
125
|
quality: {
|
|
125
126
|
minScore: 85,
|
|
126
127
|
minCoverage: 80,
|
|
@@ -300,6 +301,7 @@ var init_schema = __esm({
|
|
|
300
301
|
temperature: 0,
|
|
301
302
|
timeout: 12e4
|
|
302
303
|
}),
|
|
304
|
+
providerModels: z.record(z.string(), z.string()).optional(),
|
|
303
305
|
quality: QualityConfigSchema.default({
|
|
304
306
|
minScore: 85,
|
|
305
307
|
minCoverage: 80,
|
|
@@ -597,6 +599,7 @@ function deepMergeConfig(base, override) {
|
|
|
597
599
|
...override,
|
|
598
600
|
project: { ...base.project, ...override.project },
|
|
599
601
|
provider: { ...base.provider, ...override.provider },
|
|
602
|
+
providerModels: { ...base.providerModels, ...override.providerModels },
|
|
600
603
|
quality: { ...base.quality, ...override.quality },
|
|
601
604
|
persistence: { ...base.persistence, ...override.persistence },
|
|
602
605
|
// Merge optional sections only if present in either base or override
|
|
@@ -2565,7 +2568,11 @@ function getDefaultModel(provider) {
|
|
|
2565
2568
|
function normalizeConfiguredModel(model) {
|
|
2566
2569
|
if (typeof model !== "string") return void 0;
|
|
2567
2570
|
const trimmed = model.trim();
|
|
2568
|
-
|
|
2571
|
+
if (trimmed.length === 0) return void 0;
|
|
2572
|
+
if (["default", "none", "null", "undefined"].includes(trimmed.toLowerCase())) {
|
|
2573
|
+
return void 0;
|
|
2574
|
+
}
|
|
2575
|
+
return trimmed;
|
|
2569
2576
|
}
|
|
2570
2577
|
function getDefaultProvider() {
|
|
2571
2578
|
const envProvider = process.env["COCO_PROVIDER"]?.toLowerCase();
|
|
@@ -2588,6 +2595,10 @@ async function getLastUsedProvider() {
|
|
|
2588
2595
|
async function getLastUsedModel(provider) {
|
|
2589
2596
|
try {
|
|
2590
2597
|
const config = await loadConfig(CONFIG_PATHS.config);
|
|
2598
|
+
const perProviderModel = normalizeConfiguredModel(config.providerModels?.[provider]);
|
|
2599
|
+
if (perProviderModel) {
|
|
2600
|
+
return perProviderModel;
|
|
2601
|
+
}
|
|
2591
2602
|
if (config.provider.type === provider) {
|
|
2592
2603
|
return normalizeConfiguredModel(config.provider.model);
|
|
2593
2604
|
}
|
|
@@ -2627,6 +2638,11 @@ async function saveProviderPreference(provider, model) {
|
|
|
2627
2638
|
}
|
|
2628
2639
|
config.provider.type = provider;
|
|
2629
2640
|
const normalizedModel = normalizeConfiguredModel(model);
|
|
2641
|
+
const persistedModel = normalizedModel ?? getDefaultModel(provider);
|
|
2642
|
+
config.providerModels = {
|
|
2643
|
+
...config.providerModels,
|
|
2644
|
+
[provider]: persistedModel
|
|
2645
|
+
};
|
|
2630
2646
|
if (normalizedModel) {
|
|
2631
2647
|
config.provider.model = normalizedModel;
|
|
2632
2648
|
} else {
|
|
@@ -2725,11 +2741,25 @@ async function migrateOldPreferences() {
|
|
|
2725
2741
|
}
|
|
2726
2742
|
if (oldPrefs.provider && VALID_PROVIDERS.includes(oldPrefs.provider)) {
|
|
2727
2743
|
config.provider.type = oldPrefs.provider;
|
|
2744
|
+
config.providerModels = {
|
|
2745
|
+
...config.providerModels
|
|
2746
|
+
};
|
|
2747
|
+
for (const [providerName, modelName] of Object.entries(oldPrefs.models ?? {})) {
|
|
2748
|
+
if (VALID_PROVIDERS.includes(providerName)) {
|
|
2749
|
+
const normalized = normalizeConfiguredModel(modelName);
|
|
2750
|
+
if (normalized) {
|
|
2751
|
+
config.providerModels[providerName] = normalized;
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2728
2755
|
const modelForProvider = oldPrefs.provider ? oldPrefs.models?.[oldPrefs.provider] : void 0;
|
|
2729
|
-
|
|
2730
|
-
|
|
2756
|
+
const normalizedMigratedModel = normalizeConfiguredModel(modelForProvider);
|
|
2757
|
+
if (normalizedMigratedModel) {
|
|
2758
|
+
config.provider.model = normalizedMigratedModel;
|
|
2759
|
+
config.providerModels[oldPrefs.provider] = normalizedMigratedModel;
|
|
2731
2760
|
} else {
|
|
2732
2761
|
config.provider.model = getDefaultModel(oldPrefs.provider);
|
|
2762
|
+
config.providerModels[oldPrefs.provider] = config.provider.model;
|
|
2733
2763
|
}
|
|
2734
2764
|
await saveConfig(config, void 0, true);
|
|
2735
2765
|
}
|
|
@@ -4472,7 +4502,9 @@ var init_openai = __esm({
|
|
|
4472
4502
|
input,
|
|
4473
4503
|
instructions: instructions ?? void 0,
|
|
4474
4504
|
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
4475
|
-
...supportsTemp && {
|
|
4505
|
+
...supportsTemp && {
|
|
4506
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
4507
|
+
},
|
|
4476
4508
|
store: false
|
|
4477
4509
|
});
|
|
4478
4510
|
return {
|
|
@@ -4507,7 +4539,9 @@ var init_openai = __esm({
|
|
|
4507
4539
|
instructions: instructions ?? void 0,
|
|
4508
4540
|
tools,
|
|
4509
4541
|
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
4510
|
-
...supportsTemp && {
|
|
4542
|
+
...supportsTemp && {
|
|
4543
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
4544
|
+
},
|
|
4511
4545
|
store: false
|
|
4512
4546
|
});
|
|
4513
4547
|
let content = "";
|
|
@@ -10235,6 +10269,7 @@ Rules:
|
|
|
10235
10269
|
- If you need real-time data, CALL web_search. NEVER say "I don't have access to real-time data."
|
|
10236
10270
|
- If an MCP tool exists for a service (tool names like \`mcp_<service>_...\`), prefer that MCP tool over generic \`web_fetch\` or \`http_fetch\`.
|
|
10237
10271
|
- Use \`mcp_list_servers\` to inspect configured or connected MCP services. Do NOT use \`bash_exec\` to run \`coco mcp ...\` unless the user explicitly asked for that CLI command.
|
|
10272
|
+
- If the user asks you to use an MCP service and it is configured but disconnected, call \`mcp_connect_server\` first. This built-in flow may open a browser for OAuth. Do NOT ask the user for raw tokens when MCP OAuth is supported.
|
|
10238
10273
|
- Before answering "I can't do that", check your full tool catalog below \u2014 you likely have a tool for it.
|
|
10239
10274
|
- NEVER claim you cannot run a command because you lack credentials, access, or connectivity. bash_exec runs in the user's own shell environment and inherits their full PATH, kubeconfig, gcloud auth, AWS profiles, SSH keys, and every other tool installed on their machine. kubectl, gcloud, aws, docker, and any other CLI available to the user are available to you. ALWAYS attempt the command with bash_exec; report failure only if it actually returns a non-zero exit code.
|
|
10240
10275
|
|
|
@@ -21226,7 +21261,9 @@ function createProtectedMetadataCandidates(resourceUrl, headerUrl) {
|
|
|
21226
21261
|
candidates.push(`${origin}/.well-known/oauth-protected-resource`);
|
|
21227
21262
|
if (pathPart && pathPart !== "/") {
|
|
21228
21263
|
candidates.push(`${origin}/.well-known/oauth-protected-resource${pathPart}`);
|
|
21229
|
-
candidates.push(
|
|
21264
|
+
candidates.push(
|
|
21265
|
+
`${origin}/.well-known/oauth-protected-resource/${pathPart.replace(/^\//, "")}`
|
|
21266
|
+
);
|
|
21230
21267
|
}
|
|
21231
21268
|
return Array.from(new Set(candidates));
|
|
21232
21269
|
}
|
|
@@ -22341,6 +22378,263 @@ var init_full_power_risk_mode = __esm({
|
|
|
22341
22378
|
}
|
|
22342
22379
|
});
|
|
22343
22380
|
|
|
22381
|
+
// src/mcp/tools.ts
|
|
22382
|
+
var tools_exports = {};
|
|
22383
|
+
__export(tools_exports, {
|
|
22384
|
+
createToolsFromMCPServer: () => createToolsFromMCPServer,
|
|
22385
|
+
extractOriginalToolName: () => extractOriginalToolName,
|
|
22386
|
+
getMCPToolInfo: () => getMCPToolInfo,
|
|
22387
|
+
jsonSchemaToZod: () => jsonSchemaToZod,
|
|
22388
|
+
registerMCPTools: () => registerMCPTools,
|
|
22389
|
+
wrapMCPTool: () => wrapMCPTool,
|
|
22390
|
+
wrapMCPTools: () => wrapMCPTools
|
|
22391
|
+
});
|
|
22392
|
+
function buildMcpToolDescription(serverName, tool) {
|
|
22393
|
+
const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
|
|
22394
|
+
const lowerServer = serverName.toLowerCase();
|
|
22395
|
+
if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
|
|
22396
|
+
return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
|
|
22397
|
+
}
|
|
22398
|
+
return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
|
|
22399
|
+
}
|
|
22400
|
+
function jsonSchemaToZod(schema) {
|
|
22401
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
22402
|
+
const values = schema.enum;
|
|
22403
|
+
if (values.length > 0 && values.every((v) => typeof v === "string")) {
|
|
22404
|
+
return z.enum(values);
|
|
22405
|
+
}
|
|
22406
|
+
const literals = values.map((v) => z.literal(v));
|
|
22407
|
+
if (literals.length < 2) {
|
|
22408
|
+
return literals[0] ?? z.any();
|
|
22409
|
+
}
|
|
22410
|
+
return z.union(literals);
|
|
22411
|
+
}
|
|
22412
|
+
if (schema.const !== void 0) {
|
|
22413
|
+
return z.literal(schema.const);
|
|
22414
|
+
}
|
|
22415
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
22416
|
+
const schemas = schema.oneOf.map(jsonSchemaToZod);
|
|
22417
|
+
if (schemas.length >= 2) {
|
|
22418
|
+
return z.union(schemas);
|
|
22419
|
+
}
|
|
22420
|
+
return schemas[0] ?? z.unknown();
|
|
22421
|
+
}
|
|
22422
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
22423
|
+
const schemas = schema.anyOf.map(jsonSchemaToZod);
|
|
22424
|
+
if (schemas.length >= 2) {
|
|
22425
|
+
return z.union(schemas);
|
|
22426
|
+
}
|
|
22427
|
+
return schemas[0] ?? z.unknown();
|
|
22428
|
+
}
|
|
22429
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
22430
|
+
const schemas = schema.allOf.map(jsonSchemaToZod);
|
|
22431
|
+
return schemas.reduce((acc, s) => z.intersection(acc, s));
|
|
22432
|
+
}
|
|
22433
|
+
const type = schema.type;
|
|
22434
|
+
const makeNullable = (s) => {
|
|
22435
|
+
if (schema.nullable === true) return s.nullable();
|
|
22436
|
+
return s;
|
|
22437
|
+
};
|
|
22438
|
+
switch (type) {
|
|
22439
|
+
case "string": {
|
|
22440
|
+
let s = z.string();
|
|
22441
|
+
if (schema.format) {
|
|
22442
|
+
switch (schema.format) {
|
|
22443
|
+
case "uri":
|
|
22444
|
+
case "url":
|
|
22445
|
+
s = z.string().url();
|
|
22446
|
+
break;
|
|
22447
|
+
case "email":
|
|
22448
|
+
s = z.string().email();
|
|
22449
|
+
break;
|
|
22450
|
+
case "date-time":
|
|
22451
|
+
case "datetime":
|
|
22452
|
+
s = z.string().datetime();
|
|
22453
|
+
break;
|
|
22454
|
+
}
|
|
22455
|
+
}
|
|
22456
|
+
if (typeof schema.minLength === "number") s = s.min(schema.minLength);
|
|
22457
|
+
if (typeof schema.maxLength === "number") s = s.max(schema.maxLength);
|
|
22458
|
+
return makeNullable(s);
|
|
22459
|
+
}
|
|
22460
|
+
case "number": {
|
|
22461
|
+
let n = z.number();
|
|
22462
|
+
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22463
|
+
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22464
|
+
if (typeof schema.exclusiveMinimum === "number") n = n.gt(schema.exclusiveMinimum);
|
|
22465
|
+
if (typeof schema.exclusiveMaximum === "number") n = n.lt(schema.exclusiveMaximum);
|
|
22466
|
+
return makeNullable(n);
|
|
22467
|
+
}
|
|
22468
|
+
case "integer": {
|
|
22469
|
+
let n = z.number().int();
|
|
22470
|
+
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22471
|
+
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22472
|
+
return makeNullable(n);
|
|
22473
|
+
}
|
|
22474
|
+
case "boolean":
|
|
22475
|
+
return makeNullable(z.boolean());
|
|
22476
|
+
case "null":
|
|
22477
|
+
return z.null();
|
|
22478
|
+
case "array":
|
|
22479
|
+
if (schema.items) {
|
|
22480
|
+
const itemSchema = jsonSchemaToZod(schema.items);
|
|
22481
|
+
let arr = z.array(itemSchema);
|
|
22482
|
+
if (typeof schema.minItems === "number") arr = arr.min(schema.minItems);
|
|
22483
|
+
if (typeof schema.maxItems === "number") arr = arr.max(schema.maxItems);
|
|
22484
|
+
return makeNullable(arr);
|
|
22485
|
+
}
|
|
22486
|
+
return makeNullable(z.array(z.unknown()));
|
|
22487
|
+
case "object": {
|
|
22488
|
+
const properties = schema.properties;
|
|
22489
|
+
const required = schema.required;
|
|
22490
|
+
if (!properties) {
|
|
22491
|
+
return makeNullable(z.record(z.string(), z.unknown()));
|
|
22492
|
+
}
|
|
22493
|
+
const shape = {};
|
|
22494
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
22495
|
+
let fieldSchema = jsonSchemaToZod(propSchema);
|
|
22496
|
+
if (!required?.includes(key)) {
|
|
22497
|
+
fieldSchema = fieldSchema.optional();
|
|
22498
|
+
}
|
|
22499
|
+
if (propSchema.description && typeof propSchema.description === "string") {
|
|
22500
|
+
fieldSchema = fieldSchema.describe(propSchema.description);
|
|
22501
|
+
}
|
|
22502
|
+
shape[key] = fieldSchema;
|
|
22503
|
+
}
|
|
22504
|
+
return makeNullable(z.object(shape));
|
|
22505
|
+
}
|
|
22506
|
+
default:
|
|
22507
|
+
if (Array.isArray(schema.type)) {
|
|
22508
|
+
const types = schema.type;
|
|
22509
|
+
if (types.includes("null")) {
|
|
22510
|
+
const nonNullType = types.find((t) => t !== "null");
|
|
22511
|
+
if (nonNullType) {
|
|
22512
|
+
return jsonSchemaToZod({ ...schema, type: nonNullType }).nullable();
|
|
22513
|
+
}
|
|
22514
|
+
}
|
|
22515
|
+
}
|
|
22516
|
+
return z.unknown();
|
|
22517
|
+
}
|
|
22518
|
+
}
|
|
22519
|
+
function createToolParametersSchema(tool) {
|
|
22520
|
+
const schema = tool.inputSchema;
|
|
22521
|
+
if (!schema || schema.type !== "object") {
|
|
22522
|
+
return z.object({});
|
|
22523
|
+
}
|
|
22524
|
+
return jsonSchemaToZod(schema);
|
|
22525
|
+
}
|
|
22526
|
+
function formatToolResult(result) {
|
|
22527
|
+
if (result.isError) {
|
|
22528
|
+
throw new Error(result.content.map((c) => c.text || "").join("\n"));
|
|
22529
|
+
}
|
|
22530
|
+
return result.content.map((item) => {
|
|
22531
|
+
switch (item.type) {
|
|
22532
|
+
case "text":
|
|
22533
|
+
return item.text || "";
|
|
22534
|
+
case "image":
|
|
22535
|
+
return `[Image: ${item.mimeType || "unknown"}]`;
|
|
22536
|
+
case "resource":
|
|
22537
|
+
return `[Resource: ${item.resource?.uri || "unknown"}]`;
|
|
22538
|
+
default:
|
|
22539
|
+
return "";
|
|
22540
|
+
}
|
|
22541
|
+
}).filter(Boolean).join("\n");
|
|
22542
|
+
}
|
|
22543
|
+
function createToolName(serverName, toolName, prefix) {
|
|
22544
|
+
return `${prefix}_${serverName}_${toolName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
22545
|
+
}
|
|
22546
|
+
function wrapMCPTool(tool, serverName, client, options = {}) {
|
|
22547
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
22548
|
+
const wrappedName = createToolName(serverName, tool.name, opts.namePrefix);
|
|
22549
|
+
const parametersSchema = createToolParametersSchema(tool);
|
|
22550
|
+
const cocoTool = {
|
|
22551
|
+
name: wrappedName,
|
|
22552
|
+
description: buildMcpToolDescription(serverName, tool),
|
|
22553
|
+
category: opts.category,
|
|
22554
|
+
parameters: parametersSchema,
|
|
22555
|
+
execute: async (params) => {
|
|
22556
|
+
const timeout = opts.requestTimeout;
|
|
22557
|
+
try {
|
|
22558
|
+
const result = await Promise.race([
|
|
22559
|
+
client.callTool({
|
|
22560
|
+
name: tool.name,
|
|
22561
|
+
arguments: params
|
|
22562
|
+
}),
|
|
22563
|
+
new Promise((_, reject) => {
|
|
22564
|
+
setTimeout(() => {
|
|
22565
|
+
reject(new MCPTimeoutError(`Tool '${tool.name}' timed out after ${timeout}ms`));
|
|
22566
|
+
}, timeout);
|
|
22567
|
+
})
|
|
22568
|
+
]);
|
|
22569
|
+
return formatToolResult(result);
|
|
22570
|
+
} catch (error) {
|
|
22571
|
+
if (error instanceof MCPError) {
|
|
22572
|
+
throw error;
|
|
22573
|
+
}
|
|
22574
|
+
throw new MCPError(
|
|
22575
|
+
-32603,
|
|
22576
|
+
`Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
22577
|
+
);
|
|
22578
|
+
}
|
|
22579
|
+
}
|
|
22580
|
+
};
|
|
22581
|
+
const wrapped = {
|
|
22582
|
+
originalTool: tool,
|
|
22583
|
+
serverName,
|
|
22584
|
+
wrappedName
|
|
22585
|
+
};
|
|
22586
|
+
return { tool: cocoTool, wrapped };
|
|
22587
|
+
}
|
|
22588
|
+
function wrapMCPTools(tools, serverName, client, options = {}) {
|
|
22589
|
+
const cocoTools = [];
|
|
22590
|
+
const wrappedTools = [];
|
|
22591
|
+
for (const tool of tools) {
|
|
22592
|
+
const { tool: cocoTool, wrapped } = wrapMCPTool(tool, serverName, client, options);
|
|
22593
|
+
cocoTools.push(cocoTool);
|
|
22594
|
+
wrappedTools.push(wrapped);
|
|
22595
|
+
}
|
|
22596
|
+
return { tools: cocoTools, wrapped: wrappedTools };
|
|
22597
|
+
}
|
|
22598
|
+
async function createToolsFromMCPServer(serverName, client, options = {}) {
|
|
22599
|
+
if (!client.isConnected()) {
|
|
22600
|
+
await client.initialize({
|
|
22601
|
+
protocolVersion: "2024-11-05",
|
|
22602
|
+
capabilities: {},
|
|
22603
|
+
clientInfo: { name: "coco-mcp-client", version: "0.2.0" }
|
|
22604
|
+
});
|
|
22605
|
+
}
|
|
22606
|
+
const { tools } = await client.listTools();
|
|
22607
|
+
return wrapMCPTools(tools, serverName, client, options);
|
|
22608
|
+
}
|
|
22609
|
+
async function registerMCPTools(registry, serverName, client, options = {}) {
|
|
22610
|
+
const { tools, wrapped } = await createToolsFromMCPServer(serverName, client, options);
|
|
22611
|
+
for (const tool of tools) {
|
|
22612
|
+
registry.register(tool);
|
|
22613
|
+
}
|
|
22614
|
+
return wrapped;
|
|
22615
|
+
}
|
|
22616
|
+
function getMCPToolInfo(wrappedName, wrappedTools) {
|
|
22617
|
+
return wrappedTools.find((t) => t.wrappedName === wrappedName);
|
|
22618
|
+
}
|
|
22619
|
+
function extractOriginalToolName(wrappedName, serverName, prefix = "mcp") {
|
|
22620
|
+
const prefix_pattern = `${prefix}_${serverName}_`;
|
|
22621
|
+
if (wrappedName.startsWith(prefix_pattern)) {
|
|
22622
|
+
return wrappedName.slice(prefix_pattern.length);
|
|
22623
|
+
}
|
|
22624
|
+
return null;
|
|
22625
|
+
}
|
|
22626
|
+
var DEFAULT_OPTIONS2;
|
|
22627
|
+
var init_tools = __esm({
|
|
22628
|
+
"src/mcp/tools.ts"() {
|
|
22629
|
+
init_errors2();
|
|
22630
|
+
DEFAULT_OPTIONS2 = {
|
|
22631
|
+
namePrefix: "mcp",
|
|
22632
|
+
category: "deploy",
|
|
22633
|
+
requestTimeout: 6e4
|
|
22634
|
+
};
|
|
22635
|
+
}
|
|
22636
|
+
});
|
|
22637
|
+
|
|
22344
22638
|
// src/cli/repl/allow-path-prompt.ts
|
|
22345
22639
|
var allow_path_prompt_exports = {};
|
|
22346
22640
|
__export(allow_path_prompt_exports, {
|
|
@@ -22789,263 +23083,6 @@ var init_stack_detector = __esm({
|
|
|
22789
23083
|
}
|
|
22790
23084
|
});
|
|
22791
23085
|
|
|
22792
|
-
// src/mcp/tools.ts
|
|
22793
|
-
var tools_exports = {};
|
|
22794
|
-
__export(tools_exports, {
|
|
22795
|
-
createToolsFromMCPServer: () => createToolsFromMCPServer,
|
|
22796
|
-
extractOriginalToolName: () => extractOriginalToolName,
|
|
22797
|
-
getMCPToolInfo: () => getMCPToolInfo,
|
|
22798
|
-
jsonSchemaToZod: () => jsonSchemaToZod,
|
|
22799
|
-
registerMCPTools: () => registerMCPTools,
|
|
22800
|
-
wrapMCPTool: () => wrapMCPTool,
|
|
22801
|
-
wrapMCPTools: () => wrapMCPTools
|
|
22802
|
-
});
|
|
22803
|
-
function buildMcpToolDescription(serverName, tool) {
|
|
22804
|
-
const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
|
|
22805
|
-
const lowerServer = serverName.toLowerCase();
|
|
22806
|
-
if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
|
|
22807
|
-
return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
|
|
22808
|
-
}
|
|
22809
|
-
return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
|
|
22810
|
-
}
|
|
22811
|
-
function jsonSchemaToZod(schema) {
|
|
22812
|
-
if (schema.enum && Array.isArray(schema.enum)) {
|
|
22813
|
-
const values = schema.enum;
|
|
22814
|
-
if (values.length > 0 && values.every((v) => typeof v === "string")) {
|
|
22815
|
-
return z.enum(values);
|
|
22816
|
-
}
|
|
22817
|
-
const literals = values.map((v) => z.literal(v));
|
|
22818
|
-
if (literals.length < 2) {
|
|
22819
|
-
return literals[0] ?? z.any();
|
|
22820
|
-
}
|
|
22821
|
-
return z.union(literals);
|
|
22822
|
-
}
|
|
22823
|
-
if (schema.const !== void 0) {
|
|
22824
|
-
return z.literal(schema.const);
|
|
22825
|
-
}
|
|
22826
|
-
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
22827
|
-
const schemas = schema.oneOf.map(jsonSchemaToZod);
|
|
22828
|
-
if (schemas.length >= 2) {
|
|
22829
|
-
return z.union(schemas);
|
|
22830
|
-
}
|
|
22831
|
-
return schemas[0] ?? z.unknown();
|
|
22832
|
-
}
|
|
22833
|
-
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
22834
|
-
const schemas = schema.anyOf.map(jsonSchemaToZod);
|
|
22835
|
-
if (schemas.length >= 2) {
|
|
22836
|
-
return z.union(schemas);
|
|
22837
|
-
}
|
|
22838
|
-
return schemas[0] ?? z.unknown();
|
|
22839
|
-
}
|
|
22840
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
22841
|
-
const schemas = schema.allOf.map(jsonSchemaToZod);
|
|
22842
|
-
return schemas.reduce((acc, s) => z.intersection(acc, s));
|
|
22843
|
-
}
|
|
22844
|
-
const type = schema.type;
|
|
22845
|
-
const makeNullable = (s) => {
|
|
22846
|
-
if (schema.nullable === true) return s.nullable();
|
|
22847
|
-
return s;
|
|
22848
|
-
};
|
|
22849
|
-
switch (type) {
|
|
22850
|
-
case "string": {
|
|
22851
|
-
let s = z.string();
|
|
22852
|
-
if (schema.format) {
|
|
22853
|
-
switch (schema.format) {
|
|
22854
|
-
case "uri":
|
|
22855
|
-
case "url":
|
|
22856
|
-
s = z.string().url();
|
|
22857
|
-
break;
|
|
22858
|
-
case "email":
|
|
22859
|
-
s = z.string().email();
|
|
22860
|
-
break;
|
|
22861
|
-
case "date-time":
|
|
22862
|
-
case "datetime":
|
|
22863
|
-
s = z.string().datetime();
|
|
22864
|
-
break;
|
|
22865
|
-
}
|
|
22866
|
-
}
|
|
22867
|
-
if (typeof schema.minLength === "number") s = s.min(schema.minLength);
|
|
22868
|
-
if (typeof schema.maxLength === "number") s = s.max(schema.maxLength);
|
|
22869
|
-
return makeNullable(s);
|
|
22870
|
-
}
|
|
22871
|
-
case "number": {
|
|
22872
|
-
let n = z.number();
|
|
22873
|
-
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22874
|
-
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22875
|
-
if (typeof schema.exclusiveMinimum === "number") n = n.gt(schema.exclusiveMinimum);
|
|
22876
|
-
if (typeof schema.exclusiveMaximum === "number") n = n.lt(schema.exclusiveMaximum);
|
|
22877
|
-
return makeNullable(n);
|
|
22878
|
-
}
|
|
22879
|
-
case "integer": {
|
|
22880
|
-
let n = z.number().int();
|
|
22881
|
-
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22882
|
-
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22883
|
-
return makeNullable(n);
|
|
22884
|
-
}
|
|
22885
|
-
case "boolean":
|
|
22886
|
-
return makeNullable(z.boolean());
|
|
22887
|
-
case "null":
|
|
22888
|
-
return z.null();
|
|
22889
|
-
case "array":
|
|
22890
|
-
if (schema.items) {
|
|
22891
|
-
const itemSchema = jsonSchemaToZod(schema.items);
|
|
22892
|
-
let arr = z.array(itemSchema);
|
|
22893
|
-
if (typeof schema.minItems === "number") arr = arr.min(schema.minItems);
|
|
22894
|
-
if (typeof schema.maxItems === "number") arr = arr.max(schema.maxItems);
|
|
22895
|
-
return makeNullable(arr);
|
|
22896
|
-
}
|
|
22897
|
-
return makeNullable(z.array(z.unknown()));
|
|
22898
|
-
case "object": {
|
|
22899
|
-
const properties = schema.properties;
|
|
22900
|
-
const required = schema.required;
|
|
22901
|
-
if (!properties) {
|
|
22902
|
-
return makeNullable(z.record(z.string(), z.unknown()));
|
|
22903
|
-
}
|
|
22904
|
-
const shape = {};
|
|
22905
|
-
for (const [key, propSchema] of Object.entries(properties)) {
|
|
22906
|
-
let fieldSchema = jsonSchemaToZod(propSchema);
|
|
22907
|
-
if (!required?.includes(key)) {
|
|
22908
|
-
fieldSchema = fieldSchema.optional();
|
|
22909
|
-
}
|
|
22910
|
-
if (propSchema.description && typeof propSchema.description === "string") {
|
|
22911
|
-
fieldSchema = fieldSchema.describe(propSchema.description);
|
|
22912
|
-
}
|
|
22913
|
-
shape[key] = fieldSchema;
|
|
22914
|
-
}
|
|
22915
|
-
return makeNullable(z.object(shape));
|
|
22916
|
-
}
|
|
22917
|
-
default:
|
|
22918
|
-
if (Array.isArray(schema.type)) {
|
|
22919
|
-
const types = schema.type;
|
|
22920
|
-
if (types.includes("null")) {
|
|
22921
|
-
const nonNullType = types.find((t) => t !== "null");
|
|
22922
|
-
if (nonNullType) {
|
|
22923
|
-
return jsonSchemaToZod({ ...schema, type: nonNullType }).nullable();
|
|
22924
|
-
}
|
|
22925
|
-
}
|
|
22926
|
-
}
|
|
22927
|
-
return z.unknown();
|
|
22928
|
-
}
|
|
22929
|
-
}
|
|
22930
|
-
function createToolParametersSchema(tool) {
|
|
22931
|
-
const schema = tool.inputSchema;
|
|
22932
|
-
if (!schema || schema.type !== "object") {
|
|
22933
|
-
return z.object({});
|
|
22934
|
-
}
|
|
22935
|
-
return jsonSchemaToZod(schema);
|
|
22936
|
-
}
|
|
22937
|
-
function formatToolResult(result) {
|
|
22938
|
-
if (result.isError) {
|
|
22939
|
-
throw new Error(result.content.map((c) => c.text || "").join("\n"));
|
|
22940
|
-
}
|
|
22941
|
-
return result.content.map((item) => {
|
|
22942
|
-
switch (item.type) {
|
|
22943
|
-
case "text":
|
|
22944
|
-
return item.text || "";
|
|
22945
|
-
case "image":
|
|
22946
|
-
return `[Image: ${item.mimeType || "unknown"}]`;
|
|
22947
|
-
case "resource":
|
|
22948
|
-
return `[Resource: ${item.resource?.uri || "unknown"}]`;
|
|
22949
|
-
default:
|
|
22950
|
-
return "";
|
|
22951
|
-
}
|
|
22952
|
-
}).filter(Boolean).join("\n");
|
|
22953
|
-
}
|
|
22954
|
-
function createToolName(serverName, toolName, prefix) {
|
|
22955
|
-
return `${prefix}_${serverName}_${toolName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
22956
|
-
}
|
|
22957
|
-
function wrapMCPTool(tool, serverName, client, options = {}) {
|
|
22958
|
-
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
22959
|
-
const wrappedName = createToolName(serverName, tool.name, opts.namePrefix);
|
|
22960
|
-
const parametersSchema = createToolParametersSchema(tool);
|
|
22961
|
-
const cocoTool = {
|
|
22962
|
-
name: wrappedName,
|
|
22963
|
-
description: buildMcpToolDescription(serverName, tool),
|
|
22964
|
-
category: opts.category,
|
|
22965
|
-
parameters: parametersSchema,
|
|
22966
|
-
execute: async (params) => {
|
|
22967
|
-
const timeout = opts.requestTimeout;
|
|
22968
|
-
try {
|
|
22969
|
-
const result = await Promise.race([
|
|
22970
|
-
client.callTool({
|
|
22971
|
-
name: tool.name,
|
|
22972
|
-
arguments: params
|
|
22973
|
-
}),
|
|
22974
|
-
new Promise((_, reject) => {
|
|
22975
|
-
setTimeout(() => {
|
|
22976
|
-
reject(new MCPTimeoutError(`Tool '${tool.name}' timed out after ${timeout}ms`));
|
|
22977
|
-
}, timeout);
|
|
22978
|
-
})
|
|
22979
|
-
]);
|
|
22980
|
-
return formatToolResult(result);
|
|
22981
|
-
} catch (error) {
|
|
22982
|
-
if (error instanceof MCPError) {
|
|
22983
|
-
throw error;
|
|
22984
|
-
}
|
|
22985
|
-
throw new MCPError(
|
|
22986
|
-
-32603,
|
|
22987
|
-
`Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
22988
|
-
);
|
|
22989
|
-
}
|
|
22990
|
-
}
|
|
22991
|
-
};
|
|
22992
|
-
const wrapped = {
|
|
22993
|
-
originalTool: tool,
|
|
22994
|
-
serverName,
|
|
22995
|
-
wrappedName
|
|
22996
|
-
};
|
|
22997
|
-
return { tool: cocoTool, wrapped };
|
|
22998
|
-
}
|
|
22999
|
-
function wrapMCPTools(tools, serverName, client, options = {}) {
|
|
23000
|
-
const cocoTools = [];
|
|
23001
|
-
const wrappedTools = [];
|
|
23002
|
-
for (const tool of tools) {
|
|
23003
|
-
const { tool: cocoTool, wrapped } = wrapMCPTool(tool, serverName, client, options);
|
|
23004
|
-
cocoTools.push(cocoTool);
|
|
23005
|
-
wrappedTools.push(wrapped);
|
|
23006
|
-
}
|
|
23007
|
-
return { tools: cocoTools, wrapped: wrappedTools };
|
|
23008
|
-
}
|
|
23009
|
-
async function createToolsFromMCPServer(serverName, client, options = {}) {
|
|
23010
|
-
if (!client.isConnected()) {
|
|
23011
|
-
await client.initialize({
|
|
23012
|
-
protocolVersion: "2024-11-05",
|
|
23013
|
-
capabilities: {},
|
|
23014
|
-
clientInfo: { name: "coco-mcp-client", version: "0.2.0" }
|
|
23015
|
-
});
|
|
23016
|
-
}
|
|
23017
|
-
const { tools } = await client.listTools();
|
|
23018
|
-
return wrapMCPTools(tools, serverName, client, options);
|
|
23019
|
-
}
|
|
23020
|
-
async function registerMCPTools(registry, serverName, client, options = {}) {
|
|
23021
|
-
const { tools, wrapped } = await createToolsFromMCPServer(serverName, client, options);
|
|
23022
|
-
for (const tool of tools) {
|
|
23023
|
-
registry.register(tool);
|
|
23024
|
-
}
|
|
23025
|
-
return wrapped;
|
|
23026
|
-
}
|
|
23027
|
-
function getMCPToolInfo(wrappedName, wrappedTools) {
|
|
23028
|
-
return wrappedTools.find((t) => t.wrappedName === wrappedName);
|
|
23029
|
-
}
|
|
23030
|
-
function extractOriginalToolName(wrappedName, serverName, prefix = "mcp") {
|
|
23031
|
-
const prefix_pattern = `${prefix}_${serverName}_`;
|
|
23032
|
-
if (wrappedName.startsWith(prefix_pattern)) {
|
|
23033
|
-
return wrappedName.slice(prefix_pattern.length);
|
|
23034
|
-
}
|
|
23035
|
-
return null;
|
|
23036
|
-
}
|
|
23037
|
-
var DEFAULT_OPTIONS2;
|
|
23038
|
-
var init_tools = __esm({
|
|
23039
|
-
"src/mcp/tools.ts"() {
|
|
23040
|
-
init_errors2();
|
|
23041
|
-
DEFAULT_OPTIONS2 = {
|
|
23042
|
-
namePrefix: "mcp",
|
|
23043
|
-
category: "deploy",
|
|
23044
|
-
requestTimeout: 6e4
|
|
23045
|
-
};
|
|
23046
|
-
}
|
|
23047
|
-
});
|
|
23048
|
-
|
|
23049
23086
|
// src/cli/repl/hooks/types.ts
|
|
23050
23087
|
function isHookEvent(value) {
|
|
23051
23088
|
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
@@ -34614,8 +34651,9 @@ ${newProvider.emoji} ${newProvider.name} is not configured.`));
|
|
|
34614
34651
|
newApiKeyForSaving = key;
|
|
34615
34652
|
}
|
|
34616
34653
|
}
|
|
34654
|
+
const rememberedModel = await getLastUsedModel(newProvider.id);
|
|
34617
34655
|
const recommendedModel = getRecommendedModel(newProvider.id);
|
|
34618
|
-
const newModel = recommendedModel?.id || newProvider.models[0]?.id || "";
|
|
34656
|
+
const newModel = rememberedModel || recommendedModel?.id || newProvider.models[0]?.id || "";
|
|
34619
34657
|
const spinner18 = p26.spinner();
|
|
34620
34658
|
spinner18.start(`Connecting to ${newProvider.name}...`);
|
|
34621
34659
|
try {
|
|
@@ -38270,6 +38308,9 @@ var RECOMMENDED_DENY = [
|
|
|
38270
38308
|
"bash:eval",
|
|
38271
38309
|
"bash:source"
|
|
38272
38310
|
];
|
|
38311
|
+
function getProjectPreferenceKey(projectPath) {
|
|
38312
|
+
return path39__default.resolve(projectPath);
|
|
38313
|
+
}
|
|
38273
38314
|
async function loadPermissionPreferences() {
|
|
38274
38315
|
try {
|
|
38275
38316
|
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
@@ -38277,7 +38318,8 @@ async function loadPermissionPreferences() {
|
|
|
38277
38318
|
return {
|
|
38278
38319
|
recommendedAllowlistApplied: config.recommendedAllowlistApplied,
|
|
38279
38320
|
recommendedAllowlistDismissed: config.recommendedAllowlistDismissed,
|
|
38280
|
-
recommendedAllowlistPrompted: config.recommendedAllowlistPrompted
|
|
38321
|
+
recommendedAllowlistPrompted: config.recommendedAllowlistPrompted,
|
|
38322
|
+
recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects
|
|
38281
38323
|
};
|
|
38282
38324
|
} catch {
|
|
38283
38325
|
return {};
|
|
@@ -38297,7 +38339,26 @@ async function savePermissionPreference(key, value) {
|
|
|
38297
38339
|
} catch {
|
|
38298
38340
|
}
|
|
38299
38341
|
}
|
|
38300
|
-
async function
|
|
38342
|
+
async function markPermissionSuggestionShownForProject(projectPath) {
|
|
38343
|
+
try {
|
|
38344
|
+
let config = {};
|
|
38345
|
+
try {
|
|
38346
|
+
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
38347
|
+
config = JSON.parse(content);
|
|
38348
|
+
} catch {
|
|
38349
|
+
}
|
|
38350
|
+
const promptedProjects = {
|
|
38351
|
+
...config.recommendedAllowlistPromptedProjects,
|
|
38352
|
+
[getProjectPreferenceKey(projectPath)]: true
|
|
38353
|
+
};
|
|
38354
|
+
config.recommendedAllowlistPromptedProjects = promptedProjects;
|
|
38355
|
+
config.recommendedAllowlistPrompted = true;
|
|
38356
|
+
await fs35__default.mkdir(path39__default.dirname(CONFIG_PATHS.config), { recursive: true });
|
|
38357
|
+
await fs35__default.writeFile(CONFIG_PATHS.config, JSON.stringify(config, null, 2), "utf-8");
|
|
38358
|
+
} catch {
|
|
38359
|
+
}
|
|
38360
|
+
}
|
|
38361
|
+
async function shouldShowPermissionSuggestion(projectPath = process.cwd()) {
|
|
38301
38362
|
const prefs = await loadPermissionPreferences();
|
|
38302
38363
|
if (prefs.recommendedAllowlistDismissed) {
|
|
38303
38364
|
return false;
|
|
@@ -38305,6 +38366,10 @@ async function shouldShowPermissionSuggestion() {
|
|
|
38305
38366
|
if (prefs.recommendedAllowlistApplied) {
|
|
38306
38367
|
return false;
|
|
38307
38368
|
}
|
|
38369
|
+
const projectKey = getProjectPreferenceKey(projectPath);
|
|
38370
|
+
if (prefs.recommendedAllowlistPromptedProjects?.[projectKey]) {
|
|
38371
|
+
return false;
|
|
38372
|
+
}
|
|
38308
38373
|
return true;
|
|
38309
38374
|
}
|
|
38310
38375
|
async function applyRecommendedPermissions() {
|
|
@@ -38313,7 +38378,8 @@ async function applyRecommendedPermissions() {
|
|
|
38313
38378
|
}
|
|
38314
38379
|
await savePermissionPreference("recommendedAllowlistApplied", true);
|
|
38315
38380
|
}
|
|
38316
|
-
async function showPermissionSuggestion() {
|
|
38381
|
+
async function showPermissionSuggestion(projectPath = process.cwd()) {
|
|
38382
|
+
await markPermissionSuggestionShownForProject(projectPath);
|
|
38317
38383
|
console.log();
|
|
38318
38384
|
console.log(chalk.magenta.bold(" \u{1F4CB} Recommended Permissions"));
|
|
38319
38385
|
console.log();
|
|
@@ -47369,6 +47435,27 @@ init_registry4();
|
|
|
47369
47435
|
init_registry();
|
|
47370
47436
|
init_config_loader();
|
|
47371
47437
|
init_lifecycle();
|
|
47438
|
+
init_tools();
|
|
47439
|
+
async function loadConfiguredServers(projectPath) {
|
|
47440
|
+
const registry = new MCPRegistryImpl();
|
|
47441
|
+
await registry.load();
|
|
47442
|
+
const resolvedProjectPath = projectPath || process.cwd();
|
|
47443
|
+
return mergeMCPConfigs(
|
|
47444
|
+
registry.listServers(),
|
|
47445
|
+
await loadMCPServersFromCOCOConfig(),
|
|
47446
|
+
await loadProjectMCPFile(resolvedProjectPath)
|
|
47447
|
+
);
|
|
47448
|
+
}
|
|
47449
|
+
function findConfiguredServer(servers, requestedServer) {
|
|
47450
|
+
const normalized = requestedServer.trim().toLowerCase();
|
|
47451
|
+
return servers.find((server) => {
|
|
47452
|
+
const name = server.name.toLowerCase();
|
|
47453
|
+
if (name === normalized) return true;
|
|
47454
|
+
if (name.includes(normalized) || normalized.includes(name)) return true;
|
|
47455
|
+
if (name.includes("atlassian") && /^(atlassian|jira|confluence)$/.test(normalized)) return true;
|
|
47456
|
+
return false;
|
|
47457
|
+
});
|
|
47458
|
+
}
|
|
47372
47459
|
var mcpListServersTool = defineTool({
|
|
47373
47460
|
name: "mcp_list_servers",
|
|
47374
47461
|
description: `Inspect Coco's MCP configuration and current runtime connections.
|
|
@@ -47382,14 +47469,9 @@ when you need to know which MCP servers are configured, connected, healthy, or w
|
|
|
47382
47469
|
projectPath: z.string().optional().describe("Project path whose .mcp.json should be merged. Defaults to process.cwd()")
|
|
47383
47470
|
}),
|
|
47384
47471
|
async execute({ includeDisabled, includeTools, projectPath }) {
|
|
47385
|
-
const
|
|
47386
|
-
|
|
47387
|
-
|
|
47388
|
-
const configuredServers = mergeMCPConfigs(
|
|
47389
|
-
registry.listServers(),
|
|
47390
|
-
await loadMCPServersFromCOCOConfig(),
|
|
47391
|
-
await loadProjectMCPFile(resolvedProjectPath)
|
|
47392
|
-
).filter((server) => includeDisabled || server.enabled !== false);
|
|
47472
|
+
const configuredServers = (await loadConfiguredServers(projectPath)).filter(
|
|
47473
|
+
(server) => includeDisabled || server.enabled !== false
|
|
47474
|
+
);
|
|
47393
47475
|
const manager = getMCPServerManager();
|
|
47394
47476
|
const servers = [];
|
|
47395
47477
|
for (const server of configuredServers) {
|
|
@@ -47420,7 +47502,59 @@ when you need to know which MCP servers are configured, connected, healthy, or w
|
|
|
47420
47502
|
};
|
|
47421
47503
|
}
|
|
47422
47504
|
});
|
|
47423
|
-
var
|
|
47505
|
+
var mcpConnectServerTool = defineTool({
|
|
47506
|
+
name: "mcp_connect_server",
|
|
47507
|
+
description: `Connect or reconnect a configured MCP server in the current Coco session.
|
|
47508
|
+
|
|
47509
|
+
Use this when mcp_list_servers shows a service as configured but disconnected, or when
|
|
47510
|
+
the user explicitly asks you to use a specific MCP service. This tool can trigger the
|
|
47511
|
+
built-in MCP OAuth browser login flow. Do not ask the user for raw tokens when this exists.`,
|
|
47512
|
+
category: "config",
|
|
47513
|
+
parameters: z.object({
|
|
47514
|
+
server: z.string().describe("Configured MCP server name, or a common alias like 'jira' or 'atlassian'"),
|
|
47515
|
+
includeTools: z.boolean().optional().default(true).describe("Include discovered MCP tool names after connecting"),
|
|
47516
|
+
projectPath: z.string().optional().describe("Project path whose .mcp.json should be merged. Defaults to process.cwd()")
|
|
47517
|
+
}),
|
|
47518
|
+
async execute({ server, includeTools, projectPath }) {
|
|
47519
|
+
const configuredServers = await loadConfiguredServers(projectPath);
|
|
47520
|
+
const target = findConfiguredServer(
|
|
47521
|
+
configuredServers.filter((configuredServer) => configuredServer.enabled !== false),
|
|
47522
|
+
server
|
|
47523
|
+
);
|
|
47524
|
+
if (!target) {
|
|
47525
|
+
throw new Error(`MCP server '${server}' is not configured`);
|
|
47526
|
+
}
|
|
47527
|
+
const manager = getMCPServerManager();
|
|
47528
|
+
const existingConnection = manager.getConnection(target.name);
|
|
47529
|
+
if (existingConnection && existingConnection.healthy === false) {
|
|
47530
|
+
await manager.stopServer(target.name);
|
|
47531
|
+
}
|
|
47532
|
+
const connection = await manager.startServer(target);
|
|
47533
|
+
const toolRegistry = getAgentToolRegistry();
|
|
47534
|
+
if (toolRegistry) {
|
|
47535
|
+
await registerMCPTools(toolRegistry, connection.name, connection.client);
|
|
47536
|
+
}
|
|
47537
|
+
let tools;
|
|
47538
|
+
if (includeTools) {
|
|
47539
|
+
try {
|
|
47540
|
+
const listed = await connection.client.listTools();
|
|
47541
|
+
tools = listed.tools.map((tool) => tool.name);
|
|
47542
|
+
} catch {
|
|
47543
|
+
tools = [];
|
|
47544
|
+
}
|
|
47545
|
+
}
|
|
47546
|
+
return {
|
|
47547
|
+
requestedServer: server,
|
|
47548
|
+
connected: true,
|
|
47549
|
+
healthy: true,
|
|
47550
|
+
toolCount: connection.toolCount,
|
|
47551
|
+
...includeTools ? { tools: tools ?? [] } : {},
|
|
47552
|
+
authTriggered: target.transport === "http",
|
|
47553
|
+
message: `MCP server '${target.name}' is connected for this session.`
|
|
47554
|
+
};
|
|
47555
|
+
}
|
|
47556
|
+
});
|
|
47557
|
+
var mcpTools = [mcpListServersTool, mcpConnectServerTool];
|
|
47424
47558
|
|
|
47425
47559
|
// src/tools/index.ts
|
|
47426
47560
|
init_registry4();
|
|
@@ -53037,8 +53171,8 @@ async function startRepl(options = {}) {
|
|
|
53037
53171
|
]);
|
|
53038
53172
|
session.projectContext = projectContext;
|
|
53039
53173
|
await initializeSessionMemory(session);
|
|
53040
|
-
if (await shouldShowPermissionSuggestion()) {
|
|
53041
|
-
await showPermissionSuggestion();
|
|
53174
|
+
if (await shouldShowPermissionSuggestion(projectPath)) {
|
|
53175
|
+
await showPermissionSuggestion(projectPath);
|
|
53042
53176
|
const updatedTrust = await loadTrustedTools(projectPath);
|
|
53043
53177
|
for (const tool of updatedTrust) {
|
|
53044
53178
|
session.trustedTools.add(tool);
|
|
@@ -53074,6 +53208,8 @@ async function startRepl(options = {}) {
|
|
|
53074
53208
|
);
|
|
53075
53209
|
}
|
|
53076
53210
|
let mcpManager = null;
|
|
53211
|
+
let configuredMcpServers = [];
|
|
53212
|
+
const registeredMcpServers = /* @__PURE__ */ new Set();
|
|
53077
53213
|
const logger2 = (await Promise.resolve().then(() => (init_logger(), logger_exports))).getLogger();
|
|
53078
53214
|
try {
|
|
53079
53215
|
const { getMCPServerManager: getMCPServerManager2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
@@ -53090,6 +53226,7 @@ async function startRepl(options = {}) {
|
|
|
53090
53226
|
cocoConfigServers.filter((s) => s.enabled !== false),
|
|
53091
53227
|
projectServers.filter((s) => s.enabled !== false)
|
|
53092
53228
|
);
|
|
53229
|
+
configuredMcpServers = enabledServers;
|
|
53093
53230
|
if (enabledServers.length > 0) {
|
|
53094
53231
|
mcpManager = getMCPServerManager2();
|
|
53095
53232
|
let connections;
|
|
@@ -53109,6 +53246,7 @@ async function startRepl(options = {}) {
|
|
|
53109
53246
|
for (const connection of connections.values()) {
|
|
53110
53247
|
try {
|
|
53111
53248
|
const wrapped = await registerMCPTools2(toolRegistry, connection.name, connection.client);
|
|
53249
|
+
registeredMcpServers.add(connection.name);
|
|
53112
53250
|
if (wrapped.length === 0) {
|
|
53113
53251
|
logger2.warn(
|
|
53114
53252
|
`[MCP] Server '${connection.name}' connected but exposed 0 tools (check server auth/scopes).`
|
|
@@ -53137,6 +53275,46 @@ async function startRepl(options = {}) {
|
|
|
53137
53275
|
`[MCP] Initialization failed: ${mcpError instanceof Error ? mcpError.message : String(mcpError)}`
|
|
53138
53276
|
);
|
|
53139
53277
|
}
|
|
53278
|
+
async function ensureRequestedMcpConnections(message) {
|
|
53279
|
+
if (configuredMcpServers.length === 0) return;
|
|
53280
|
+
if (!mcpManager) {
|
|
53281
|
+
const { getMCPServerManager: getMCPServerManager2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
53282
|
+
mcpManager = getMCPServerManager2();
|
|
53283
|
+
}
|
|
53284
|
+
const normalizedMessage = message.toLowerCase();
|
|
53285
|
+
const explicitlyRequestsMcp = /\bmcp\b/.test(normalizedMessage) || /\b(use|using|usa|usar|utiliza|utilizar)\b.{0,24}\bmcp\b/.test(normalizedMessage);
|
|
53286
|
+
const matchingServers = configuredMcpServers.filter((server) => {
|
|
53287
|
+
if (mcpManager?.getConnection(server.name)) return false;
|
|
53288
|
+
if (explicitlyRequestsMcp) return true;
|
|
53289
|
+
const loweredName = server.name.toLowerCase();
|
|
53290
|
+
if (normalizedMessage.includes(loweredName)) return true;
|
|
53291
|
+
if (loweredName.includes("atlassian")) {
|
|
53292
|
+
return /\b(atlassian|jira|confluence)\b/.test(normalizedMessage);
|
|
53293
|
+
}
|
|
53294
|
+
return false;
|
|
53295
|
+
});
|
|
53296
|
+
for (const server of matchingServers) {
|
|
53297
|
+
try {
|
|
53298
|
+
const existingConnection = mcpManager.getConnection(server.name);
|
|
53299
|
+
if (existingConnection?.healthy === false) {
|
|
53300
|
+
await mcpManager.stopServer(server.name);
|
|
53301
|
+
}
|
|
53302
|
+
const connection = await mcpManager.startServer(server);
|
|
53303
|
+
if (!registeredMcpServers.has(connection.name)) {
|
|
53304
|
+
await (await Promise.resolve().then(() => (init_tools(), tools_exports))).registerMCPTools(toolRegistry, connection.name, connection.client);
|
|
53305
|
+
registeredMcpServers.add(connection.name);
|
|
53306
|
+
}
|
|
53307
|
+
} catch (error) {
|
|
53308
|
+
logger2.warn(
|
|
53309
|
+
`[MCP] On-demand connect failed for '${server.name}': ${error instanceof Error ? error.message : String(error)}`
|
|
53310
|
+
);
|
|
53311
|
+
}
|
|
53312
|
+
}
|
|
53313
|
+
}
|
|
53314
|
+
function extractMessageText(content) {
|
|
53315
|
+
if (typeof content === "string") return content;
|
|
53316
|
+
return content.filter((block) => block.type === "text").map((block) => block.text).join(" ");
|
|
53317
|
+
}
|
|
53140
53318
|
let hookRegistry;
|
|
53141
53319
|
let hookExecutor;
|
|
53142
53320
|
try {
|
|
@@ -53584,6 +53762,7 @@ ${imagePrompts}`.trim() : imagePrompts;
|
|
|
53584
53762
|
let streamStarted = false;
|
|
53585
53763
|
let llmCallCount = 0;
|
|
53586
53764
|
let lastToolGroup = null;
|
|
53765
|
+
await ensureRequestedMcpConnections(extractMessageText(effectiveMessage));
|
|
53587
53766
|
const result = await executeAgentTurn(session, effectiveMessage, provider, toolRegistry, {
|
|
53588
53767
|
onStream: (chunk) => {
|
|
53589
53768
|
if (!streamStarted) {
|