文档库:目录改为编号制(01-规范/02-架构设计/03-数据库/04-调整方案/
05-交接单/06-ops/07-scripts/08-skills/09-archive),顶层散文件归入 01-规范/;
INDEX.md 与 docs-manifest.json 重刷(档案 146 篇);旧目录名引用全量对齐。
IM 线:src/im/**(SDK / hub / store / presence / ws / gateway-token)、
src/web/routes/im.ts、src/db/plugin-data/**、src/supervisor/plugin-assembly.ts
及对应 test/**。
插件线:poc/{im-agent-bridge,im-connection-gateway,im-conversation-tabs,
business-plugins-im,carbon-mcp-probe}、src/web/routes/{sessions,overlay-device}.ts、
src/net/relay/{device-grant,instance-credential}.ts。
仓库卫生:清出 40 个历史误入库 / 已改名文件(34 个交接单归档 + 6 个旧结构,
本地均有副本);dsh-server-docs/.gitignore 补 tmp/;交接单不入库(政策)。
This commit is contained in:
1 parent
3d8f50e366
commit
e6207aa691
239 files changed
+34477
-14633
No files matched your search
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* carbon MCP probe — stub MCP server(零依赖单文件)
|
||||
*
|
||||
* 用途:只回答「dsh 的 mcp-client 能否连上并同步工具」这一个问题。
|
||||
* 不实现任何业务语义,不做任何外部访问。
|
||||
*
|
||||
* 用法:
|
||||
* node stub-mcp-server.mjs # stdio(默认)
|
||||
* node stub-mcp-server.mjs --http 39877 # Streamable HTTP
|
||||
*
|
||||
* 环境变量:
|
||||
* PROBE_LOG 请求日志落点(默认 ./stub-mcp.log)
|
||||
*/
|
||||
|
||||
import http from "node:http";
|
||||
import { appendFileSync, writeFileSync } from "node:fs";
|
||||
|
||||
const LOG = process.env.PROBE_LOG || "./stub-mcp.log";
|
||||
const TOOL_NAME = "ping";
|
||||
const PONG_PREFIX = "pong-carbon-probe";
|
||||
|
||||
try { writeFileSync(LOG, ""); } catch { /* 日志不可写不致命 */ }
|
||||
|
||||
function log(line) {
|
||||
const row = `${new Date().toISOString()} ${line}\n`;
|
||||
try { appendFileSync(LOG, row); } catch { /* ignore */ }
|
||||
process.stderr.write(row);
|
||||
}
|
||||
|
||||
const PING_TOOL = {
|
||||
name: TOOL_NAME,
|
||||
description: "carbon MCP probe stub — 返回固定串与服务端时间",
|
||||
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
||||
};
|
||||
|
||||
function callTool(params) {
|
||||
if (!params || params.name !== TOOL_NAME) {
|
||||
return { content: [{ type: "text", text: `unknown tool: ${params && params.name}` }], isError: true };
|
||||
}
|
||||
const ts = new Date().toISOString();
|
||||
return { content: [{ type: "text", text: `${PONG_PREFIX} ${ts}` }] };
|
||||
}
|
||||
|
||||
/** 处理一条 JSON-RPC message,返回 response 或 null(notification)。 */
|
||||
function handle(msg) {
|
||||
const method = msg && msg.method;
|
||||
const id = msg && msg.id;
|
||||
log(`rpc method=${method} id=${id === undefined ? "-" : id}`);
|
||||
if (method === "initialize") {
|
||||
return {
|
||||
jsonrpc: "2.0",
|
||||
id,
|
||||
result: {
|
||||
protocolVersion: msg.params && msg.params.protocolVersion ? msg.params.protocolVersion : "2025-06-18",
|
||||
capabilities: { tools: { listChanged: false } },
|
||||
serverInfo: { name: "carbon-probe-stub", version: "0.0.1" },
|
||||
},
|
||||
};
|
||||
}
|
||||
if (method === "notifications/initialized" || String(method || "").startsWith("notifications/")) return null;
|
||||
if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
|
||||
if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools: [PING_TOOL] } };
|
||||
if (method === "tools/call") return { jsonrpc: "2.0", id, result: callTool(msg.params) };
|
||||
if (id === undefined) return null;
|
||||
return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
|
||||
}
|
||||
|
||||
function dispatch(payload) {
|
||||
const msgs = Array.isArray(payload) ? payload : [payload];
|
||||
const out = [];
|
||||
for (const m of msgs) {
|
||||
const r = handle(m);
|
||||
if (r !== null) out.push(r);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ---------------- Streamable HTTP ---------------- */
|
||||
|
||||
function startHttp(port) {
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.method === "GET" && req.url.startsWith("/health")) {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: true, server: "carbon-probe-stub" }));
|
||||
return;
|
||||
}
|
||||
if (req.method !== "POST") {
|
||||
res.writeHead(405).end();
|
||||
return;
|
||||
}
|
||||
let body = "";
|
||||
req.on("data", (c) => { body += c.toString("utf8"); });
|
||||
req.on("end", () => {
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(body);
|
||||
} catch (e) {
|
||||
log(`http parse-error ${e.message} raw=${body.slice(0, 200)}`);
|
||||
res.writeHead(400, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } }));
|
||||
return;
|
||||
}
|
||||
const out = dispatch(payload);
|
||||
if (out.length === 0) {
|
||||
log("http -> 202 (notification only)");
|
||||
res.writeHead(202, { "Mcp-Session-Id": "probe-session-1" });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
const payloadOut = out.length === 1 ? out[0] : out;
|
||||
log(`http -> 200 ${JSON.stringify(payloadOut).slice(0, 300)}`);
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/json",
|
||||
"Mcp-Session-Id": "probe-session-1",
|
||||
});
|
||||
res.end(JSON.stringify(payloadOut));
|
||||
});
|
||||
});
|
||||
server.listen(port, "127.0.0.1", () => {
|
||||
log(`stub MCP server listening on http://127.0.0.1:${port}/mcp (transport=streamable-http)`);
|
||||
});
|
||||
server.on("error", (e) => { log(`listen error ${e.message}`); process.exit(1); });
|
||||
}
|
||||
|
||||
/* ---------------- stdio ---------------- */
|
||||
|
||||
function startStdio() {
|
||||
log("stub MCP server on stdio (transport=stdio)");
|
||||
let buf = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
buf += chunk;
|
||||
let idx;
|
||||
while ((idx = buf.indexOf("\n")) >= 0) {
|
||||
const line = buf.slice(0, idx).trim();
|
||||
buf = buf.slice(idx + 1);
|
||||
if (!line) continue;
|
||||
let msg;
|
||||
try { msg = JSON.parse(line); } catch (e) { log(`stdio parse-error ${e.message}`); continue; }
|
||||
const out = dispatch(msg);
|
||||
for (const r of out) process.stdout.write(JSON.stringify(r) + "\n");
|
||||
}
|
||||
});
|
||||
process.stdin.on("end", () => process.exit(0));
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const httpFlag = argv.indexOf("--http");
|
||||
if (httpFlag >= 0) {
|
||||
const port = Number(argv[httpFlag + 1] || 39877);
|
||||
startHttp(port);
|
||||
} else {
|
||||
startStdio();
|
||||
}
|
||||
Reference in new issue
Block a user