@corbat-tech/coco 2.25.10 → 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 +392 -281
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +85 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2568,7 +2568,11 @@ function getDefaultModel(provider) {
|
|
|
2568
2568
|
function normalizeConfiguredModel(model) {
|
|
2569
2569
|
if (typeof model !== "string") return void 0;
|
|
2570
2570
|
const trimmed = model.trim();
|
|
2571
|
-
|
|
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;
|
|
2572
2576
|
}
|
|
2573
2577
|
function getDefaultProvider() {
|
|
2574
2578
|
const envProvider = process.env["COCO_PROVIDER"]?.toLowerCase();
|
|
@@ -4498,7 +4502,9 @@ var init_openai = __esm({
|
|
|
4498
4502
|
input,
|
|
4499
4503
|
instructions: instructions ?? void 0,
|
|
4500
4504
|
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
4501
|
-
...supportsTemp && {
|
|
4505
|
+
...supportsTemp && {
|
|
4506
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
4507
|
+
},
|
|
4502
4508
|
store: false
|
|
4503
4509
|
});
|
|
4504
4510
|
return {
|
|
@@ -4533,7 +4539,9 @@ var init_openai = __esm({
|
|
|
4533
4539
|
instructions: instructions ?? void 0,
|
|
4534
4540
|
tools,
|
|
4535
4541
|
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
4536
|
-
...supportsTemp && {
|
|
4542
|
+
...supportsTemp && {
|
|
4543
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
4544
|
+
},
|
|
4537
4545
|
store: false
|
|
4538
4546
|
});
|
|
4539
4547
|
let content = "";
|
|
@@ -10261,6 +10269,7 @@ Rules:
|
|
|
10261
10269
|
- If you need real-time data, CALL web_search. NEVER say "I don't have access to real-time data."
|
|
10262
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\`.
|
|
10263
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.
|
|
10264
10273
|
- Before answering "I can't do that", check your full tool catalog below \u2014 you likely have a tool for it.
|
|
10265
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.
|
|
10266
10275
|
|
|
@@ -21252,7 +21261,9 @@ function createProtectedMetadataCandidates(resourceUrl, headerUrl) {
|
|
|
21252
21261
|
candidates.push(`${origin}/.well-known/oauth-protected-resource`);
|
|
21253
21262
|
if (pathPart && pathPart !== "/") {
|
|
21254
21263
|
candidates.push(`${origin}/.well-known/oauth-protected-resource${pathPart}`);
|
|
21255
|
-
candidates.push(
|
|
21264
|
+
candidates.push(
|
|
21265
|
+
`${origin}/.well-known/oauth-protected-resource/${pathPart.replace(/^\//, "")}`
|
|
21266
|
+
);
|
|
21256
21267
|
}
|
|
21257
21268
|
return Array.from(new Set(candidates));
|
|
21258
21269
|
}
|
|
@@ -22367,6 +22378,263 @@ var init_full_power_risk_mode = __esm({
|
|
|
22367
22378
|
}
|
|
22368
22379
|
});
|
|
22369
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
|
+
|
|
22370
22638
|
// src/cli/repl/allow-path-prompt.ts
|
|
22371
22639
|
var allow_path_prompt_exports = {};
|
|
22372
22640
|
__export(allow_path_prompt_exports, {
|
|
@@ -22815,263 +23083,6 @@ var init_stack_detector = __esm({
|
|
|
22815
23083
|
}
|
|
22816
23084
|
});
|
|
22817
23085
|
|
|
22818
|
-
// src/mcp/tools.ts
|
|
22819
|
-
var tools_exports = {};
|
|
22820
|
-
__export(tools_exports, {
|
|
22821
|
-
createToolsFromMCPServer: () => createToolsFromMCPServer,
|
|
22822
|
-
extractOriginalToolName: () => extractOriginalToolName,
|
|
22823
|
-
getMCPToolInfo: () => getMCPToolInfo,
|
|
22824
|
-
jsonSchemaToZod: () => jsonSchemaToZod,
|
|
22825
|
-
registerMCPTools: () => registerMCPTools,
|
|
22826
|
-
wrapMCPTool: () => wrapMCPTool,
|
|
22827
|
-
wrapMCPTools: () => wrapMCPTools
|
|
22828
|
-
});
|
|
22829
|
-
function buildMcpToolDescription(serverName, tool) {
|
|
22830
|
-
const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
|
|
22831
|
-
const lowerServer = serverName.toLowerCase();
|
|
22832
|
-
if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
|
|
22833
|
-
return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
|
|
22834
|
-
}
|
|
22835
|
-
return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
|
|
22836
|
-
}
|
|
22837
|
-
function jsonSchemaToZod(schema) {
|
|
22838
|
-
if (schema.enum && Array.isArray(schema.enum)) {
|
|
22839
|
-
const values = schema.enum;
|
|
22840
|
-
if (values.length > 0 && values.every((v) => typeof v === "string")) {
|
|
22841
|
-
return z.enum(values);
|
|
22842
|
-
}
|
|
22843
|
-
const literals = values.map((v) => z.literal(v));
|
|
22844
|
-
if (literals.length < 2) {
|
|
22845
|
-
return literals[0] ?? z.any();
|
|
22846
|
-
}
|
|
22847
|
-
return z.union(literals);
|
|
22848
|
-
}
|
|
22849
|
-
if (schema.const !== void 0) {
|
|
22850
|
-
return z.literal(schema.const);
|
|
22851
|
-
}
|
|
22852
|
-
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
22853
|
-
const schemas = schema.oneOf.map(jsonSchemaToZod);
|
|
22854
|
-
if (schemas.length >= 2) {
|
|
22855
|
-
return z.union(schemas);
|
|
22856
|
-
}
|
|
22857
|
-
return schemas[0] ?? z.unknown();
|
|
22858
|
-
}
|
|
22859
|
-
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
22860
|
-
const schemas = schema.anyOf.map(jsonSchemaToZod);
|
|
22861
|
-
if (schemas.length >= 2) {
|
|
22862
|
-
return z.union(schemas);
|
|
22863
|
-
}
|
|
22864
|
-
return schemas[0] ?? z.unknown();
|
|
22865
|
-
}
|
|
22866
|
-
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
22867
|
-
const schemas = schema.allOf.map(jsonSchemaToZod);
|
|
22868
|
-
return schemas.reduce((acc, s) => z.intersection(acc, s));
|
|
22869
|
-
}
|
|
22870
|
-
const type = schema.type;
|
|
22871
|
-
const makeNullable = (s) => {
|
|
22872
|
-
if (schema.nullable === true) return s.nullable();
|
|
22873
|
-
return s;
|
|
22874
|
-
};
|
|
22875
|
-
switch (type) {
|
|
22876
|
-
case "string": {
|
|
22877
|
-
let s = z.string();
|
|
22878
|
-
if (schema.format) {
|
|
22879
|
-
switch (schema.format) {
|
|
22880
|
-
case "uri":
|
|
22881
|
-
case "url":
|
|
22882
|
-
s = z.string().url();
|
|
22883
|
-
break;
|
|
22884
|
-
case "email":
|
|
22885
|
-
s = z.string().email();
|
|
22886
|
-
break;
|
|
22887
|
-
case "date-time":
|
|
22888
|
-
case "datetime":
|
|
22889
|
-
s = z.string().datetime();
|
|
22890
|
-
break;
|
|
22891
|
-
}
|
|
22892
|
-
}
|
|
22893
|
-
if (typeof schema.minLength === "number") s = s.min(schema.minLength);
|
|
22894
|
-
if (typeof schema.maxLength === "number") s = s.max(schema.maxLength);
|
|
22895
|
-
return makeNullable(s);
|
|
22896
|
-
}
|
|
22897
|
-
case "number": {
|
|
22898
|
-
let n = z.number();
|
|
22899
|
-
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22900
|
-
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22901
|
-
if (typeof schema.exclusiveMinimum === "number") n = n.gt(schema.exclusiveMinimum);
|
|
22902
|
-
if (typeof schema.exclusiveMaximum === "number") n = n.lt(schema.exclusiveMaximum);
|
|
22903
|
-
return makeNullable(n);
|
|
22904
|
-
}
|
|
22905
|
-
case "integer": {
|
|
22906
|
-
let n = z.number().int();
|
|
22907
|
-
if (typeof schema.minimum === "number") n = n.min(schema.minimum);
|
|
22908
|
-
if (typeof schema.maximum === "number") n = n.max(schema.maximum);
|
|
22909
|
-
return makeNullable(n);
|
|
22910
|
-
}
|
|
22911
|
-
case "boolean":
|
|
22912
|
-
return makeNullable(z.boolean());
|
|
22913
|
-
case "null":
|
|
22914
|
-
return z.null();
|
|
22915
|
-
case "array":
|
|
22916
|
-
if (schema.items) {
|
|
22917
|
-
const itemSchema = jsonSchemaToZod(schema.items);
|
|
22918
|
-
let arr = z.array(itemSchema);
|
|
22919
|
-
if (typeof schema.minItems === "number") arr = arr.min(schema.minItems);
|
|
22920
|
-
if (typeof schema.maxItems === "number") arr = arr.max(schema.maxItems);
|
|
22921
|
-
return makeNullable(arr);
|
|
22922
|
-
}
|
|
22923
|
-
return makeNullable(z.array(z.unknown()));
|
|
22924
|
-
case "object": {
|
|
22925
|
-
const properties = schema.properties;
|
|
22926
|
-
const required = schema.required;
|
|
22927
|
-
if (!properties) {
|
|
22928
|
-
return makeNullable(z.record(z.string(), z.unknown()));
|
|
22929
|
-
}
|
|
22930
|
-
const shape = {};
|
|
22931
|
-
for (const [key, propSchema] of Object.entries(properties)) {
|
|
22932
|
-
let fieldSchema = jsonSchemaToZod(propSchema);
|
|
22933
|
-
if (!required?.includes(key)) {
|
|
22934
|
-
fieldSchema = fieldSchema.optional();
|
|
22935
|
-
}
|
|
22936
|
-
if (propSchema.description && typeof propSchema.description === "string") {
|
|
22937
|
-
fieldSchema = fieldSchema.describe(propSchema.description);
|
|
22938
|
-
}
|
|
22939
|
-
shape[key] = fieldSchema;
|
|
22940
|
-
}
|
|
22941
|
-
return makeNullable(z.object(shape));
|
|
22942
|
-
}
|
|
22943
|
-
default:
|
|
22944
|
-
if (Array.isArray(schema.type)) {
|
|
22945
|
-
const types = schema.type;
|
|
22946
|
-
if (types.includes("null")) {
|
|
22947
|
-
const nonNullType = types.find((t) => t !== "null");
|
|
22948
|
-
if (nonNullType) {
|
|
22949
|
-
return jsonSchemaToZod({ ...schema, type: nonNullType }).nullable();
|
|
22950
|
-
}
|
|
22951
|
-
}
|
|
22952
|
-
}
|
|
22953
|
-
return z.unknown();
|
|
22954
|
-
}
|
|
22955
|
-
}
|
|
22956
|
-
function createToolParametersSchema(tool) {
|
|
22957
|
-
const schema = tool.inputSchema;
|
|
22958
|
-
if (!schema || schema.type !== "object") {
|
|
22959
|
-
return z.object({});
|
|
22960
|
-
}
|
|
22961
|
-
return jsonSchemaToZod(schema);
|
|
22962
|
-
}
|
|
22963
|
-
function formatToolResult(result) {
|
|
22964
|
-
if (result.isError) {
|
|
22965
|
-
throw new Error(result.content.map((c) => c.text || "").join("\n"));
|
|
22966
|
-
}
|
|
22967
|
-
return result.content.map((item) => {
|
|
22968
|
-
switch (item.type) {
|
|
22969
|
-
case "text":
|
|
22970
|
-
return item.text || "";
|
|
22971
|
-
case "image":
|
|
22972
|
-
return `[Image: ${item.mimeType || "unknown"}]`;
|
|
22973
|
-
case "resource":
|
|
22974
|
-
return `[Resource: ${item.resource?.uri || "unknown"}]`;
|
|
22975
|
-
default:
|
|
22976
|
-
return "";
|
|
22977
|
-
}
|
|
22978
|
-
}).filter(Boolean).join("\n");
|
|
22979
|
-
}
|
|
22980
|
-
function createToolName(serverName, toolName, prefix) {
|
|
22981
|
-
return `${prefix}_${serverName}_${toolName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
22982
|
-
}
|
|
22983
|
-
function wrapMCPTool(tool, serverName, client, options = {}) {
|
|
22984
|
-
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
22985
|
-
const wrappedName = createToolName(serverName, tool.name, opts.namePrefix);
|
|
22986
|
-
const parametersSchema = createToolParametersSchema(tool);
|
|
22987
|
-
const cocoTool = {
|
|
22988
|
-
name: wrappedName,
|
|
22989
|
-
description: buildMcpToolDescription(serverName, tool),
|
|
22990
|
-
category: opts.category,
|
|
22991
|
-
parameters: parametersSchema,
|
|
22992
|
-
execute: async (params) => {
|
|
22993
|
-
const timeout = opts.requestTimeout;
|
|
22994
|
-
try {
|
|
22995
|
-
const result = await Promise.race([
|
|
22996
|
-
client.callTool({
|
|
22997
|
-
name: tool.name,
|
|
22998
|
-
arguments: params
|
|
22999
|
-
}),
|
|
23000
|
-
new Promise((_, reject) => {
|
|
23001
|
-
setTimeout(() => {
|
|
23002
|
-
reject(new MCPTimeoutError(`Tool '${tool.name}' timed out after ${timeout}ms`));
|
|
23003
|
-
}, timeout);
|
|
23004
|
-
})
|
|
23005
|
-
]);
|
|
23006
|
-
return formatToolResult(result);
|
|
23007
|
-
} catch (error) {
|
|
23008
|
-
if (error instanceof MCPError) {
|
|
23009
|
-
throw error;
|
|
23010
|
-
}
|
|
23011
|
-
throw new MCPError(
|
|
23012
|
-
-32603,
|
|
23013
|
-
`Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
23014
|
-
);
|
|
23015
|
-
}
|
|
23016
|
-
}
|
|
23017
|
-
};
|
|
23018
|
-
const wrapped = {
|
|
23019
|
-
originalTool: tool,
|
|
23020
|
-
serverName,
|
|
23021
|
-
wrappedName
|
|
23022
|
-
};
|
|
23023
|
-
return { tool: cocoTool, wrapped };
|
|
23024
|
-
}
|
|
23025
|
-
function wrapMCPTools(tools, serverName, client, options = {}) {
|
|
23026
|
-
const cocoTools = [];
|
|
23027
|
-
const wrappedTools = [];
|
|
23028
|
-
for (const tool of tools) {
|
|
23029
|
-
const { tool: cocoTool, wrapped } = wrapMCPTool(tool, serverName, client, options);
|
|
23030
|
-
cocoTools.push(cocoTool);
|
|
23031
|
-
wrappedTools.push(wrapped);
|
|
23032
|
-
}
|
|
23033
|
-
return { tools: cocoTools, wrapped: wrappedTools };
|
|
23034
|
-
}
|
|
23035
|
-
async function createToolsFromMCPServer(serverName, client, options = {}) {
|
|
23036
|
-
if (!client.isConnected()) {
|
|
23037
|
-
await client.initialize({
|
|
23038
|
-
protocolVersion: "2024-11-05",
|
|
23039
|
-
capabilities: {},
|
|
23040
|
-
clientInfo: { name: "coco-mcp-client", version: "0.2.0" }
|
|
23041
|
-
});
|
|
23042
|
-
}
|
|
23043
|
-
const { tools } = await client.listTools();
|
|
23044
|
-
return wrapMCPTools(tools, serverName, client, options);
|
|
23045
|
-
}
|
|
23046
|
-
async function registerMCPTools(registry, serverName, client, options = {}) {
|
|
23047
|
-
const { tools, wrapped } = await createToolsFromMCPServer(serverName, client, options);
|
|
23048
|
-
for (const tool of tools) {
|
|
23049
|
-
registry.register(tool);
|
|
23050
|
-
}
|
|
23051
|
-
return wrapped;
|
|
23052
|
-
}
|
|
23053
|
-
function getMCPToolInfo(wrappedName, wrappedTools) {
|
|
23054
|
-
return wrappedTools.find((t) => t.wrappedName === wrappedName);
|
|
23055
|
-
}
|
|
23056
|
-
function extractOriginalToolName(wrappedName, serverName, prefix = "mcp") {
|
|
23057
|
-
const prefix_pattern = `${prefix}_${serverName}_`;
|
|
23058
|
-
if (wrappedName.startsWith(prefix_pattern)) {
|
|
23059
|
-
return wrappedName.slice(prefix_pattern.length);
|
|
23060
|
-
}
|
|
23061
|
-
return null;
|
|
23062
|
-
}
|
|
23063
|
-
var DEFAULT_OPTIONS2;
|
|
23064
|
-
var init_tools = __esm({
|
|
23065
|
-
"src/mcp/tools.ts"() {
|
|
23066
|
-
init_errors2();
|
|
23067
|
-
DEFAULT_OPTIONS2 = {
|
|
23068
|
-
namePrefix: "mcp",
|
|
23069
|
-
category: "deploy",
|
|
23070
|
-
requestTimeout: 6e4
|
|
23071
|
-
};
|
|
23072
|
-
}
|
|
23073
|
-
});
|
|
23074
|
-
|
|
23075
23086
|
// src/cli/repl/hooks/types.ts
|
|
23076
23087
|
function isHookEvent(value) {
|
|
23077
23088
|
return typeof value === "string" && HOOK_EVENTS.includes(value);
|
|
@@ -38297,6 +38308,9 @@ var RECOMMENDED_DENY = [
|
|
|
38297
38308
|
"bash:eval",
|
|
38298
38309
|
"bash:source"
|
|
38299
38310
|
];
|
|
38311
|
+
function getProjectPreferenceKey(projectPath) {
|
|
38312
|
+
return path39__default.resolve(projectPath);
|
|
38313
|
+
}
|
|
38300
38314
|
async function loadPermissionPreferences() {
|
|
38301
38315
|
try {
|
|
38302
38316
|
const content = await fs35__default.readFile(CONFIG_PATHS.config, "utf-8");
|
|
@@ -38304,7 +38318,8 @@ async function loadPermissionPreferences() {
|
|
|
38304
38318
|
return {
|
|
38305
38319
|
recommendedAllowlistApplied: config.recommendedAllowlistApplied,
|
|
38306
38320
|
recommendedAllowlistDismissed: config.recommendedAllowlistDismissed,
|
|
38307
|
-
recommendedAllowlistPrompted: config.recommendedAllowlistPrompted
|
|
38321
|
+
recommendedAllowlistPrompted: config.recommendedAllowlistPrompted,
|
|
38322
|
+
recommendedAllowlistPromptedProjects: config.recommendedAllowlistPromptedProjects
|
|
38308
38323
|
};
|
|
38309
38324
|
} catch {
|
|
38310
38325
|
return {};
|
|
@@ -38324,7 +38339,26 @@ async function savePermissionPreference(key, value) {
|
|
|
38324
38339
|
} catch {
|
|
38325
38340
|
}
|
|
38326
38341
|
}
|
|
38327
|
-
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()) {
|
|
38328
38362
|
const prefs = await loadPermissionPreferences();
|
|
38329
38363
|
if (prefs.recommendedAllowlistDismissed) {
|
|
38330
38364
|
return false;
|
|
@@ -38332,6 +38366,10 @@ async function shouldShowPermissionSuggestion() {
|
|
|
38332
38366
|
if (prefs.recommendedAllowlistApplied) {
|
|
38333
38367
|
return false;
|
|
38334
38368
|
}
|
|
38369
|
+
const projectKey = getProjectPreferenceKey(projectPath);
|
|
38370
|
+
if (prefs.recommendedAllowlistPromptedProjects?.[projectKey]) {
|
|
38371
|
+
return false;
|
|
38372
|
+
}
|
|
38335
38373
|
return true;
|
|
38336
38374
|
}
|
|
38337
38375
|
async function applyRecommendedPermissions() {
|
|
@@ -38340,7 +38378,8 @@ async function applyRecommendedPermissions() {
|
|
|
38340
38378
|
}
|
|
38341
38379
|
await savePermissionPreference("recommendedAllowlistApplied", true);
|
|
38342
38380
|
}
|
|
38343
|
-
async function showPermissionSuggestion() {
|
|
38381
|
+
async function showPermissionSuggestion(projectPath = process.cwd()) {
|
|
38382
|
+
await markPermissionSuggestionShownForProject(projectPath);
|
|
38344
38383
|
console.log();
|
|
38345
38384
|
console.log(chalk.magenta.bold(" \u{1F4CB} Recommended Permissions"));
|
|
38346
38385
|
console.log();
|
|
@@ -47396,6 +47435,27 @@ init_registry4();
|
|
|
47396
47435
|
init_registry();
|
|
47397
47436
|
init_config_loader();
|
|
47398
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
|
+
}
|
|
47399
47459
|
var mcpListServersTool = defineTool({
|
|
47400
47460
|
name: "mcp_list_servers",
|
|
47401
47461
|
description: `Inspect Coco's MCP configuration and current runtime connections.
|
|
@@ -47409,14 +47469,9 @@ when you need to know which MCP servers are configured, connected, healthy, or w
|
|
|
47409
47469
|
projectPath: z.string().optional().describe("Project path whose .mcp.json should be merged. Defaults to process.cwd()")
|
|
47410
47470
|
}),
|
|
47411
47471
|
async execute({ includeDisabled, includeTools, projectPath }) {
|
|
47412
|
-
const
|
|
47413
|
-
|
|
47414
|
-
|
|
47415
|
-
const configuredServers = mergeMCPConfigs(
|
|
47416
|
-
registry.listServers(),
|
|
47417
|
-
await loadMCPServersFromCOCOConfig(),
|
|
47418
|
-
await loadProjectMCPFile(resolvedProjectPath)
|
|
47419
|
-
).filter((server) => includeDisabled || server.enabled !== false);
|
|
47472
|
+
const configuredServers = (await loadConfiguredServers(projectPath)).filter(
|
|
47473
|
+
(server) => includeDisabled || server.enabled !== false
|
|
47474
|
+
);
|
|
47420
47475
|
const manager = getMCPServerManager();
|
|
47421
47476
|
const servers = [];
|
|
47422
47477
|
for (const server of configuredServers) {
|
|
@@ -47447,7 +47502,59 @@ when you need to know which MCP servers are configured, connected, healthy, or w
|
|
|
47447
47502
|
};
|
|
47448
47503
|
}
|
|
47449
47504
|
});
|
|
47450
|
-
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];
|
|
47451
47558
|
|
|
47452
47559
|
// src/tools/index.ts
|
|
47453
47560
|
init_registry4();
|
|
@@ -53064,8 +53171,8 @@ async function startRepl(options = {}) {
|
|
|
53064
53171
|
]);
|
|
53065
53172
|
session.projectContext = projectContext;
|
|
53066
53173
|
await initializeSessionMemory(session);
|
|
53067
|
-
if (await shouldShowPermissionSuggestion()) {
|
|
53068
|
-
await showPermissionSuggestion();
|
|
53174
|
+
if (await shouldShowPermissionSuggestion(projectPath)) {
|
|
53175
|
+
await showPermissionSuggestion(projectPath);
|
|
53069
53176
|
const updatedTrust = await loadTrustedTools(projectPath);
|
|
53070
53177
|
for (const tool of updatedTrust) {
|
|
53071
53178
|
session.trustedTools.add(tool);
|
|
@@ -53169,7 +53276,11 @@ async function startRepl(options = {}) {
|
|
|
53169
53276
|
);
|
|
53170
53277
|
}
|
|
53171
53278
|
async function ensureRequestedMcpConnections(message) {
|
|
53172
|
-
if (
|
|
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
|
+
}
|
|
53173
53284
|
const normalizedMessage = message.toLowerCase();
|
|
53174
53285
|
const explicitlyRequestsMcp = /\bmcp\b/.test(normalizedMessage) || /\b(use|using|usa|usar|utiliza|utilizar)\b.{0,24}\bmcp\b/.test(normalizedMessage);
|
|
53175
53286
|
const matchingServers = configuredMcpServers.filter((server) => {
|
|
@@ -53184,13 +53295,13 @@ async function startRepl(options = {}) {
|
|
|
53184
53295
|
});
|
|
53185
53296
|
for (const server of matchingServers) {
|
|
53186
53297
|
try {
|
|
53298
|
+
const existingConnection = mcpManager.getConnection(server.name);
|
|
53299
|
+
if (existingConnection?.healthy === false) {
|
|
53300
|
+
await mcpManager.stopServer(server.name);
|
|
53301
|
+
}
|
|
53187
53302
|
const connection = await mcpManager.startServer(server);
|
|
53188
53303
|
if (!registeredMcpServers.has(connection.name)) {
|
|
53189
|
-
await (await Promise.resolve().then(() => (init_tools(), tools_exports))).registerMCPTools(
|
|
53190
|
-
toolRegistry,
|
|
53191
|
-
connection.name,
|
|
53192
|
-
connection.client
|
|
53193
|
-
);
|
|
53304
|
+
await (await Promise.resolve().then(() => (init_tools(), tools_exports))).registerMCPTools(toolRegistry, connection.name, connection.client);
|
|
53194
53305
|
registeredMcpServers.add(connection.name);
|
|
53195
53306
|
}
|
|
53196
53307
|
} catch (error) {
|