@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 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
- return trimmed.length > 0 ? trimmed : void 0;
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 && { temperature: options?.temperature ?? this.config.temperature ?? 0 },
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 && { temperature: options?.temperature ?? this.config.temperature ?? 0 },
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(`${origin}/.well-known/oauth-protected-resource/${pathPart.replace(/^\//, "")}`);
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 shouldShowPermissionSuggestion() {
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 registry = new MCPRegistryImpl();
47413
- await registry.load();
47414
- const resolvedProjectPath = projectPath || process.cwd();
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 mcpTools = [mcpListServersTool];
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 (!mcpManager || configuredMcpServers.length === 0) return;
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) {