Skip to content

Commit 46b6b99

Browse files
JackWang032JackWang032
andauthored
feat: add MCP
* feat: add MCP services * feat: add MCP web pages * feat: optimize web page UI and add inspector iframe * feat: enhance mcp services implements * feat: add mcp inspector agent process * chore: add @env alias and update node engines * feat: update sql template * fix: revert database config * fix: remove unnessary styles * fix: fix sql syntax * feat: refactor mcpProxy to agent process * feat: optimize markdown reviewer styles * fix: add agent args * fix: use cjs import for react-syntax-highlighter * fix: stdio args split with white-space * fix: remove useless endpoint * feat: add one-click fill options for Node and UV commands in MCP server registry * feat: enhance start script to manage MCP deployment directory and update build command * fix: update message endpoint construction to use forwarded protocol and hostname * fix: run lint:fix * chore: update CI.yml * chore: add NODE_OPTIONS for legacy OpenSSL support in CI workflow * fix: handle server-side rendering in InspectorIframe component * chore: update MCP health check schedule time * feat: update inspector to v0.17.5 * chore: add .nvmrc file specifying Node.js version 18.20.3 * feat: enhance file upload validation and logging for extraction process * chore: upgrade inspector and mcp sdk to compatible with 2025-11-25 mcp protocal version --------- Co-authored-by: JackWang032 <[email protected]>
1 parent 10af9d5 commit 46b6b99

File tree

59 files changed

+7987
-28
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+7987
-28
lines changed

.github/workflows/CI.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ on:
1111
jobs:
1212
setup:
1313
runs-on: ubuntu-latest
14+
env:
15+
NODE_OPTIONS: --openssl-legacy-provider
1416
strategy:
1517
matrix:
16-
node-version: [14.x, 16.x]
18+
node-version: [18.x]
1719
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
1820
steps:
1921
- name: Checkout code

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
18.20.3

agent.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
1+
const http = require('http');
2+
const env = require('./env.json');
13
const {
24
createTimedTask,
35
changeTimedTask,
46
cancelTimedTask,
57
timedTaskList,
68
timedTaskResult,
79
} = require('./app/utils/timedTask');
10+
const { startMcpInspector } = require('./app/agent/mcpInspector');
11+
const { MCPHttpHandler } = require('./app/agent/mcpHttpHandler');
12+
const { MCPProxy } = require('./app/mcp/mcpProxy');
13+
const { buildMCPConfig } = require('./app/utils');
814

915
// 接收 app 发送来的消息并作出反应
1016
module.exports = (agent) => {
17+
// 初始化MCP端点HTTP处理器
18+
const mcpHttpHandler = new MCPHttpHandler(agent);
19+
const mcpProxy = new MCPProxy(agent.logger);
20+
let httpServer = null;
21+
22+
// 等待Worker进程启动完成后再启动MCP服务
23+
agent.messenger.once('egg-ready', async () => {
24+
try {
25+
agent.logger.info('Worker进程已就绪,开始启动MCP端点HTTP服务...');
26+
27+
httpServer = await createHttpServer(agent, mcpHttpHandler);
28+
29+
// 启动所有已注册的MCP代理服务
30+
await startMCPServices(agent);
31+
} catch (error) {
32+
agent.logger.error('MCP端点HTTP服务启动失败:', error);
33+
}
34+
});
35+
1136
// 创建文章订阅任务
1237
agent.messenger.on('createTimedTask', ({ id, sendCron }) => {
1338
createTimedTask(id, sendCron, agent);
@@ -32,4 +57,169 @@ module.exports = (agent) => {
3257
agent.messenger.on('timedTaskResult', ({ result }) => {
3358
timedTaskResult(result, agent);
3459
});
60+
61+
// 处理MCP服务器启动请求
62+
agent.messenger.on('mcpStart', async ({ serverId, config }) => {
63+
try {
64+
await mcpProxy.startProxy(serverId, config);
65+
agent.logger.info(`MCP服务器启动成功 [${serverId}]`);
66+
67+
// 发送成功消息回 worker
68+
agent.messenger.sendRandom('mcpStartResult', {
69+
serverId,
70+
success: true,
71+
});
72+
} catch (error) {
73+
agent.logger.error(`MCP服务器启动失败 [${serverId}]:`, error);
74+
75+
// 发送失败消息回 worker
76+
agent.messenger.sendRandom('mcpStartResult', {
77+
serverId,
78+
success: false,
79+
error: error.message,
80+
});
81+
}
82+
});
83+
84+
// 处理MCP服务器停止请求
85+
agent.messenger.on('mcpStop', async ({ serverId }) => {
86+
try {
87+
await mcpProxy.stopProxy(serverId);
88+
agent.logger.info(`MCP服务器停止成功 [${serverId}]`);
89+
90+
// 发送成功消息回 worker
91+
agent.messenger.sendToApp('mcpStopResult', {
92+
serverId,
93+
success: true,
94+
});
95+
} catch (error) {
96+
agent.logger.error(`MCP服务器停止失败 [${serverId}]:`, error);
97+
98+
// 发送失败消息回 worker
99+
agent.messenger.sendRandom('mcpStopResult', {
100+
serverId,
101+
success: false,
102+
error: error.message,
103+
});
104+
}
105+
});
106+
107+
// 处理MCP服务器重启请求
108+
agent.messenger.on('mcpRestart', async ({ serverId }) => {
109+
try {
110+
await mcpProxy.restartProxy(serverId);
111+
agent.logger.info(`MCP服务器重启成功 [${serverId}]`);
112+
113+
// 发送成功消息回 worker
114+
agent.messenger.sendRandom('mcpRestartResult', {
115+
serverId,
116+
success: true,
117+
});
118+
} catch (error) {
119+
agent.logger.error(`MCP服务器重启失败 [${serverId}]:`, error);
120+
121+
// 发送失败消息回 worker
122+
agent.messenger.sendRandom('mcpRestartResult', {
123+
serverId,
124+
success: false,
125+
error: error.message,
126+
});
127+
}
128+
});
129+
130+
// 启动MCP Inspector
131+
startMcpInspector(agent);
132+
133+
// 进程退出前清理
134+
agent.beforeClose(async () => {
135+
agent.logger.info('Agent进程关闭,清理MCP服务...');
136+
137+
try {
138+
// 关闭HTTP服务器
139+
if (httpServer) {
140+
await new Promise((resolve) => {
141+
httpServer.close(() => {
142+
resolve();
143+
});
144+
});
145+
}
146+
147+
// 清理MCP代理
148+
await mcpProxy.cleanup();
149+
agent.logger.info('MCP服务清理完成');
150+
} catch (error) {
151+
agent.logger.error('MCP服务清理失败:', error);
152+
}
153+
});
35154
};
155+
156+
// 创建HTTP服务(用于处理MCP端点请求)
157+
async function createHttpServer(agent, mcpHttpHandler) {
158+
const port = env.mcpEndpointPort || 7005;
159+
160+
const server = http.createServer(async (req, res) => {
161+
await mcpHttpHandler.handleRequest(req, res);
162+
});
163+
164+
server.listen(port, () => {
165+
agent.logger.info(`MCP端点HTTP服务已启动,监听端口: ${port}`);
166+
});
167+
168+
server.on('error', (error) => {
169+
agent.logger.error('MCP端点HTTP服务错误:', error);
170+
});
171+
172+
return server;
173+
}
174+
175+
// 启动所有MCP服务,缓存配置信息
176+
async function startMCPServices(agent) {
177+
agent.logger.info('开始启动MCP服务...');
178+
179+
const ctx = agent.createAnonymousContext();
180+
181+
// 获取所有未删除的MCP服务器
182+
const enabledServers = await ctx.model.McpServer.findAll({
183+
where: {
184+
is_delete: 0,
185+
},
186+
});
187+
188+
if (enabledServers.length === 0) {
189+
agent.logger.info('无需要启动的MCP服务器');
190+
return;
191+
}
192+
193+
agent.logger.info(`启动${enabledServers.length}个MCP服务器...`);
194+
195+
let successCount = 0;
196+
let failCount = 0;
197+
198+
// 启动每个服务器
199+
for (const server of enabledServers) {
200+
try {
201+
// 构建MCP配置对象
202+
const config = buildMCPConfig(server);
203+
await MCPProxy.getInstance().startProxy(server.server_id, config);
204+
205+
successCount++;
206+
} catch (error) {
207+
agent.logger.error(`MCP服务器启动失败 [${server.server_id}]:`, error);
208+
209+
// 更新状态为错误
210+
await server.update({
211+
status: 'error',
212+
ping_error: error.message,
213+
last_ping_at: new Date(),
214+
});
215+
216+
failCount++;
217+
}
218+
}
219+
220+
setTimeout(() => {
221+
agent.messenger.sendRandom('mcpCheckAllServersHealth');
222+
}, 2000);
223+
224+
agent.logger.info(`MCP服务启动完成: 成功${successCount}个, 失败${failCount}个`);
225+
}

app.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,49 @@ module.exports = class AppBootHook {
2121
await ctx.service.articleSubscription.sendArticleSubscription(id);
2222
});
2323
});
24+
25+
// 监听 MCP 服务器启动结果
26+
app.messenger.on('mcpStartResult', (data) => {
27+
const { serverId, success, error } = data;
28+
const ctx = app.createAnonymousContext();
29+
ctx.runInBackground(async () => {
30+
await ctx.service.mcp.handleMCPStartResult(serverId, success, error);
31+
});
32+
});
33+
34+
// 监听 MCP 服务器停止结果
35+
app.messenger.on('mcpStopResult', (data) => {
36+
const { serverId, success, error } = data;
37+
const ctx = app.createAnonymousContext();
38+
ctx.runInBackground(async () => {
39+
await ctx.service.mcp.handleMCPStopResult(serverId, success, error);
40+
});
41+
});
42+
43+
// 监听 MCP 服务器重启结果
44+
app.messenger.on('mcpRestartResult', (data) => {
45+
const { serverId, success, error } = data;
46+
const ctx = app.createAnonymousContext();
47+
ctx.runInBackground(async () => {
48+
await ctx.service.mcp.handleMCPRestartResult(serverId, success, error);
49+
});
50+
});
51+
52+
// 监听 MCP 服务器健康检查请求
53+
app.messenger.on('mcpCheckAllServersHealth', () => {
54+
const ctx = app.createAnonymousContext();
55+
ctx.runInBackground(async () => {
56+
await ctx.service.mcp.checkAllServersHealth();
57+
});
58+
});
59+
60+
// 监听 MCP 请求埋点
61+
app.messenger.on('mcpTraceRequest', (data) => {
62+
const ctx = app.createAnonymousContext();
63+
ctx.runInBackground(async () => {
64+
const { serverId } = data;
65+
await ctx.service.mcp.incrementUseCount(serverId);
66+
});
67+
});
2468
}
2569
};

0 commit comments

Comments
 (0)