Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff4306a
feat: add MCP services
JackWang032 Sep 29, 2025
fb697cd
feat: add MCP web pages
JackWang032 Sep 29, 2025
f390371
feat: optimize web page UI and add inspector iframe
JackWang032 Oct 10, 2025
d70ab8a
feat: enhance mcp services implements
JackWang032 Oct 10, 2025
b7919f3
feat: add mcp inspector agent process
JackWang032 Oct 10, 2025
f3459c7
chore: add @env alias and update node engines
JackWang032 Oct 10, 2025
786ae16
feat: update sql template
JackWang032 Oct 10, 2025
740d2b3
fix: revert database config
JackWang032 Oct 10, 2025
43c6d9e
fix: remove unnessary styles
JackWang032 Oct 10, 2025
c68311a
fix: fix sql syntax
JackWang032 Oct 10, 2025
e2bb9f1
feat: refactor mcpProxy to agent process
JackWang032 Oct 17, 2025
108d407
feat: optimize markdown reviewer styles
JackWang032 Oct 22, 2025
612ad69
fix: add agent args
JackWang032 Oct 22, 2025
ced2bd2
fix: use cjs import for react-syntax-highlighter
JackWang032 Oct 22, 2025
8c83952
fix: stdio args split with white-space
JackWang032 Oct 23, 2025
1c537ca
fix: remove useless endpoint
JackWang032 Oct 27, 2025
2d68012
feat: add one-click fill options for Node and UV commands in MCP serv…
JackWang032 Oct 27, 2025
8a1fd0e
feat: enhance start script to manage MCP deployment directory and upd…
JackWang032 Oct 27, 2025
fbd180f
fix: update message endpoint construction to use forwarded protocol a…
JackWang032 Oct 27, 2025
cd0c577
fix: run lint:fix
JackWang032 Oct 27, 2025
ce3a16b
chore: update CI.yml
JackWang032 Oct 27, 2025
0988b03
chore: add NODE_OPTIONS for legacy OpenSSL support in CI workflow
JackWang032 Dec 14, 2025
ddb292a
fix: handle server-side rendering in InspectorIframe component
JackWang032 Dec 14, 2025
1e32fc5
chore: update MCP health check schedule time
JackWang032 Dec 14, 2025
9ff271b
feat: update inspector to v0.17.5
JackWang032 Dec 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ on:
jobs:
setup:
runs-on: ubuntu-latest
env:
NODE_OPTIONS: --openssl-legacy-provider
strategy:
matrix:
node-version: [14.x, 16.x]
node-version: [18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- name: Checkout code
Expand Down
190 changes: 190 additions & 0 deletions agent.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
const http = require('http');
const env = require('./env.json');
const {
createTimedTask,
changeTimedTask,
cancelTimedTask,
timedTaskList,
timedTaskResult,
} = require('./app/utils/timedTask');
const { startMcpInspector } = require('./app/agent/mcpInspector');
const { MCPHttpHandler } = require('./app/agent/mcpHttpHandler');
const { MCPProxy } = require('./app/mcp/mcpProxy');
const { buildMCPConfig } = require('./app/utils');

// 接收 app 发送来的消息并作出反应
module.exports = (agent) => {
// 初始化MCP端点HTTP处理器
const mcpHttpHandler = new MCPHttpHandler(agent);
const mcpProxy = new MCPProxy(agent.logger);
let httpServer = null;

// 等待Worker进程启动完成后再启动MCP服务
agent.messenger.once('egg-ready', async () => {
try {
agent.logger.info('Worker进程已就绪,开始启动MCP端点HTTP服务...');

httpServer = await createHttpServer(agent, mcpHttpHandler);

// 启动所有已注册的MCP代理服务
await startMCPServices(agent);
} catch (error) {
agent.logger.error('MCP端点HTTP服务启动失败:', error);
}
});

// 创建文章订阅任务
agent.messenger.on('createTimedTask', ({ id, sendCron }) => {
createTimedTask(id, sendCron, agent);
Expand All @@ -32,4 +57,169 @@
agent.messenger.on('timedTaskResult', ({ result }) => {
timedTaskResult(result, agent);
});

// 处理MCP服务器启动请求
agent.messenger.on('mcpStart', async ({ serverId, config }) => {
try {
await mcpProxy.startProxy(serverId, config);
agent.logger.info(`MCP服务器启动成功 [${serverId}]`);

// 发送成功消息回 worker
agent.messenger.sendRandom('mcpStartResult', {
serverId,
success: true,
});
} catch (error) {
agent.logger.error(`MCP服务器启动失败 [${serverId}]:`, error);

// 发送失败消息回 worker
agent.messenger.sendRandom('mcpStartResult', {
serverId,
success: false,
error: error.message,
});
}
});

// 处理MCP服务器停止请求
agent.messenger.on('mcpStop', async ({ serverId }) => {
try {
await mcpProxy.stopProxy(serverId);
agent.logger.info(`MCP服务器停止成功 [${serverId}]`);

// 发送成功消息回 worker
agent.messenger.sendToApp('mcpStopResult', {
serverId,
success: true,
});
} catch (error) {
agent.logger.error(`MCP服务器停止失败 [${serverId}]:`, error);

// 发送失败消息回 worker
agent.messenger.sendRandom('mcpStopResult', {
serverId,
success: false,
error: error.message,
});
}
});

// 处理MCP服务器重启请求
agent.messenger.on('mcpRestart', async ({ serverId }) => {
try {
await mcpProxy.restartProxy(serverId);
agent.logger.info(`MCP服务器重启成功 [${serverId}]`);

// 发送成功消息回 worker
agent.messenger.sendRandom('mcpRestartResult', {
serverId,
success: true,
});
} catch (error) {
agent.logger.error(`MCP服务器重启失败 [${serverId}]:`, error);

// 发送失败消息回 worker
agent.messenger.sendRandom('mcpRestartResult', {
serverId,
success: false,
error: error.message,
});
}
});

// 启动MCP Inspector
startMcpInspector(agent);

// 进程退出前清理
agent.beforeClose(async () => {
agent.logger.info('Agent进程关闭,清理MCP服务...');

try {
// 关闭HTTP服务器
if (httpServer) {
await new Promise((resolve) => {
httpServer.close(() => {
resolve();
});
});
}

// 清理MCP代理
await mcpProxy.cleanup();
agent.logger.info('MCP服务清理完成');
} catch (error) {
agent.logger.error('MCP服务清理失败:', error);
}
});
};

// 创建HTTP服务(用于处理MCP端点请求)
async function createHttpServer(agent, mcpHttpHandler) {
const port = env.mcpEndpointPort || 7005;

const server = http.createServer(async (req, res) => {
await mcpHttpHandler.handleRequest(req, res);
});

server.listen(port, () => {
agent.logger.info(`MCP端点HTTP服务已启动,监听端口: ${port}`);
});

server.on('error', (error) => {
agent.logger.error('MCP端点HTTP服务错误:', error);
});

return server;
}

// 启动所有MCP服务,缓存配置信息
async function startMCPServices(agent) {
agent.logger.info('开始启动MCP服务...');

const ctx = agent.createAnonymousContext();

// 获取所有未删除的MCP服务器
const enabledServers = await ctx.model.McpServer.findAll({
where: {
is_delete: 0,

Check warning on line 184 in agent.js

View workflow job for this annotation

GitHub Actions / setup (18.x)

Identifier 'is_delete' is not in camel case
},
});

if (enabledServers.length === 0) {
agent.logger.info('无需要启动的MCP服务器');
return;
}

agent.logger.info(`启动${enabledServers.length}个MCP服务器...`);

let successCount = 0;
let failCount = 0;

// 启动每个服务器
for (const server of enabledServers) {
try {
// 构建MCP配置对象
const config = buildMCPConfig(server);
await MCPProxy.getInstance().startProxy(server.server_id, config);

successCount++;
} catch (error) {
agent.logger.error(`MCP服务器启动失败 [${server.server_id}]:`, error);

// 更新状态为错误
await server.update({
status: 'error',
ping_error: error.message,

Check warning on line 212 in agent.js

View workflow job for this annotation

GitHub Actions / setup (18.x)

Identifier 'ping_error' is not in camel case
last_ping_at: new Date(),

Check warning on line 213 in agent.js

View workflow job for this annotation

GitHub Actions / setup (18.x)

Identifier 'last_ping_at' is not in camel case
});

failCount++;
}
}

setTimeout(() => {
agent.messenger.sendRandom('mcpCheckAllServersHealth');
}, 2000);

agent.logger.info(`MCP服务启动完成: 成功${successCount}个, 失败${failCount}个`);
}
44 changes: 44 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
this.app = app;
}
async serverDidReady() {
const { app } = this;

Check warning on line 9 in app.js

View workflow job for this annotation

GitHub Actions / setup (18.x)

Unexpected aliasing of members of 'this' to local variables
app.utils = utils;
const { cacheDirectory } = app.config;
if (!fs.existsSync(cacheDirectory)) {
Expand All @@ -21,5 +21,49 @@
await ctx.service.articleSubscription.sendArticleSubscription(id);
});
});

// 监听 MCP 服务器启动结果
app.messenger.on('mcpStartResult', (data) => {
const { serverId, success, error } = data;
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
await ctx.service.mcp.handleMCPStartResult(serverId, success, error);
});
});

// 监听 MCP 服务器停止结果
app.messenger.on('mcpStopResult', (data) => {
const { serverId, success, error } = data;
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
await ctx.service.mcp.handleMCPStopResult(serverId, success, error);
});
});

// 监听 MCP 服务器重启结果
app.messenger.on('mcpRestartResult', (data) => {
const { serverId, success, error } = data;
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
await ctx.service.mcp.handleMCPRestartResult(serverId, success, error);
});
});

// 监听 MCP 服务器健康检查请求
app.messenger.on('mcpCheckAllServersHealth', () => {
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
await ctx.service.mcp.checkAllServersHealth();
});
});

// 监听 MCP 请求埋点
app.messenger.on('mcpTraceRequest', (data) => {
const ctx = app.createAnonymousContext();
ctx.runInBackground(async () => {
const { serverId } = data;
await ctx.service.mcp.incrementUseCount(serverId);
});
});
}
};
Loading
Loading