mcp-interactive-feedback-server
Advanced tools
Comparing version
254
index.js
@@ -198,2 +198,29 @@ #!/usr/bin/env node | ||
} | ||
} else if (data.type === 'message') { | ||
// 处理用户主动发送的消息(非回答问题) | ||
log(`收到用户消息: ${data.message}`); | ||
// 这里可以添加处理用户消息的逻辑,比如转发给AI模型 | ||
} else if (data.type === 'stop_server') { | ||
log('收到停止服务器请求'); | ||
// 发送确认消息给所有客户端 | ||
if (state.wsServer) { | ||
state.wsServer.clients.forEach(client => { | ||
if (client.readyState === WebSocket.OPEN) { | ||
client.send(JSON.stringify({ | ||
type: 'server_stopping', | ||
message: '服务器正在关闭...' | ||
})); | ||
} | ||
}); | ||
} | ||
// 延迟一点时间让消息发送完成,然后优雅关闭服务器 | ||
setTimeout(async () => { | ||
log('用户请求停止服务器,开始优雅关闭...'); | ||
try { | ||
await gracefulShutdown('USER_REQUEST'); | ||
} catch (error) { | ||
log(`优雅关闭失败: ${error.message}`); | ||
process.exit(1); | ||
} | ||
}, 1000); | ||
} | ||
@@ -228,120 +255,147 @@ } catch (error) { | ||
// MCP服务器 | ||
const server = new McpServer({ | ||
name: 'interactive-feedback-mcp', | ||
version: '1.0.0', | ||
}); | ||
// MCP服务器封装 | ||
const mcpInstance = { | ||
mcpServer: null, | ||
transport: null, | ||
// 注册工具 | ||
server.tool( | ||
'ask_user_question', | ||
{ question: z.string().describe('要向用户提出的问题') }, | ||
async ({ question }) => { | ||
if (!question) throw new Error('问题不能为空'); | ||
try { | ||
if (!state.webServer) { | ||
return { content: [{ type: 'text', text: 'Web服务器未启动' }] }; | ||
} | ||
// 等待客户端连接 | ||
if (!state.wsServer || state.wsServer.clients.size === 0) { | ||
log(`等待客户端连接: http://localhost:${CONFIG.PORT}`); | ||
await new Promise(resolve => { | ||
const check = () => { | ||
if (state.wsServer && state.wsServer.clients.size > 0) { | ||
resolve(); | ||
} else { | ||
setTimeout(check, 1000); | ||
async start() { | ||
this.transport = new StdioServerTransport(); | ||
this.mcpServer = new McpServer({ | ||
name: "interactive-feedback-server", | ||
version: "1.0.3" | ||
}, { | ||
// ... (MCP配置) | ||
ask: async (ctx, request) => { | ||
state.questionId++; | ||
historyManager.addQuestion(state.questionId, request.question); | ||
if (state.wsServer) { | ||
state.wsServer.clients.forEach(client => { | ||
if (client.readyState === WebSocket.OPEN) { | ||
client.send(JSON.stringify({ | ||
type: 'new_question', | ||
questionId: state.questionId, | ||
question: request.question, | ||
history: historyManager.getAllHistory() // 发送完整历史以便客户端同步 | ||
})); | ||
} | ||
}; | ||
check(); | ||
}); | ||
} | ||
return new Promise((resolve, reject) => { | ||
state.pendingQuestions.set(state.questionId, { resolve, reject }); | ||
// 可选:设置超时 | ||
// setTimeout(() => { | ||
// if (state.pendingQuestions.has(state.questionId)) { | ||
// state.pendingQuestions.delete(state.questionId); | ||
// reject(new Error('提问超时')); | ||
// } | ||
// }, 60000); // 60秒超时 | ||
}); | ||
} | ||
// 发送问题 | ||
const questionId = ++state.questionId; | ||
historyManager.addQuestion(questionId, question); | ||
log(`发送问题 ${questionId}: ${question.substring(0, 50)}...`); | ||
// 广播新问题 | ||
if (state.wsServer) { | ||
state.wsServer.clients.forEach(client => { | ||
if (client.readyState === WebSocket.OPEN) { | ||
client.send(JSON.stringify({ | ||
type: 'new_question', | ||
questionId, | ||
question, | ||
history: historyManager.getAllHistory() | ||
})); | ||
} | ||
}); | ||
} | ||
// 等待回答 | ||
const answer = await new Promise((resolve, reject) => { | ||
state.pendingQuestions.set(questionId, { resolve, reject, question }); | ||
}); | ||
return { | ||
content: [{ type: 'text', text: `用户回答: ${answer}` }] | ||
}; | ||
} catch (error) { | ||
return { | ||
content: [{ type: 'text', text: `获取回答失败: ${error.message}` }] | ||
}; | ||
}); | ||
// 连接传输层 | ||
await this.mcpServer.connect(this.transport); | ||
log('MCP服务器已通过Stdio启动'); | ||
}, | ||
async stop() { | ||
if (this.mcpServer) { | ||
log('正在关闭MCP服务器...'); | ||
await this.mcpServer.close(); | ||
this.mcpServer = null; | ||
log('MCP服务器已关闭'); | ||
} | ||
if (this.transport) { | ||
// StdioTransport 可能不需要显式关闭,或者其关闭已集成在mcpServer.close()中 | ||
this.transport = null; | ||
} | ||
} | ||
); | ||
}; | ||
async function main() { | ||
try { | ||
historyManager.loadHistory(); | ||
await webServer.start(CONFIG.PORT); | ||
await mcpInstance.start(); | ||
log(`应用 "${require('./package.json').name}" 版本 "${require('./package.json').version}" 已成功启动。`); | ||
setupShutdownHandlers(); // <--- 添加调用 | ||
} catch (error) { | ||
log(`启动失败: ${error.message}`); | ||
console.error('启动失败:', error); | ||
process.exit(1); | ||
} | ||
} | ||
// 优雅关闭处理 | ||
function setupShutdownHandlers() { | ||
const shutdown = async (signal) => { | ||
log(`收到 ${signal} 信号,正在关闭...`); | ||
try { | ||
historyManager.saveHistory(); | ||
await webServer.stop(); | ||
process.exit(0); | ||
} catch (error) { | ||
log(`关闭失败: ${error.message}`); | ||
process.exit(1); | ||
} | ||
await gracefulShutdown(signal); | ||
}; | ||
// 监听退出信号 | ||
process.on('SIGINT', () => shutdown('SIGINT')); | ||
process.on('SIGTERM', () => shutdown('SIGTERM')); | ||
process.on('uncaughtException', (error) => { | ||
log(`未捕获异常: ${error.message}`); | ||
shutdown('uncaughtException'); | ||
}); | ||
process.on('unhandledRejection', (reason) => { | ||
log(`未处理的Promise拒绝: ${reason}`); | ||
shutdown('unhandledRejection'); | ||
}); | ||
log('已设置优雅关闭处理程序。'); | ||
} | ||
// 主函数 | ||
async function main() { | ||
// 优雅关闭函数(可被信号处理和用户请求共用) | ||
async function gracefulShutdown(source) { | ||
log(`接收到信号 ${source}。开始关闭服务...`); | ||
let exitCode = 0; | ||
try { | ||
setupShutdownHandlers(); | ||
// 加载历史记录 | ||
historyManager.loadHistory(); | ||
log('启动Web服务器...'); | ||
await webServer.start(CONFIG.PORT); | ||
log('Web服务器启动成功'); | ||
log('启动MCP服务器...'); | ||
const transport = new StdioServerTransport(); | ||
await server.connect(transport); | ||
log('MCP服务器启动成功'); | ||
log(`访问: http://localhost:${CONFIG.PORT}`); | ||
// 1. 关闭MCP服务器 (如果它需要先于WebSocket关闭) | ||
await mcpInstance.stop(); | ||
// 2. 关闭WebSocket服务器 | ||
if (state.wsServer) { | ||
log('正在关闭WebSocket服务器...'); | ||
await new Promise((resolve, reject) => { | ||
state.wsServer.clients.forEach(client => { | ||
if (client.readyState === WebSocket.OPEN) { | ||
client.terminate(); // 强制关闭客户端连接 | ||
} | ||
}); | ||
state.wsServer.close((err) => { | ||
if (err) { | ||
log(`关闭WebSocket服务器错误: ${err.message}`); | ||
// 不立即将exitCode设为1,继续尝试关闭HTTP服务器 | ||
// exitCode = 1; | ||
reject(err); // 但promise应该被reject | ||
return; | ||
} | ||
log('WebSocket服务器已关闭。'); | ||
state.wsServer = null; | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
// 3. 关闭HTTP服务器 | ||
if (state.webServer) { | ||
log('正在关闭HTTP服务器...'); | ||
await new Promise((resolve, reject) => { | ||
state.webServer.close((err) => { | ||
if (err) { | ||
log(`关闭HTTP服务器错误: ${err.message}`); | ||
// exitCode = 1; // 不立即将exitCode设为1 | ||
reject(err); // 但promise应该被reject | ||
return; | ||
} | ||
log('HTTP服务器已关闭。'); | ||
state.webServer = null; | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
} catch (error) { | ||
log(`启动失败: ${error.message}`); | ||
await webServer.stop(); | ||
process.exit(1); | ||
log(`关闭过程中发生错误: ${error.message}`); | ||
exitCode = 1; | ||
} finally { | ||
log(`服务关闭完成,退出码: ${exitCode}`); | ||
process.exit(exitCode); | ||
} | ||
@@ -348,0 +402,0 @@ } |
{ | ||
"name": "mcp-interactive-feedback-server", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"description": "一个简洁高效的MCP服务器,支持AI与用户的实时交互问答", | ||
@@ -49,4 +49,5 @@ "main": "index.js", | ||
"ws": "^8.18.2", | ||
"zod": "^3.25.32" | ||
"zod": "^3.25.32", | ||
"zod-to-json-schema": "^3.24.5" | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
529637
0.95%3070
1.86%5
25%+ Added