Skip to main content
Glama
masx200

Persistent Terminal MCP Server

by masx200
test-cursor-scenario.mjs7.72 kB
#!/usr/bin/env node /** * 模拟 Cursor 使用 MCP 服务器的场景 * 测试创建终端、写入命令、读取输出等操作 */ import { spawn } from "child_process"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); console.log("=".repeat(80)); console.log("模拟 Cursor 使用场景测试"); console.log("=".repeat(80)); console.log(); const serverPath = join(__dirname, "..", "..", "dist", "index.js"); console.log(`启动服务器: node ${serverPath}`); console.log(); const server = spawn("node", [serverPath], { stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, MCP_DEBUG: "false", }, }); let requestId = 0; let responses = new Map(); let nonJsonLines = []; let testsPassed = 0; let testsFailed = 0; // 发送 JSON-RPC 请求 function sendRequest(method, params = {}) { requestId++; const request = { jsonrpc: "2.0", id: requestId, method, params, }; console.log(`\n→ 发送请求 #${requestId}: ${method}`); server.stdin.write(JSON.stringify(request) + "\n"); return new Promise((resolve) => { responses.set(requestId, resolve); }); } // 监听 stdout server.stdout.on("data", (data) => { const text = data.toString(); const lines = text.split("\n"); for (const line of lines) { if (line.trim()) { try { const parsed = JSON.parse(line); if (parsed.id && responses.has(parsed.id)) { const resolve = responses.get(parsed.id); responses.delete(parsed.id); resolve(parsed); } } catch (e) { nonJsonLines.push(line); console.error("✗ 非 JSON 输出:", line); } } } }); // 监听 stderr server.stderr.on("data", (data) => { const text = data.toString(); if (text.trim()) { console.log("[STDERR]", text.trim()); } }); // 运行测试场景 async function runTests() { try { // 等待服务器启动 await new Promise((resolve) => setTimeout(resolve, 500)); // 1. 初始化 console.log("\n" + "=".repeat(80)); console.log("测试 1: 初始化连接"); console.log("=".repeat(80)); const initResponse = await sendRequest("initialize", { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "cursor-test", version: "1.0.0", }, }); if (initResponse.result) { console.log("✓ 初始化成功"); testsPassed++; } else { console.error("✗ 初始化失败"); testsFailed++; } // 2. 列出工具 console.log("\n" + "=".repeat(80)); console.log("测试 2: 列出可用工具"); console.log("=".repeat(80)); const toolsResponse = await sendRequest("tools/list"); if (toolsResponse.result && toolsResponse.result.tools) { console.log(`✓ 获取到 ${toolsResponse.result.tools.length} 个工具`); toolsResponse.result.tools.forEach((tool) => { console.log(` - ${tool.name}`); }); testsPassed++; } else { console.error("✗ 获取工具列表失败"); testsFailed++; } // 3. 创建终端 console.log("\n" + "=".repeat(80)); console.log("测试 3: 创建终端"); console.log("=".repeat(80)); const createResponse = await sendRequest("tools/call", { name: "create_terminal_basic", arguments: { cwd: process.cwd(), }, }); let terminalId = null; if (createResponse.result && createResponse.result.content) { const content = createResponse.result.content[0]; console.log("✓ 终端创建成功"); console.log(" 响应:", content.text.substring(0, 200)); // 从 structuredContent 中提取 terminalId if ( createResponse.result.structuredContent && createResponse.result.structuredContent.terminalId ) { terminalId = createResponse.result.structuredContent.terminalId; console.log(` Terminal ID: ${terminalId}`); } testsPassed++; } else { console.error("✗ 创建终端失败"); testsFailed++; } if (terminalId) { // 4. 写入命令 console.log("\n" + "=".repeat(80)); console.log("测试 4: 写入命令"); console.log("=".repeat(80)); const writeResponse = await sendRequest("tools/call", { name: "write_terminal", arguments: { terminalId: terminalId, input: 'echo "Hello from MCP test"', }, }); if (writeResponse.result) { console.log("✓ 命令写入成功"); testsPassed++; } else { console.error("✗ 命令写入失败"); testsFailed++; } // 等待命令执行 await new Promise((resolve) => setTimeout(resolve, 500)); // 5. 读取输出 console.log("\n" + "=".repeat(80)); console.log("测试 5: 读取输出"); console.log("=".repeat(80)); const readResponse = await sendRequest("tools/call", { name: "read_terminal", arguments: { terminalId: terminalId, }, }); if (readResponse.result && readResponse.result.content) { const content = readResponse.result.content[0]; console.log("✓ 读取输出成功"); console.log(" 输出预览:", content.text.substring(0, 300)); testsPassed++; } else { console.error("✗ 读取输出失败"); testsFailed++; } // 6. 列出终端 console.log("\n" + "=".repeat(80)); console.log("测试 6: 列出所有终端"); console.log("=".repeat(80)); const listResponse = await sendRequest("tools/call", { name: "list_terminals", arguments: {}, }); if (listResponse.result) { console.log("✓ 列出终端成功"); testsPassed++; } else { console.error("✗ 列出终端失败"); testsFailed++; } // 7. 终止终端 console.log("\n" + "=".repeat(80)); console.log("测试 7: 终止终端"); console.log("=".repeat(80)); const killResponse = await sendRequest("tools/call", { name: "kill_terminal", arguments: { terminalId: terminalId, }, }); if (killResponse.result) { console.log("✓ 终止终端成功"); testsPassed++; } else { console.error("✗ 终止终端失败"); testsFailed++; } } // 显示测试结果 console.log("\n" + "=".repeat(80)); console.log("测试结果汇总"); console.log("=".repeat(80)); console.log(`通过: ${testsPassed}`); console.log(`失败: ${testsFailed}`); console.log(`非 JSON 输出行数: ${nonJsonLines.length}`); console.log(); if (testsFailed > 0 || nonJsonLines.length > 0) { console.error("❌ 测试失败!"); if (nonJsonLines.length > 0) { console.error("\n发现非 JSON 输出:"); nonJsonLines.forEach((line, i) => { console.error(` ${i + 1}. ${line}`); }); } process.exit(1); } else { console.log("✅ 所有测试通过!MCP 服务器工作正常,stdout 通道纯净"); process.exit(0); } } catch (error) { console.error("测试过程中发生错误:", error); process.exit(1); } finally { server.kill("SIGTERM"); } } // 启动测试 runTests(); // 处理服务器退出 server.on("exit", (code, signal) => { console.log(`\n服务器已退出 (code: ${code}, signal: ${signal})`); }); // 处理错误 server.on("error", (error) => { console.error("服务器错误:", error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/masx200/persistent-terminal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server