@yeaft/webchat-agent 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/connection/message-router.js +17 -0
- package/context.js +2 -0
- package/index.js +15 -45
- package/mcp.js +83 -0
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from '../crew.js';
|
|
23
23
|
import { sendToServer, flushMessageBuffer } from './buffer.js';
|
|
24
24
|
import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
|
|
25
|
+
import { loadMcpServers, updateMcpConfig } from '../mcp.js';
|
|
25
26
|
|
|
26
27
|
export async function handleMessage(msg) {
|
|
27
28
|
switch (msg.type) {
|
|
@@ -54,6 +55,11 @@ export async function handleMessage(msg) {
|
|
|
54
55
|
|
|
55
56
|
// ★ Phase 1: 通知 server 同步完成
|
|
56
57
|
sendToServer({ type: 'agent_sync_complete' });
|
|
58
|
+
|
|
59
|
+
// ★ 发送 MCP servers 列表给 server(供前端 Settings > Tools tab 使用)
|
|
60
|
+
if (ctx.mcpServers.length > 0) {
|
|
61
|
+
sendToServer({ type: 'mcp_servers_list', servers: ctx.mcpServers });
|
|
62
|
+
}
|
|
57
63
|
break;
|
|
58
64
|
|
|
59
65
|
case 'create_conversation':
|
|
@@ -267,5 +273,16 @@ export async function handleMessage(msg) {
|
|
|
267
273
|
case 'upgrade_agent':
|
|
268
274
|
await handleUpgradeAgent();
|
|
269
275
|
break;
|
|
276
|
+
|
|
277
|
+
// MCP configuration
|
|
278
|
+
case 'get_mcp_servers':
|
|
279
|
+
sendToServer({ type: 'mcp_servers_list', servers: ctx.mcpServers });
|
|
280
|
+
break;
|
|
281
|
+
|
|
282
|
+
case 'update_mcp_config': {
|
|
283
|
+
const updated = updateMcpConfig(msg.config || {});
|
|
284
|
+
sendToServer({ type: 'mcp_config_updated', servers: updated });
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
270
287
|
}
|
|
271
288
|
}
|
package/context.js
CHANGED
|
@@ -14,6 +14,8 @@ export default {
|
|
|
14
14
|
agentCapabilities: [],
|
|
15
15
|
// Agent 级别的 slash commands 缓存(所有 conversation 共用)
|
|
16
16
|
slashCommands: [],
|
|
17
|
+
// MCP servers 列表 (从 ~/.claude.json 读取): [{ name, enabled, source }]
|
|
18
|
+
mcpServers: [],
|
|
17
19
|
// 连接相关
|
|
18
20
|
reconnectTimer: null,
|
|
19
21
|
pendingAuthTempId: null,
|
package/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import ctx from './context.js';
|
|
|
9
9
|
import { getConfigPath, loadServiceConfig } from './service.js';
|
|
10
10
|
import { loadNodePty } from './terminal.js';
|
|
11
11
|
import { connect } from './connection.js';
|
|
12
|
+
import { loadMcpServers } from './mcp.js';
|
|
12
13
|
|
|
13
14
|
const execAsync = promisify(exec);
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -62,58 +63,27 @@ const CONFIG = {
|
|
|
62
63
|
workDir: process.env.WORK_DIR || fileConfig.workDir || process.cwd(),
|
|
63
64
|
reconnectInterval: fileConfig.reconnectInterval,
|
|
64
65
|
agentSecret: process.env.AGENT_SECRET || fileConfig.agentSecret,
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
// 默认只允许 playwright
|
|
68
|
-
disallowedTools: (() => {
|
|
69
|
-
// 解析显式禁用列表
|
|
66
|
+
// 显式禁用的工具(非 MCP 相关)
|
|
67
|
+
explicitDisallowedTools: (() => {
|
|
70
68
|
const raw = process.env.DISALLOWED_TOOLS || fileConfig.disallowedTools || '';
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
if (existsSync(claudeConfigPath)) {
|
|
82
|
-
const claudeConfig = JSON.parse(readFileSync(claudeConfigPath, 'utf-8'));
|
|
83
|
-
const allMcpNames = new Set();
|
|
84
|
-
// 收集所有项目中配置的 MCP 服务器名
|
|
85
|
-
for (const [, projCfg] of Object.entries(claudeConfig.projects || {})) {
|
|
86
|
-
for (const name of Object.keys(projCfg.mcpServers || {})) {
|
|
87
|
-
allMcpNames.add(name);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
// 顶层 mcpServers
|
|
91
|
-
for (const name of Object.keys(claudeConfig.mcpServers || {})) {
|
|
92
|
-
allMcpNames.add(name);
|
|
93
|
-
}
|
|
94
|
-
// 不在白名单中的 MCP 服务器 → 禁用
|
|
95
|
-
for (const name of allMcpNames) {
|
|
96
|
-
if (!allowedMcpServers.includes(name)) {
|
|
97
|
-
mcpDisallowed.push(`mcp__${name}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (mcpDisallowed.length > 0) {
|
|
101
|
-
console.log(`[MCP] Allowed: ${allowedMcpServers.join(', ')}`);
|
|
102
|
-
console.log(`[MCP] Disallowed: ${mcpDisallowed.join(', ')}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
} catch (e) {
|
|
106
|
-
console.warn('[MCP] Failed to read ~/.claude.json:', e.message);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return [...explicit, ...mcpDisallowed];
|
|
110
|
-
})()
|
|
69
|
+
return raw === 'none' ? [] : raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
70
|
+
})(),
|
|
71
|
+
// MCP 白名单初始值(环境变量或配置文件指定)
|
|
72
|
+
allowedMcpServers: (() => {
|
|
73
|
+
const raw = process.env.ALLOWED_MCP_SERVERS || fileConfig.allowedMcpServers || 'playwright';
|
|
74
|
+
return raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
75
|
+
})(),
|
|
76
|
+
// disallowedTools 会在 loadMcpServers() 中计算
|
|
77
|
+
disallowedTools: []
|
|
111
78
|
};
|
|
112
79
|
|
|
113
80
|
// 初始化共享上下文
|
|
114
81
|
ctx.CONFIG = CONFIG;
|
|
115
82
|
ctx.saveConfig = saveConfig;
|
|
116
83
|
|
|
84
|
+
// 初始加载 MCP servers(必须在 ctx.CONFIG 赋值之后)
|
|
85
|
+
loadMcpServers();
|
|
86
|
+
|
|
117
87
|
// Agent capabilities(启动时自动检测)
|
|
118
88
|
async function detectCapabilities() {
|
|
119
89
|
const capabilities = ['background_tasks', 'file_editor'];
|
package/mcp.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import ctx from './context.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 读取 ~/.claude.json 中的 MCP servers 列表,
|
|
8
|
+
* 并根据 allowedMcpServers 白名单计算 disallowedTools。
|
|
9
|
+
* 返回 mcpServers 列表并同时存入 ctx.mcpServers。
|
|
10
|
+
*/
|
|
11
|
+
export function loadMcpServers() {
|
|
12
|
+
const claudeConfigPath = join(homedir(), '.claude.json');
|
|
13
|
+
const allMcpNames = new Set();
|
|
14
|
+
try {
|
|
15
|
+
if (existsSync(claudeConfigPath)) {
|
|
16
|
+
const claudeConfig = JSON.parse(readFileSync(claudeConfigPath, 'utf-8'));
|
|
17
|
+
for (const [, projCfg] of Object.entries(claudeConfig.projects || {})) {
|
|
18
|
+
for (const name of Object.keys(projCfg.mcpServers || {})) {
|
|
19
|
+
allMcpNames.add(name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (const name of Object.keys(claudeConfig.mcpServers || {})) {
|
|
23
|
+
allMcpNames.add(name);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.warn('[MCP] Failed to read ~/.claude.json:', e.message);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const allowed = ctx.CONFIG.allowedMcpServers;
|
|
31
|
+
const mcpServers = [];
|
|
32
|
+
for (const name of allMcpNames) {
|
|
33
|
+
mcpServers.push({
|
|
34
|
+
name,
|
|
35
|
+
enabled: allowed.includes(name),
|
|
36
|
+
source: name === 'playwright' ? 'Built-in' : 'MCP'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 重新计算 disallowedTools
|
|
41
|
+
recalcDisallowedTools(mcpServers);
|
|
42
|
+
ctx.mcpServers = mcpServers;
|
|
43
|
+
|
|
44
|
+
if (mcpServers.length > 0) {
|
|
45
|
+
const enabledNames = mcpServers.filter(s => s.enabled).map(s => s.name);
|
|
46
|
+
console.log(`[MCP] Enabled: ${enabledNames.join(', ') || 'none'}`);
|
|
47
|
+
const mcpDisallowed = mcpServers.filter(s => !s.enabled).map(s => s.name);
|
|
48
|
+
if (mcpDisallowed.length > 0) {
|
|
49
|
+
console.log(`[MCP] Disabled: ${mcpDisallowed.join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return mcpServers;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 更新 MCP servers 的 enabled 状态(从前端 toggle 触发)
|
|
58
|
+
* @param {Object<string, boolean>} config - { serverName: enabled }
|
|
59
|
+
*/
|
|
60
|
+
export function updateMcpConfig(config) {
|
|
61
|
+
for (const server of ctx.mcpServers) {
|
|
62
|
+
if (config[server.name] !== undefined) {
|
|
63
|
+
server.enabled = config[server.name];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 更新白名单
|
|
68
|
+
ctx.CONFIG.allowedMcpServers = ctx.mcpServers
|
|
69
|
+
.filter(s => s.enabled)
|
|
70
|
+
.map(s => s.name);
|
|
71
|
+
|
|
72
|
+
recalcDisallowedTools(ctx.mcpServers);
|
|
73
|
+
|
|
74
|
+
console.log(`[MCP] Config updated. Allowed: ${ctx.CONFIG.allowedMcpServers.join(', ') || 'none'}`);
|
|
75
|
+
return ctx.mcpServers;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function recalcDisallowedTools(mcpServers) {
|
|
79
|
+
const mcpDisallowed = mcpServers
|
|
80
|
+
.filter(s => !s.enabled)
|
|
81
|
+
.map(s => `mcp__${s.name}`);
|
|
82
|
+
ctx.CONFIG.disallowedTools = [...ctx.CONFIG.explicitDisallowedTools, ...mcpDisallowed];
|
|
83
|
+
}
|