// @dsh-local/carbon-mcp-probe — host 半区(POC 探针) // // 本包在 host 侧只做一件事:**取证**。 // 真正的 MCP 挂载不在这里写代码,而是由同目录的 cordis.patch.yml // insert 一条内核 `@deepseek-ai/dsh-mcp-client` entry 完成 —— 这正是本轮要验证的契约。 // // 探针把「boot 后 ctx.tools 注册表里到底有没有 MCP 工具」落成 JSON 证据文件, // 使验收不依赖 agent / 模型 / 界面。 import { appendFileSync, writeFileSync } from "node:fs"; const DUMP = process.env.CARBON_PROBE_DUMP || "/tmp/carbon-probe-dump.json"; const LOG = process.env.CARBON_PROBE_LOG || "/tmp/carbon-probe-host.log"; const DELAY_MS = Number(process.env.CARBON_PROBE_DELAY_MS || 8000); function log(line) { const row = `${new Date().toISOString()} ${line}\n`; try { appendFileSync(LOG, row); } catch { /* 日志不可写不致命 */ } process.stdout.write(`[carbon-mcp-probe] ${row}`); } /** 在有界预算内 BFS 一个对象图,收集所有含 needle 的键/字符串路径。 */ function bfsFind(root, needle, maxDepth = 5, maxNodes = 600) { const seen = new Set(); const hits = []; const queue = [[root, "$", 0]]; let visited = 0; while (queue.length && visited < maxNodes) { const [value, path, depth] = queue.shift(); if (value === null || value === undefined) continue; const t = typeof value; if (t !== "object" && t !== "function") { if (t === "string" && value.includes(needle)) hits.push({ path, kind: "string", value: value.slice(0, 160) }); continue; } if (seen.has(value)) continue; seen.add(value); visited += 1; let keys = []; try { keys = Object.getOwnPropertyNames(value); } catch { continue; } for (const k of keys) { if (k.includes(needle)) hits.push({ path: `${path}.${k}`, kind: "KEY" }); if (depth + 1 > maxDepth) continue; let child; try { child = value[k]; } catch { continue; } // getter 抛错就跳过,绝不冒泡 const childPath = `${path}.${k}`; if (child instanceof Map) { let i = 0; for (const [mk, mv] of child) { const mp = `${childPath}{Map}["${String(mk)}"]`; if (String(mk).includes(needle)) hits.push({ path: mp, kind: "MAPKEY" }); if (i++ > 250) break; queue.push([mv, mp, depth + 1]); } } else if (Array.isArray(child)) { child.slice(0, 60).forEach((v, i) => queue.push([v, `${childPath}[${i}]`, depth + 1])); } else if (typeof child === "string") { if (child.includes(needle)) hits.push({ path: childPath, kind: "string", value: child.slice(0, 160) }); } else if (child !== null && typeof child === "object") { queue.push([child, childPath, depth + 1]); } } } return { hits, visited, truncated: queue.length > 0 }; } function probeTools(ctx) { const rec = { ts: new Date().toISOString(), pid: process.pid }; // 通道 A:直接属性(未声明 inject 时应报错 —— 这本身是契约事实) try { const direct = ctx.tools; rec.channelA = { ok: true, type: typeof direct }; } catch (e) { rec.channelA = { ok: false, error: String(e) }; } // 通道 B:ctx.get(name) let tools = null; try { tools = typeof ctx.get === "function" ? ctx.get("tools") : null; rec.channelB = { ok: !!tools, name: tools && tools.name }; } catch (e) { rec.channelB = { ok: false, error: String(e) }; } if (!tools) return rec; // 通道 C:已知候选方法(不预设签名,逐个试) rec.channelC = {}; for (const m of ["list", "view", "schemas", "sdkSchemas", "get", "names"]) { try { if (typeof tools[m] !== "function") { rec.channelC[m] = { skipped: "not a function" }; continue; } const v = typeof tools[m] === "function" ? tools[m]() : undefined; if (v && typeof v.then === "function") { rec.channelC[m] = { async: true }; continue; } rec.channelC[m] = { type: typeof v, preview: JSON.stringify(v).slice(0, 400) }; } catch (e) { rec.channelC[m] = { threw: String(e).slice(0, 200) }; } } // 通道 D:有界 BFS —— 不依赖任何 API 形状,直接找 mcp__ 命名 try { rec.channelD = bfsFind(tools, "mcp__"); rec.hasMcpQualified = rec.channelD.hits.length > 0; } catch (e) { rec.channelD = { error: String(e) }; rec.hasMcpQualified = false; } // 通道 E:同上,只要 carbonprobe 片段(防命名前缀差异) try { rec.channelE = bfsFind(tools, "carbonprobe", 5, 600); } catch (e) { rec.channelE = { error: String(e) }; } return rec; } export function apply(ctx) { log(`apply pid=${process.pid} node=${process.version} dump=${DUMP}`); const timer = setTimeout(() => { let rec; try { rec = probeTools(ctx); } catch (e) { rec = { ts: new Date().toISOString(), fatal: String(e) }; } try { writeFileSync(DUMP, JSON.stringify(rec, null, 2)); log(`dump written -> ${DUMP}`); } catch (e) { log(`dump write failed: ${e}`); } log(`SUMMARY hasMcpQualified=${rec.hasMcpQualified} D.hits=${JSON.stringify(rec.channelD?.hits || []).slice(0, 800)}`); }, DELAY_MS); timer.unref?.(); }