@neurynae/toolcairn-mcp 0.8.9 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1372,7 +1372,9 @@ var checkIssueSchema = {
1372
1372
  };
1373
1373
  var checkCompatibilitySchema = {
1374
1374
  tool_a: z.string(),
1375
- tool_b: z.string()
1375
+ tool_b: z.string(),
1376
+ tool_a_version: z.string().optional().describe('Specific version of tool_a to evaluate (e.g., "14.0.0"). Default: latest.'),
1377
+ tool_b_version: z.string().optional().describe('Specific version of tool_b to evaluate (e.g., "18.2.0"). Default: latest.')
1376
1378
  };
1377
1379
  var suggestGraphUpdateSchema = {
1378
1380
  suggestion_type: z.enum(["new_tool", "new_edge", "update_health", "new_use_case"]),
@@ -1510,7 +1512,7 @@ ${args.prompt}
1510
1512
  """${projectToolsContext}
1511
1513
 
1512
1514
  Your response (one category name only):`;
1513
- const needs_tool_search_prompt = `Based on this classification, determine if ToolPilot tool search should be invoked.
1515
+ const needs_tool_search_prompt = `Based on this classification, determine if ToolCairn tool search should be invoked.
1514
1516
  Respond with 1 if the classification is one of: tool_discovery, stack_building, tool_comparison
1515
1517
  Respond with 0 if the classification is: tool_configuration, debugging, general_coding
1516
1518
  Respond with ONLY 0 or 1.`;
@@ -1526,7 +1528,7 @@ Respond with ONLY 0 or 1.`;
1526
1528
  "general_coding"
1527
1529
  ],
1528
1530
  tool_required_if: TOOL_REQUIRED_CLASSIFICATIONS,
1529
- instructions: "Step 1: Send classification_prompt to the LLM and get a classification. Step 2: If classification is in tool_required_if, call refine_requirement with the classification. Otherwise, proceed without ToolPilot search."
1531
+ instructions: "Step 1: Send classification_prompt to the LLM and get a classification. Step 2: If classification is in tool_required_if, call refine_requirement with the classification. Otherwise, proceed without ToolCairn search."
1530
1532
  });
1531
1533
  } catch (e) {
1532
1534
  logger2.error({ err: e }, "classify_prompt failed");
@@ -1540,29 +1542,29 @@ var import_errors5 = __toESM(require_dist2(), 1);
1540
1542
 
1541
1543
  // ../../packages/tools-local/dist/templates/agent-instructions.js
1542
1544
  init_esm_shims();
1543
- var TOOLPILOT_MCP_ENTRY = {
1544
- toolpilot: {
1545
+ var TOOLCAIRN_MCP_ENTRY = {
1546
+ toolcairn: {
1545
1547
  command: "npx",
1546
- args: ["-y", "@toolcairn/mcp@latest"]
1548
+ args: ["-y", "@neurynae/toolcairn-mcp"]
1547
1549
  }
1548
1550
  };
1549
1551
  var CORE_RULES = `
1550
- ## ToolPilot \u2014 Tool Intelligence MCP
1552
+ ## ToolCairn \u2014 Tool Intelligence MCP
1551
1553
 
1552
- ToolPilot is your tool intelligence co-pilot. It has a graph database of 500+ indexed tools with health signals, compatibility data, and known issue tracking. Always use it before recommending any tool, library, or framework.
1554
+ ToolCairn is your tool intelligence co-pilot. It has a graph database of 500+ indexed tools with health signals, compatibility data, and known issue tracking. Always use it before recommending any tool, library, or framework.
1553
1555
 
1554
1556
  ### Workflow for Tool Selection
1555
1557
 
1556
1558
  1. **Before recommending any tool**: Always call \`search_tools\` first. Never recommend from training data alone.
1557
1559
  2. **When user describes a use case**: Call \`classify_prompt\` first, then \`refine_requirement\` if tool selection is needed.
1558
- 3. **When starting a new project**: Call \`toolpilot_init\` to set up .toolpilot/config.json.
1559
- 4. **When resuming work on a project**: Read .toolpilot/config.json via \`read_project_config\` to see confirmed tools.
1560
+ 3. **When starting a new project**: Call \`toolcairn_init\` to set up .toolcairn/config.json.
1561
+ 4. **When resuming work on a project**: Read .toolcairn/config.json via \`read_project_config\` to see confirmed tools.
1560
1562
  5. **When a tool is selected**: Call \`update_project_config\` to persist the choice.
1561
1563
  6. **When encountering an error with a tool**: Call \`check_issue\` before debugging \u2014 it may be a known issue with an open GitHub ticket.
1562
1564
  7. **When user asks to compare tools**: Call \`compare_tools\` for a structured comparison with health data.
1563
1565
  8. **When user chooses a non-indexed/proprietary tool**: Add it to config with source: "non_oss" via \`update_project_config\`.
1564
1566
 
1565
- ### Available ToolPilot MCP Tools
1567
+ ### Available ToolCairn MCP Tools
1566
1568
 
1567
1569
  | Tool | When to use |
1568
1570
  |------|------------|
@@ -1575,17 +1577,17 @@ ToolPilot is your tool intelligence co-pilot. It has a graph database of 500+ in
1575
1577
  | \`check_compatibility\` | Check if two tools work well together |
1576
1578
  | \`compare_tools\` | Compare two tools with health and graph data |
1577
1579
  | \`report_outcome\` | Report whether a recommended tool worked (improves future results) |
1578
- | \`toolpilot_init\` | Set up ToolPilot for a new project |
1579
- | \`init_project_config\` | Initialize .toolpilot/config.json |
1580
- | \`read_project_config\` | Parse .toolpilot/config.json to get confirmed tools |
1581
- | \`update_project_config\` | Add/remove/update tools in .toolpilot/config.json |
1582
- | \`suggest_graph_update\` | Suggest a new tool or relationship for the ToolPilot graph |
1580
+ | \`toolcairn_init\` | Set up ToolCairn for a new project |
1581
+ | \`init_project_config\` | Initialize .toolcairn/config.json |
1582
+ | \`read_project_config\` | Parse .toolcairn/config.json to get confirmed tools |
1583
+ | \`update_project_config\` | Add/remove/update tools in .toolcairn/config.json |
1584
+ | \`suggest_graph_update\` | Suggest a new tool or relationship for the ToolCairn graph |
1583
1585
 
1584
1586
  ### Rules
1585
1587
 
1586
- - NEVER recommend a tool without first checking ToolPilot unless the tool is already in .toolpilot/config.json
1587
- - Always check .toolpilot/config.json at the start of a session if it exists
1588
- - Prefer open-source tools from ToolPilot index; flag proprietary alternatives clearly
1588
+ - NEVER recommend a tool without first checking ToolCairn unless the tool is already in .toolcairn/config.json
1589
+ - Always check .toolcairn/config.json at the start of a session if it exists
1590
+ - Prefer open-source tools from ToolCairn index; flag proprietary alternatives clearly
1589
1591
  - After selecting a tool, always call \`update_project_config\` to persist it
1590
1592
  - After using a tool successfully/unsuccessfully, call \`report_outcome\`
1591
1593
  `;
@@ -1661,20 +1663,20 @@ function getInstructionsForAgent(agent) {
1661
1663
  function getMcpConfigEntry(serverPath) {
1662
1664
  if (serverPath) {
1663
1665
  return {
1664
- toolpilot: {
1666
+ toolcairn: {
1665
1667
  command: "node",
1666
1668
  args: [serverPath]
1667
1669
  }
1668
1670
  };
1669
1671
  }
1670
- return TOOLPILOT_MCP_ENTRY;
1672
+ return TOOLCAIRN_MCP_ENTRY;
1671
1673
  }
1672
1674
  function getOpenCodeMcpEntry(serverPath) {
1673
1675
  const resolvedPath = serverPath;
1674
1676
  return {
1675
- toolpilot: {
1677
+ toolcairn: {
1676
1678
  type: "local",
1677
- command: resolvedPath ? ["node", resolvedPath] : ["npx", "-y", "@toolcairn/mcp@latest"],
1679
+ command: resolvedPath ? ["node", resolvedPath] : ["npx", "-y", "@neurynae/toolcairn-mcp"],
1678
1680
  enabled: true
1679
1681
  }
1680
1682
  };
@@ -1688,7 +1690,7 @@ function generateTrackerHtml2(eventsPath) {
1688
1690
  <head>
1689
1691
  <meta charset="UTF-8" />
1690
1692
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1691
- <title>ToolPilot Tracker</title>
1693
+ <title>ToolCairn Tracker</title>
1692
1694
  <style>
1693
1695
  :root {
1694
1696
  --bg: #0a0a0f;
@@ -1785,7 +1787,7 @@ function generateTrackerHtml2(eventsPath) {
1785
1787
  <circle cx="10" cy="10" r="9" stroke="#7c5cfc" stroke-width="1.5"/>
1786
1788
  <path d="M6 10h8M10 6v8" stroke="#7c5cfc" stroke-width="1.5" stroke-linecap="round"/>
1787
1789
  </svg>
1788
- <h1><span>Tool</span>Pilot Tracker</h1>
1790
+ <h1><span>Tool</span>Cairn Tracker</h1>
1789
1791
  <div id="statusText" style="font-size:12px; color:var(--muted);">Loading...</div>
1790
1792
  <div id="statusDot" class="status-dot paused"></div>
1791
1793
  </header>
@@ -1814,7 +1816,7 @@ function generateTrackerHtml2(eventsPath) {
1814
1816
  <div class="empty" id="emptyState">
1815
1817
  <svg width="40" height="40" viewBox="0 0 40 40"><circle cx="20" cy="20" r="18" stroke="currentColor" stroke-width="1.5" fill="none"/><path d="M13 20h14M20 13v14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
1816
1818
  <p>Waiting for MCP tool calls...</p>
1817
- <code>Set TOOLPILOT_EVENTS_PATH in your MCP server env</code>
1819
+ <code>Set TOOLCAIRN_EVENTS_PATH in your MCP server env</code>
1818
1820
  </div>
1819
1821
  </div>
1820
1822
  <div class="sidebar">
@@ -2076,7 +2078,7 @@ function renderAll() {
2076
2078
  // \u2500\u2500\u2500 Boot \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2077
2079
  if (!EVENTS_PATH || EVENTS_PATH === 'null') {
2078
2080
  document.getElementById('statusText').textContent = 'No events path configured';
2079
- document.getElementById('emptyState').querySelector('p').textContent = 'TOOLPILOT_EVENTS_PATH not set in MCP server environment';
2081
+ document.getElementById('emptyState').querySelector('p').textContent = 'TOOLCAIRN_EVENTS_PATH not set in MCP server environment';
2080
2082
  } else {
2081
2083
  startPolling();
2082
2084
  }
@@ -2086,19 +2088,19 @@ if (!EVENTS_PATH || EVENTS_PATH === 'null') {
2086
2088
  }
2087
2089
 
2088
2090
  // ../../packages/tools-local/dist/handlers/toolcairn-init.js
2089
- var logger3 = (0, import_errors5.createMcpLogger)({ name: "@toolcairn/tools:toolpilot-init" });
2091
+ var logger3 = (0, import_errors5.createMcpLogger)({ name: "@toolcairn/tools:toolcairn-init" });
2090
2092
  async function handleToolcairnInit(args) {
2091
2093
  try {
2092
- logger3.info({ agent: args.agent, project_root: args.project_root }, "toolpilot_init called");
2094
+ logger3.info({ agent: args.agent, project_root: args.project_root }, "toolcairn_init called");
2093
2095
  const instructions = getInstructionsForAgent(args.agent);
2094
2096
  const isOpenCode = args.agent === "opencode";
2095
2097
  const mcpConfigEntry = isOpenCode ? getOpenCodeMcpEntry(args.server_path) : getMcpConfigEntry(args.server_path);
2096
2098
  const mcpConfigFile = isOpenCode ? "opencode.json" : ".mcp.json";
2097
2099
  const hasMcpJson = args.detected_files?.some((f) => f === mcpConfigFile || f.endsWith(`/${mcpConfigFile}`));
2098
2100
  const hasInstructionFile = args.detected_files?.some((f) => f.endsWith(instructions.file_path));
2099
- const hasToolpilotConfig = args.detected_files?.some((f) => f.includes(".toolpilot/config.json"));
2100
- const hasTrackerHtml = args.detected_files?.some((f) => f.includes(".toolpilot/tracker.html"));
2101
- const eventsPath = `${args.project_root}/.toolpilot/events.jsonl`;
2101
+ const hasToolcairnConfig = args.detected_files?.some((f) => f.includes(".toolcairn/config.json"));
2102
+ const hasTrackerHtml = args.detected_files?.some((f) => f.includes(".toolcairn/tracker.html"));
2103
+ const eventsPath = `${args.project_root}/.toolcairn/events.jsonl`;
2102
2104
  const setupSteps = [];
2103
2105
  let step = 1;
2104
2106
  setupSteps.push({
@@ -2109,7 +2111,7 @@ async function handleToolcairnInit(args) {
2109
2111
  note: hasInstructionFile ? `Append the content to your existing ${instructions.file_path}` : `Create ${instructions.file_path} with the content`
2110
2112
  });
2111
2113
  const mcpContent = isOpenCode ? JSON.stringify({ mcp: mcpConfigEntry }, null, 2) : JSON.stringify({ mcpServers: mcpConfigEntry }, null, 2);
2112
- const mcpMergeNote = isOpenCode ? `Merge the toolpilot entry into your existing ${mcpConfigFile} under "mcp"` : `Merge the toolpilot entry into your existing ${mcpConfigFile} under "mcpServers"`;
2114
+ const mcpMergeNote = isOpenCode ? `Merge the toolcairn entry into your existing ${mcpConfigFile} under "mcp"` : `Merge the toolcairn entry into your existing ${mcpConfigFile} under "mcpServers"`;
2113
2115
  const mcpCreateNote = isOpenCode ? `Create ${mcpConfigFile} with this content (OpenCode MCP config format)` : `Create ${mcpConfigFile} with this content`;
2114
2116
  setupSteps.push({
2115
2117
  step: step++,
@@ -2118,29 +2120,29 @@ async function handleToolcairnInit(args) {
2118
2120
  content: mcpContent,
2119
2121
  note: hasMcpJson ? mcpMergeNote : mcpCreateNote
2120
2122
  });
2121
- if (!hasToolpilotConfig) {
2123
+ if (!hasToolcairnConfig) {
2122
2124
  setupSteps.push({
2123
2125
  step: step++,
2124
2126
  action: "create",
2125
- file: ".toolpilot/config.json",
2126
- note: "Call init_project_config to generate the config content, then write to .toolpilot/config.json"
2127
+ file: ".toolcairn/config.json",
2128
+ note: "Call init_project_config to generate the config content, then write to .toolcairn/config.json"
2127
2129
  });
2128
2130
  }
2129
2131
  if (!hasTrackerHtml) {
2130
2132
  setupSteps.push({
2131
2133
  step: step++,
2132
2134
  action: "create",
2133
- file: ".toolpilot/tracker.html",
2135
+ file: ".toolcairn/tracker.html",
2134
2136
  content: generateTrackerHtml2(eventsPath),
2135
- note: `Open .toolpilot/tracker.html in your browser to monitor MCP tool calls in real time. Set TOOLPILOT_EVENTS_PATH=${eventsPath} in your MCP server environment to enable event logging.`
2137
+ note: `Open .toolcairn/tracker.html in your browser to monitor MCP tool calls in real time. Set TOOLCAIRN_EVENTS_PATH=${eventsPath} in your MCP server environment to enable event logging.`
2136
2138
  });
2137
2139
  }
2138
2140
  setupSteps.push({
2139
2141
  step: step++,
2140
2142
  action: "append",
2141
2143
  file: ".gitignore",
2142
- content: "\n# ToolPilot\n.toolpilot/events.jsonl\n",
2143
- note: "Add .toolpilot/events.jsonl to .gitignore (the tracker event log)"
2144
+ content: "\n# ToolCairn\n.toolcairn/events.jsonl\n",
2145
+ note: "Add .toolcairn/events.jsonl to .gitignore (the tracker event log)"
2144
2146
  });
2145
2147
  const agentFileLabel = {
2146
2148
  claude: "CLAUDE.md",
@@ -2158,16 +2160,16 @@ async function handleToolcairnInit(args) {
2158
2160
  mcp_config_entry: mcpConfigEntry,
2159
2161
  events_path: eventsPath,
2160
2162
  summary: [
2161
- `ToolPilot setup for ${args.agent} agent in ${args.project_root}`,
2163
+ `ToolCairn setup for ${args.agent} agent in ${args.project_root}`,
2162
2164
  `Instructions will be added to: ${instructions.file_path}`,
2163
- `MCP server entry: toolpilot \u2192 ${mcpConfigFile}`,
2164
- hasToolpilotConfig ? ".toolpilot/config.json already exists \u2014 skipping init" : "Run init_project_config next to generate .toolpilot/config.json",
2165
- hasTrackerHtml ? ".toolpilot/tracker.html already exists \u2014 skipping" : "Tracker dashboard: open .toolpilot/tracker.html in browser"
2165
+ `MCP server entry: toolcairn \u2192 ${mcpConfigFile}`,
2166
+ hasToolcairnConfig ? ".toolcairn/config.json already exists \u2014 skipping init" : "Run init_project_config next to generate .toolcairn/config.json",
2167
+ hasTrackerHtml ? ".toolcairn/tracker.html already exists \u2014 skipping" : "Tracker dashboard: open .toolcairn/tracker.html in browser"
2166
2168
  ].join("\n"),
2167
- next_steps: hasToolpilotConfig ? "Setup complete. Open .toolpilot/tracker.html to monitor tool calls." : "After completing setup steps, call init_project_config to initialize .toolpilot/config.json."
2169
+ next_steps: hasToolcairnConfig ? "Setup complete. Open .toolcairn/tracker.html to monitor tool calls." : "After completing setup steps, call init_project_config to initialize .toolcairn/config.json."
2168
2170
  });
2169
2171
  } catch (e) {
2170
- logger3.error({ err: e }, "toolpilot_init failed");
2172
+ logger3.error({ err: e }, "toolcairn_init failed");
2171
2173
  return errResult("init_error", e instanceof Error ? e.message : String(e));
2172
2174
  }
2173
2175
  }
@@ -2185,7 +2187,7 @@ async function handleInitProjectConfig(args) {
2185
2187
  source: t.source,
2186
2188
  version: t.version,
2187
2189
  chosen_at: now,
2188
- chosen_reason: "Auto-detected from project files during toolpilot_init",
2190
+ chosen_reason: "Auto-detected from project files during toolcairn_init",
2189
2191
  alternatives_considered: []
2190
2192
  }));
2191
2193
  const config5 = {
@@ -2211,8 +2213,8 @@ async function handleInitProjectConfig(args) {
2211
2213
  const config_json = JSON.stringify(config5, null, 2);
2212
2214
  return okResult({
2213
2215
  config_json,
2214
- file_path: ".toolpilot/config.json",
2215
- instructions: "Create the directory .toolpilot/ in your project root (if it does not exist), then write this config_json content to .toolpilot/config.json. Also add .toolpilot/ to .gitignore if not already present.",
2216
+ file_path: ".toolcairn/config.json",
2217
+ instructions: "Create the directory .toolcairn/ in your project root (if it does not exist), then write this config_json content to .toolcairn/config.json. Also add .toolcairn/ to .gitignore if not already present.",
2216
2218
  confirmed_count: confirmedTools.length,
2217
2219
  next_step: confirmedTools.length > 0 ? "Config initialized with auto-detected tools. Use search_tools to find any additional tools you need." : "Config initialized. Use classify_prompt \u2192 refine_requirement \u2192 search_tools to discover tools for your project."
2218
2220
  });
@@ -2258,13 +2260,13 @@ async function handleReadProjectConfig(args) {
2258
2260
  };
2259
2261
  });
2260
2262
  const non_oss_tools = config5.tools.confirmed.filter((t) => t.source === "non_oss").map((t) => t.name);
2261
- const toolpilot_indexed_tools = config5.tools.confirmed.filter((t) => t.source === "toolpilot").map((t) => t.name);
2263
+ const toolcairn_indexed_tools = config5.tools.confirmed.filter((t) => t.source === "toolcairn" || t.source === "toolpilot").map((t) => t.name);
2262
2264
  return okResult({
2263
2265
  project: config5.project,
2264
2266
  confirmed_tools: confirmedToolNames,
2265
2267
  pending_tools: pendingToolNames,
2266
2268
  non_oss_tools,
2267
- toolpilot_indexed_tools,
2269
+ toolcairn_indexed_tools,
2268
2270
  stale_tools: staleTools,
2269
2271
  total_confirmed: confirmedToolNames.length,
2270
2272
  total_pending: pendingToolNames.length,
@@ -2304,11 +2306,11 @@ async function handleUpdateProjectConfig(args) {
2304
2306
  if (!config5.tools.confirmed.some((t) => t.name === args.tool_name)) {
2305
2307
  const newTool = {
2306
2308
  name: args.tool_name,
2307
- source: data.source ?? "toolpilot",
2309
+ source: data.source ?? "toolcairn",
2308
2310
  github_url: data.github_url,
2309
2311
  version: data.version,
2310
2312
  chosen_at: now,
2311
- chosen_reason: data.chosen_reason ?? "Selected via ToolPilot",
2313
+ chosen_reason: data.chosen_reason ?? "Selected via ToolCairn",
2312
2314
  alternatives_considered: data.alternatives_considered ?? [],
2313
2315
  query_id: data.query_id,
2314
2316
  notes: data.notes
@@ -2319,7 +2321,7 @@ async function handleUpdateProjectConfig(args) {
2319
2321
  action: "add_tool",
2320
2322
  tool: args.tool_name,
2321
2323
  timestamp: now,
2322
- reason: data.chosen_reason ?? "Added via ToolPilot recommendation"
2324
+ reason: data.chosen_reason ?? "Added via ToolCairn recommendation"
2323
2325
  });
2324
2326
  break;
2325
2327
  }
@@ -2379,12 +2381,12 @@ async function handleUpdateProjectConfig(args) {
2379
2381
  const updated_config_json = JSON.stringify(config5, null, 2);
2380
2382
  return okResult({
2381
2383
  updated_config_json,
2382
- file_path: ".toolpilot/config.json",
2384
+ file_path: ".toolcairn/config.json",
2383
2385
  action_applied: args.action,
2384
2386
  tool_name: args.tool_name,
2385
2387
  confirmed_count: config5.tools.confirmed.length,
2386
2388
  pending_count: config5.tools.pending_evaluation.length,
2387
- instructions: "Write updated_config_json to .toolpilot/config.json to persist this change."
2389
+ instructions: "Write updated_config_json to .toolcairn/config.json to persist this change."
2388
2390
  });
2389
2391
  } catch (e) {
2390
2392
  logger6.error({ err: e }, "update_project_config failed");
@@ -2640,7 +2642,7 @@ async function addToolsToServer(server) {
2640
2642
  server.registerTool(
2641
2643
  "get_stack",
2642
2644
  {
2643
- description: 'Build a complementary tool stack for a project use case. For best results, call refine_requirement first with classification "stack_building", evaluate its decomposition_prompt to get sub-needs, then pass each {sub_need_type, keyword_sentence} object as a sub_needs entry. This lets get_stack keyword-match per layer (e.g. "web-framework", "database", "auth") instead of one broad search. Falls back to balanced search when sub_needs is omitted.',
2645
+ description: 'Build a complementary tool stack for a project use case. For best results, call refine_requirement first with classification "stack_building", evaluate its decomposition_prompt to get sub-needs, then pass each {sub_need_type, keyword_sentence} object as a sub_needs entry. This lets get_stack keyword-match per layer (e.g. "web-framework", "database", "auth") instead of one broad search. Falls back to balanced search when sub_needs is omitted. Each tool in the returned stack also carries a `version` object with the recommended version that is cross-compatible with the rest of the stack (downgraded from latest if needed to satisfy peer constraints), plus a top-level `compatibility_matrix` + `stack_compatibility` summarising cross-tool version fit.',
2644
2646
  inputSchema: getStackSchema
2645
2647
  },
2646
2648
  wrap("get_stack", async (args) => remote.getStack(args))
@@ -2648,7 +2650,7 @@ async function addToolsToServer(server) {
2648
2650
  server.registerTool(
2649
2651
  "check_compatibility",
2650
2652
  {
2651
- description: "Check compatibility between two tools. Returns direct graph relationships and inferred compatibility from shared neighbors.",
2653
+ description: 'Check compatibility between two tools with version-aware matching. When both tools have declared dependency metadata (npm peerDependencies, PyPI requires_dist, etc.) the handler evaluates range constraints directly and returns a version_checks array plus runtime_requirements. Pass optional tool_a_version / tool_b_version to evaluate specific versions (e.g. "is next@14 compatible with react@17?"). Falls back to graph-edge + shared-neighbors inference when version metadata is unavailable. Response includes `source`: "declared_dependency" | "graph_edges" | "shared_neighbors".',
2652
2654
  inputSchema: checkCompatibilitySchema
2653
2655
  },
2654
2656
  wrap("check_compatibility", async (args) => remote.checkCompatibility(args))