Files

61 lines
2.5 KiB
JavaScript
Raw Permalink Normal View History

// dsh-plugin-mcn MCP stdio 客户端(复用已验证方案;拆分自 index.js)
import { homedir } from "node:os";
import { join, isAbsolute, resolve } from "node:path";
import { spawn } from "node:child_process";
import { NPX_CMD, MCP_PACKAGE, MCP_ENV } from "./config.js";
function dshHome() {
const configured = process.env.DSH_HOME?.trim();
if (!configured) return join(homedir(), ".dsh");
return isAbsolute(configured) ? configured : resolve(configured);
}
function callMcpTool(toolName, args = {}) {
return new Promise((resolvePromise, reject) => {
const reqs = [
JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "dsh-plugin-mcn", version: "0.1.0" } } }),
JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }),
JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: toolName, arguments: args } }),
].join("\n") + "\n";
const child = spawn(NPX_CMD, ["-y", MCP_PACKAGE], {
env: { ...process.env, ...MCP_ENV },
shell: true,
stdio: ["pipe", "pipe", "inherit"],
});
let stdout = "";
let settled = false;
const timer = setTimeout(() => {
if (!settled) { settled = true; child.kill(); reject(new Error("MCP 调用超时")); }
}, 120000);
child.stdout.on("data", (chunk) => { stdout += chunk.toString("utf8"); });
child.on("close", (code) => {
if (settled) return;
settled = true;
clearTimeout(timer);
try {
// 找到 id=2 的响应
const lines = stdout.split("\n");
for (const line of lines) {
if (!line.includes('"id":2')) continue;
const msg = JSON.parse(line);
if (msg.result?.isError) { reject(new Error(msg.result.content?.[0]?.text || "MCP 工具报错")); return; }
// 优先 structuredContent(结构化数据);无则回退 text(格式化文本)
const sc = msg.result?.structuredContent;
const text = msg.result?.content?.[0]?.text ?? "";
resolvePromise({ structured: sc ?? null, text });
return;
}
reject(new Error("未找到 MCP 工具响应"));
} catch (e) {
reject(new Error("解析 MCP 响应失败: " + e.message));
}
});
child.stdin.write(reqs);
child.stdin.end();
child.on("error", (e) => { if (!settled) { settled = true; clearTimeout(timer); reject(new Error("MCP 进程启动失败: " + e.message)); } });
});
}
export { dshHome, callMcpTool };