foliko 1.0.73 → 1.0.75
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/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
- package/.agent/ARCHITECTURE.md +288 -0
- package/.agent/agents/ambient-agent.md +57 -0
- package/.agent/agents/debugger.md +55 -0
- package/.agent/agents/email-assistant.md +49 -0
- package/.agent/agents/file-manager.md +42 -0
- package/.agent/agents/python-developer.md +60 -0
- package/.agent/agents/scheduler.md +59 -0
- package/.agent/agents/web-developer.md +45 -0
- package/.agent/data/default.json +29 -0
- package/.agent/data/plugins-state.json +255 -0
- package/.agent/mcp_config.json +4 -0
- package/.agent/mcp_config_updated.json +12 -0
- package/.agent/plugins.json +5 -0
- package/.agent/rules/GEMINI.md +273 -0
- package/.agent/rules/allow-rule.md +77 -0
- package/.agent/rules/log-rule.md +83 -0
- package/.agent/rules/security-rule.md +93 -0
- package/.agent/scripts/auto_preview.py +148 -0
- package/.agent/scripts/checklist.py +217 -0
- package/.agent/scripts/session_manager.py +120 -0
- package/.agent/scripts/verify_all.py +327 -0
- package/.agent/skills/api-patterns/SKILL.md +81 -0
- package/.agent/skills/api-patterns/api-style.md +42 -0
- package/.agent/skills/api-patterns/auth.md +24 -0
- package/.agent/skills/api-patterns/documentation.md +26 -0
- package/.agent/skills/api-patterns/graphql.md +41 -0
- package/.agent/skills/api-patterns/rate-limiting.md +31 -0
- package/.agent/skills/api-patterns/response.md +37 -0
- package/.agent/skills/api-patterns/rest.md +40 -0
- package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
- package/.agent/skills/api-patterns/security-testing.md +122 -0
- package/.agent/skills/api-patterns/trpc.md +41 -0
- package/.agent/skills/api-patterns/versioning.md +22 -0
- package/.agent/skills/app-builder/SKILL.md +75 -0
- package/.agent/skills/app-builder/agent-coordination.md +71 -0
- package/.agent/skills/app-builder/feature-building.md +53 -0
- package/.agent/skills/app-builder/project-detection.md +34 -0
- package/.agent/skills/app-builder/scaffolding.md +118 -0
- package/.agent/skills/app-builder/tech-stack.md +40 -0
- package/.agent/skills/app-builder/templates/SKILL.md +39 -0
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
- package/.agent/skills/architecture/SKILL.md +55 -0
- package/.agent/skills/architecture/context-discovery.md +43 -0
- package/.agent/skills/architecture/examples.md +94 -0
- package/.agent/skills/architecture/pattern-selection.md +68 -0
- package/.agent/skills/architecture/patterns-reference.md +50 -0
- package/.agent/skills/architecture/trade-off-analysis.md +77 -0
- package/.agent/skills/clean-code/SKILL.md +201 -0
- package/.agent/skills/doc.md +177 -0
- package/.agent/skills/frontend-design/SKILL.md +418 -0
- package/.agent/skills/frontend-design/animation-guide.md +331 -0
- package/.agent/skills/frontend-design/color-system.md +311 -0
- package/.agent/skills/frontend-design/decision-trees.md +418 -0
- package/.agent/skills/frontend-design/motion-graphics.md +306 -0
- package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/.agent/skills/frontend-design/typography-system.md +345 -0
- package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
- package/.agent/skills/frontend-design/visual-effects.md +383 -0
- package/.agent/skills/i18n-localization/SKILL.md +154 -0
- package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/.agent/skills/mcp-builder/SKILL.md +176 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
- package/.agent/workflows/brainstorm.md +113 -0
- package/.agent/workflows/create.md +59 -0
- package/.agent/workflows/debug.md +103 -0
- package/.agent/workflows/deploy.md +176 -0
- package/.agent/workflows/enhance.md +63 -0
- package/.agent/workflows/orchestrate.md +237 -0
- package/.agent/workflows/plan.md +89 -0
- package/.agent/workflows/preview.md +81 -0
- package/.agent/workflows/simple-test.md +42 -0
- package/.agent/workflows/status.md +86 -0
- package/.agent/workflows/structured-orchestrate.md +180 -0
- package/.agent/workflows/test.md +144 -0
- package/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/.claude/settings.local.json +157 -149
- package/.editorconfig +56 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc +7 -0
- package/.prettierignore +29 -0
- package/.prettierrc +11 -0
- package/CLAUDE.md +2 -0
- package/README.md +64 -55
- package/SPEC.md +102 -61
- package/cli/bin/foliko.js +4 -4
- package/cli/src/commands/chat.js +53 -51
- package/cli/src/commands/list.js +40 -37
- package/cli/src/index.js +18 -18
- package/cli/src/ui/chat-ui.js +78 -76
- package/cli/src/utils/ansi.js +15 -15
- package/cli/src/utils/markdown.js +112 -116
- package/docker-compose.yml +1 -1
- package/docs/ai-sdk-optimization.md +655 -636
- package/docs/features.md +80 -80
- package/docs/quick-reference.md +49 -46
- package/docs/user-manual.md +411 -380
- package/examples/ambient-example.js +95 -97
- package/examples/basic.js +115 -110
- package/examples/bootstrap.js +52 -43
- package/examples/mcp-example.js +56 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +60 -58
- package/examples/test-mcp.js +49 -43
- package/examples/test-reload.js +38 -40
- package/examples/test-telegram.js +3 -3
- package/examples/test-tg-bot.js +7 -4
- package/examples/test-tg-simple.js +4 -3
- package/examples/test-tg.js +3 -3
- package/examples/test-think.js +13 -7
- package/examples/test-web-plugin.js +61 -56
- package/examples/test-weixin-feishu.js +40 -37
- package/examples/workflow.js +49 -49
- package/foliko-1.0.75.tgz +0 -0
- package/package.json +37 -3
- package/plugins/ai-plugin.js +7 -5
- package/plugins/ambient-agent/EventWatcher.js +113 -0
- package/plugins/ambient-agent/ExplorerLoop.js +640 -0
- package/plugins/ambient-agent/GoalManager.js +197 -0
- package/plugins/ambient-agent/Reflector.js +95 -0
- package/plugins/ambient-agent/StateStore.js +90 -0
- package/plugins/ambient-agent/constants.js +101 -0
- package/plugins/ambient-agent/index.js +579 -0
- package/plugins/default-plugins.js +62 -49
- package/plugins/email/constants.js +64 -0
- package/plugins/email/handlers.js +461 -0
- package/plugins/email/index.js +278 -0
- package/plugins/email/monitor.js +269 -0
- package/plugins/email/parser.js +138 -0
- package/plugins/email/reply.js +151 -0
- package/plugins/email/utils.js +124 -0
- package/plugins/feishu-plugin.js +23 -19
- package/plugins/file-system-plugin.js +469 -120
- package/plugins/install-plugin.js +6 -4
- package/plugins/python-executor-plugin.js +3 -1
- package/plugins/python-plugin-loader.js +10 -8
- package/plugins/rules-plugin.js +5 -3
- package/plugins/scheduler-plugin.js +18 -16
- package/plugins/session-plugin.js +3 -1
- package/plugins/storage-plugin.js +5 -3
- package/plugins/subagent-plugin.js +152 -92
- package/plugins/telegram-plugin.js +26 -19
- package/plugins/think-plugin.js +4 -2
- package/plugins/tools-plugin.js +3 -1
- package/plugins/web-plugin.js +15 -13
- package/plugins/weixin-plugin.js +43 -36
- package/reports/system-health-report-20260401.md +79 -0
- package/skills/ambient-agent/SKILL.md +49 -39
- package/skills/foliko-dev/AGENTS.md +64 -61
- package/skills/foliko-dev/SKILL.md +125 -119
- package/skills/mcp-usage/SKILL.md +19 -17
- package/skills/python-plugin-dev/SKILL.md +16 -15
- package/skills/skill-guide/SKILL.md +12 -12
- package/skills/subagent-guide/SKILL.md +237 -0
- package/skills/workflow-guide/SKILL.md +90 -45
- package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
- package/skills/workflow-troubleshooting/SKILL.md +156 -79
- package/src/capabilities/index.js +4 -4
- package/src/capabilities/skill-manager.js +211 -197
- package/src/capabilities/workflow-engine.js +461 -547
- package/src/core/agent-chat.js +426 -279
- package/src/core/agent.js +453 -248
- package/src/core/framework.js +183 -149
- package/src/core/index.js +8 -8
- package/src/core/plugin-base.js +52 -52
- package/src/core/plugin-manager.js +377 -281
- package/src/core/provider.js +35 -32
- package/src/core/sub-agent-config.js +264 -0
- package/src/core/system-prompt-builder.js +120 -0
- package/src/core/tool-registry.js +416 -33
- package/src/core/tool-router.js +149 -68
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +269 -257
- package/src/index.js +5 -17
- package/src/utils/circuit-breaker.js +301 -0
- package/src/utils/error-boundary.js +363 -0
- package/src/utils/error.js +374 -0
- package/src/utils/event-emitter.js +20 -20
- package/src/utils/id.js +133 -0
- package/src/utils/index.js +217 -3
- package/src/utils/logger.js +181 -0
- package/src/utils/plugin-helpers.js +90 -0
- package/src/utils/retry.js +122 -0
- package/src/utils/sandbox.js +292 -0
- package/test/tool-registry-validation.test.js +218 -0
- package/test_report.md +70 -0
- package/website/docs/api.html +169 -107
- package/website/docs/configuration.html +296 -144
- package/website/docs/plugin-development.html +154 -85
- package/website/docs/project-structure.html +110 -109
- package/website/docs/skill-development.html +117 -61
- package/website/index.html +209 -205
- package/website/script.js +20 -17
- package/website/styles.css +1 -1
- package/plugins/ambient-agent-plugin.js +0 -1565
- package/plugins/email.js +0 -1142
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* 使用 @ai-sdk/mcp 连接到 MCP 服务器
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { Plugin } = require('../core/plugin-base')
|
|
7
|
-
const { spawn } = require('child_process')
|
|
8
|
-
const fs = require('fs')
|
|
9
|
-
const path = require('path')
|
|
10
|
-
const { z } = require('zod')
|
|
11
|
-
const {createMCPClient} = require('@ai-sdk/mcp')
|
|
6
|
+
const { Plugin } = require('../core/plugin-base');
|
|
7
|
+
const { spawn } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { z } = require('zod');
|
|
11
|
+
const { createMCPClient } = require('@ai-sdk/mcp');
|
|
12
12
|
const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -16,72 +16,72 @@ const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio
|
|
|
16
16
|
*/
|
|
17
17
|
class MCPClientWrapper {
|
|
18
18
|
constructor(options = {}) {
|
|
19
|
-
this.serverName = options.serverName || 'unknown'
|
|
20
|
-
this.command = options.command
|
|
21
|
-
this.args = options.args || []
|
|
22
|
-
this.env = options.env || {}
|
|
23
|
-
this.timeout = options.timeout || 30000
|
|
24
|
-
this.priority = 11
|
|
25
|
-
this.client = null
|
|
26
|
-
this.connected = false
|
|
27
|
-
this.tools = []
|
|
19
|
+
this.serverName = options.serverName || 'unknown';
|
|
20
|
+
this.command = options.command;
|
|
21
|
+
this.args = options.args || [];
|
|
22
|
+
this.env = options.env || {};
|
|
23
|
+
this.timeout = options.timeout || 30000;
|
|
24
|
+
this.priority = 11;
|
|
25
|
+
this.client = null;
|
|
26
|
+
this.connected = false;
|
|
27
|
+
this.tools = [];
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* 连接 MCP 服务器
|
|
32
32
|
*/
|
|
33
33
|
async connect() {
|
|
34
|
-
if (this.connected) return
|
|
34
|
+
if (this.connected) return;
|
|
35
35
|
|
|
36
36
|
try {
|
|
37
37
|
// 动态导入 @ai-sdk/mcp
|
|
38
|
-
let createMCPClient
|
|
38
|
+
let createMCPClient;
|
|
39
39
|
|
|
40
40
|
try {
|
|
41
|
-
const mcpModule = require('@ai-sdk/mcp')
|
|
42
|
-
createMCPClient = mcpModule.createMCPClient || mcpModule.experimental_createMCPClient
|
|
41
|
+
const mcpModule = require('@ai-sdk/mcp');
|
|
42
|
+
createMCPClient = mcpModule.createMCPClient || mcpModule.experimental_createMCPClient;
|
|
43
43
|
} catch (err) {
|
|
44
|
-
throw new Error('@ai-sdk/mcp not installed. Please run: npm install @ai-sdk/mcp')
|
|
44
|
+
throw new Error('@ai-sdk/mcp not installed. Please run: npm install @ai-sdk/mcp');
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
if (!createMCPClient) {
|
|
48
|
-
throw new Error('createMCPClient not available')
|
|
48
|
+
throw new Error('createMCPClient not available');
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// 解析命令路径
|
|
52
|
-
const { which, shellResolve } = this._resolveCommand(this.command)
|
|
52
|
+
const { which, shellResolve } = this._resolveCommand(this.command);
|
|
53
53
|
|
|
54
54
|
// 使用正确的子路径导入
|
|
55
55
|
try {
|
|
56
|
-
const { Experimental_StdioMCPTransport } = require('@ai-sdk/mcp/mcp-stdio')
|
|
56
|
+
const { Experimental_StdioMCPTransport } = require('@ai-sdk/mcp/mcp-stdio');
|
|
57
57
|
const transport = new Experimental_StdioMCPTransport({
|
|
58
58
|
command: which || this.command,
|
|
59
59
|
args: shellResolve ? [shellResolve, ...this.args] : this.args,
|
|
60
|
-
env: { ...process.env, ...this.env }
|
|
61
|
-
})
|
|
62
|
-
this.client = await createMCPClient({ transport })
|
|
60
|
+
env: { ...process.env, ...this.env },
|
|
61
|
+
});
|
|
62
|
+
this.client = await createMCPClient({ transport });
|
|
63
63
|
} catch (transportErr) {
|
|
64
|
-
|
|
65
|
-
throw transportErr
|
|
64
|
+
log.error(` Transport error:`, transportErr.message);
|
|
65
|
+
throw transportErr;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// 获取工具列表
|
|
69
69
|
// 新版 API: tools() 是函数
|
|
70
70
|
if (typeof this.client.tools === 'function') {
|
|
71
|
-
const toolsObject = await this.client.tools()
|
|
71
|
+
const toolsObject = await this.client.tools();
|
|
72
72
|
this.tools = Object.entries(toolsObject)
|
|
73
73
|
.filter(([, tool]) => tool != null)
|
|
74
|
-
.map(([name, tool]) => ({ ...tool, name }))
|
|
74
|
+
.map(([name, tool]) => ({ ...tool, name }));
|
|
75
75
|
} else if (this.client.tools && typeof this.client.tools.list === 'function') {
|
|
76
76
|
// 旧版 API: tools.list()
|
|
77
|
-
const listResult = await this.client.tools.list()
|
|
78
|
-
this.tools = listResult.tools || []
|
|
77
|
+
const listResult = await this.client.tools.list();
|
|
78
|
+
this.tools = listResult.tools || [];
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
this.connected = true
|
|
82
|
-
|
|
81
|
+
this.connected = true;
|
|
82
|
+
log.info(` Connected to ${this.serverName} with ${this.tools.length} tools`);
|
|
83
83
|
} catch (err) {
|
|
84
|
-
|
|
84
|
+
log.error(` Failed to connect to ${this.serverName}:`, err.message);
|
|
85
85
|
// 不抛出错误,让框架继续运行
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -90,18 +90,18 @@ class MCPClientWrapper {
|
|
|
90
90
|
* 解析命令路径
|
|
91
91
|
*/
|
|
92
92
|
_resolveCommand(cmd) {
|
|
93
|
-
const isWindows = process.platform === 'win32'
|
|
94
|
-
const ext = isWindows ? '.cmd' : ''
|
|
95
|
-
const withExt = cmd + ext
|
|
93
|
+
const isWindows = process.platform === 'win32';
|
|
94
|
+
const ext = isWindows ? '.cmd' : '';
|
|
95
|
+
const withExt = cmd + ext;
|
|
96
96
|
|
|
97
97
|
// 检查命令是否直接存在
|
|
98
98
|
try {
|
|
99
99
|
const result = spawn.sync(withExt, ['--version'], {
|
|
100
100
|
stdio: 'ignore',
|
|
101
|
-
timeout: 2000
|
|
102
|
-
})
|
|
101
|
+
timeout: 2000,
|
|
102
|
+
});
|
|
103
103
|
if (result.error?.code !== 'ENOENT') {
|
|
104
|
-
return { which: withExt, shellResolve: null }
|
|
104
|
+
return { which: withExt, shellResolve: null };
|
|
105
105
|
}
|
|
106
106
|
} catch (e) {
|
|
107
107
|
// ignore
|
|
@@ -111,21 +111,21 @@ class MCPClientWrapper {
|
|
|
111
111
|
const globalCommands = isWindows
|
|
112
112
|
? [
|
|
113
113
|
path.join(process.env.APPDATA || '', 'npm', withExt),
|
|
114
|
-
path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'npm', withExt)
|
|
114
|
+
path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'npm', withExt),
|
|
115
115
|
]
|
|
116
|
-
: ['/usr/local/bin/' + cmd, '/usr/bin/' + cmd]
|
|
116
|
+
: ['/usr/local/bin/' + cmd, '/usr/bin/' + cmd];
|
|
117
117
|
|
|
118
118
|
for (const cmdPath of globalCommands) {
|
|
119
119
|
try {
|
|
120
120
|
if (fs.existsSync(cmdPath)) {
|
|
121
|
-
return { which: cmdPath, shellResolve: null }
|
|
121
|
+
return { which: cmdPath, shellResolve: null };
|
|
122
122
|
}
|
|
123
123
|
} catch (e) {
|
|
124
124
|
// ignore
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
return { which: cmd, shellResolve: null }
|
|
128
|
+
return { which: cmd, shellResolve: null };
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
@@ -133,36 +133,36 @@ class MCPClientWrapper {
|
|
|
133
133
|
*/
|
|
134
134
|
async callTool(toolName, args) {
|
|
135
135
|
if (!this.connected) {
|
|
136
|
-
await this.connect()
|
|
136
|
+
await this.connect();
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
if (!this.client) {
|
|
140
|
-
throw new Error('MCP client not initialized')
|
|
140
|
+
throw new Error('MCP client not initialized');
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
try {
|
|
144
144
|
// 新版 API: tools() 返回对象
|
|
145
|
-
let toolsObject
|
|
145
|
+
let toolsObject;
|
|
146
146
|
if (typeof this.client.tools === 'function') {
|
|
147
|
-
toolsObject = await this.client.tools()
|
|
147
|
+
toolsObject = await this.client.tools();
|
|
148
148
|
} else if (this.client.tools && typeof this.client.tools.list === 'function') {
|
|
149
149
|
// 旧版 API
|
|
150
|
-
const listResult = await this.client.tools.list()
|
|
151
|
-
toolsObject = listResult.tools || {}
|
|
150
|
+
const listResult = await this.client.tools.list();
|
|
151
|
+
toolsObject = listResult.tools || {};
|
|
152
152
|
} else {
|
|
153
|
-
throw new Error('Cannot get tools from MCP client')
|
|
153
|
+
throw new Error('Cannot get tools from MCP client');
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
const tool = toolsObject[toolName]
|
|
156
|
+
const tool = toolsObject[toolName];
|
|
157
157
|
if (!tool) {
|
|
158
|
-
throw new Error(`Tool not found: ${toolName}`)
|
|
158
|
+
throw new Error(`Tool not found: ${toolName}`);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
const result = await tool.execute(args)
|
|
162
|
-
return result
|
|
161
|
+
const result = await tool.execute(args);
|
|
162
|
+
return result;
|
|
163
163
|
} catch (err) {
|
|
164
|
-
|
|
165
|
-
throw err
|
|
164
|
+
log.error(` Tool call failed: ${toolName}:`, err.message);
|
|
165
|
+
throw err;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -172,13 +172,13 @@ class MCPClientWrapper {
|
|
|
172
172
|
async disconnect() {
|
|
173
173
|
if (this.client) {
|
|
174
174
|
try {
|
|
175
|
-
await this.client.destroy()
|
|
175
|
+
await this.client.destroy();
|
|
176
176
|
} catch (e) {
|
|
177
177
|
// ignore
|
|
178
178
|
}
|
|
179
|
-
this.client = null
|
|
180
|
-
this.connected = false
|
|
181
|
-
this.tools = []
|
|
179
|
+
this.client = null;
|
|
180
|
+
this.connected = false;
|
|
181
|
+
this.tools = [];
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
}
|
|
@@ -188,29 +188,29 @@ class MCPClientWrapper {
|
|
|
188
188
|
*/
|
|
189
189
|
class MCPExecutorPlugin extends Plugin {
|
|
190
190
|
constructor(config = {}) {
|
|
191
|
-
super()
|
|
192
|
-
this.name = 'mcp-executor'
|
|
193
|
-
this.version = '1.0.0'
|
|
194
|
-
this.description = 'MCP (Model Context Protocol) 执行器'
|
|
195
|
-
this.priority = 11
|
|
196
|
-
this.system = true
|
|
197
|
-
|
|
198
|
-
this._framework = null
|
|
191
|
+
super();
|
|
192
|
+
this.name = 'mcp-executor';
|
|
193
|
+
this.version = '1.0.0';
|
|
194
|
+
this.description = 'MCP (Model Context Protocol) 执行器';
|
|
195
|
+
this.priority = 11;
|
|
196
|
+
this.system = true;
|
|
197
|
+
|
|
198
|
+
this._framework = null;
|
|
199
199
|
// serverName -> { client, tools: [{ name, description, inputSchema }], toolObjects: { toolName: actualTool } }
|
|
200
|
-
this._clients = new Map()
|
|
201
|
-
this._config = config
|
|
200
|
+
this._clients = new Map();
|
|
201
|
+
this._config = config;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
install(framework) {
|
|
205
|
-
this._framework = framework
|
|
206
|
-
return this
|
|
205
|
+
this._framework = framework;
|
|
206
|
+
return this;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
async start(framework) {
|
|
210
210
|
// 先连接所有 MCP 服务器
|
|
211
211
|
if (this._config.servers) {
|
|
212
212
|
for (const serverConfig of this._config.servers) {
|
|
213
|
-
await this.addServer(serverConfig)
|
|
213
|
+
await this.addServer(serverConfig);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
@@ -220,60 +220,60 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
220
220
|
description: '查询工具的调用示例,直接复制 example 或 fullExample 字段使用',
|
|
221
221
|
inputSchema: z.object({
|
|
222
222
|
server: z.string().describe('MCP 服务器名称'),
|
|
223
|
-
tool: z.string().describe('工具名称')
|
|
223
|
+
tool: z.string().describe('工具名称'),
|
|
224
224
|
}),
|
|
225
225
|
execute: async (args) => {
|
|
226
|
-
const { server, tool } = args
|
|
227
|
-
const clientInfo = this._clients.get(server)
|
|
226
|
+
const { server, tool } = args;
|
|
227
|
+
const clientInfo = this._clients.get(server);
|
|
228
228
|
if (!clientInfo) {
|
|
229
|
-
return { success: false, error: `MCP server '${server}' not found` }
|
|
229
|
+
return { success: false, error: `MCP server '${server}' not found` };
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
const toolInfo = clientInfo.tools.find(t => t.name === tool)
|
|
232
|
+
const toolInfo = clientInfo.tools.find((t) => t.name === tool);
|
|
233
233
|
if (!toolInfo) {
|
|
234
|
-
return { success: false, error: `Tool '${tool}' not found` }
|
|
234
|
+
return { success: false, error: `Tool '${tool}' not found` };
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
const schema = this._extractSchema(toolInfo.inputSchema)
|
|
238
|
-
const props = schema.properties || {}
|
|
239
|
-
const required = schema.required || []
|
|
237
|
+
const schema = this._extractSchema(toolInfo.inputSchema);
|
|
238
|
+
const props = schema.properties || {};
|
|
239
|
+
const required = schema.required || [];
|
|
240
240
|
|
|
241
241
|
// 生成示例值的辅助函数
|
|
242
242
|
const generateExample = (prop, depth = 0) => {
|
|
243
|
-
if (!prop) return null
|
|
244
|
-
const type = prop.type || (prop.properties ? 'object' : 'string')
|
|
243
|
+
if (!prop) return null;
|
|
244
|
+
const type = prop.type || (prop.properties ? 'object' : 'string');
|
|
245
245
|
|
|
246
246
|
if (type === 'string') {
|
|
247
|
-
return prop.description?.split('.')[0] || `${prop.title || '值'}
|
|
247
|
+
return prop.description?.split('.')[0] || `${prop.title || '值'}`;
|
|
248
248
|
} else if (type === 'number' || type === 'integer') {
|
|
249
|
-
return prop.default || 0
|
|
249
|
+
return prop.default || 0;
|
|
250
250
|
} else if (type === 'boolean') {
|
|
251
|
-
return false
|
|
251
|
+
return false;
|
|
252
252
|
} else if (type === 'array') {
|
|
253
253
|
// 数组类型:提供单个示例元素
|
|
254
254
|
if (prop.items) {
|
|
255
|
-
const itemExample = generateExample(prop.items, depth + 1)
|
|
256
|
-
return depth === 0 ? [itemExample] : itemExample
|
|
255
|
+
const itemExample = generateExample(prop.items, depth + 1);
|
|
256
|
+
return depth === 0 ? [itemExample] : itemExample;
|
|
257
257
|
}
|
|
258
|
-
return ['示例']
|
|
258
|
+
return ['示例'];
|
|
259
259
|
} else if (type === 'object' || prop.properties) {
|
|
260
260
|
// 对象类型:递归生成嵌套结构
|
|
261
|
-
const obj = {}
|
|
262
|
-
const objProps = prop.properties || {}
|
|
261
|
+
const obj = {};
|
|
262
|
+
const objProps = prop.properties || {};
|
|
263
263
|
for (const [key, val] of Object.entries(objProps)) {
|
|
264
|
-
obj[key] = generateExample(val, depth + 1)
|
|
264
|
+
obj[key] = generateExample(val, depth + 1);
|
|
265
265
|
}
|
|
266
|
-
return obj
|
|
266
|
+
return obj;
|
|
267
267
|
}
|
|
268
|
-
return null
|
|
269
|
-
}
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
270
270
|
|
|
271
271
|
// 生成可直接使用的调用示例
|
|
272
|
-
const exampleArgs = {}
|
|
272
|
+
const exampleArgs = {};
|
|
273
273
|
for (const key of required) {
|
|
274
|
-
const prop = props[key]
|
|
274
|
+
const prop = props[key];
|
|
275
275
|
if (prop) {
|
|
276
|
-
exampleArgs[key] = generateExample(prop)
|
|
276
|
+
exampleArgs[key] = generateExample(prop);
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -285,76 +285,82 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
285
285
|
required,
|
|
286
286
|
// 各参数的详细说明
|
|
287
287
|
parameters: Object.fromEntries(
|
|
288
|
-
required.map(key => [
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
required.map((key) => [
|
|
289
|
+
key,
|
|
290
|
+
{
|
|
291
|
+
type: props[key]?.type || 'string',
|
|
292
|
+
description: props[key]?.description || '',
|
|
293
|
+
example: generateExample(props[key]),
|
|
294
|
+
},
|
|
295
|
+
])
|
|
293
296
|
),
|
|
294
297
|
// 直接返回可以复制使用的调用示例
|
|
295
298
|
example: JSON.stringify(exampleArgs, null, 2),
|
|
296
|
-
fullExample: `mcp_call({ server: "${server}", tool: "${tool}", args_json: ${JSON.stringify(JSON.stringify(exampleArgs))} })
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
})
|
|
299
|
+
fullExample: `mcp_call({ server: "${server}", tool: "${tool}", args_json: ${JSON.stringify(JSON.stringify(exampleArgs))} })`,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
});
|
|
301
304
|
|
|
302
305
|
// 注册 mcp_call 工具
|
|
303
306
|
framework.registerTool({
|
|
304
307
|
name: 'mcp_call',
|
|
305
|
-
description:
|
|
308
|
+
description:
|
|
309
|
+
'调用 MCP 服务器工具。args_json 是包含实际参数的 JSON 字符串,如 \'{"url": "https://news.baidu.com"}\'',
|
|
306
310
|
inputSchema: z.object({
|
|
307
311
|
server: z.string().describe('MCP 服务器名称'),
|
|
308
312
|
tool: z.string().describe('工具名称'),
|
|
309
|
-
args_json: z
|
|
313
|
+
args_json: z
|
|
314
|
+
.string()
|
|
315
|
+
.describe('参数 JSON 字符串,如 {"url": "https://..."},fetch 工具必填 url'),
|
|
310
316
|
}),
|
|
311
317
|
execute: async (args) => {
|
|
312
|
-
const { server, tool, args_json } = args
|
|
313
|
-
|
|
318
|
+
const { server, tool, args_json } = args;
|
|
319
|
+
log.info(` mcp_call: server=${server}, tool=${tool}, args_json=${args_json}`);
|
|
314
320
|
|
|
315
|
-
const clientInfo = this._clients.get(server)
|
|
321
|
+
const clientInfo = this._clients.get(server);
|
|
316
322
|
if (!clientInfo) {
|
|
317
|
-
return { success: false, error: `MCP server '${server}' not found` }
|
|
323
|
+
return { success: false, error: `MCP server '${server}' not found` };
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
// 解析 args_json
|
|
321
|
-
let finalArgs = {}
|
|
327
|
+
let finalArgs = {};
|
|
322
328
|
if (args_json) {
|
|
323
329
|
try {
|
|
324
|
-
finalArgs = JSON.parse(args_json)
|
|
330
|
+
finalArgs = JSON.parse(args_json);
|
|
325
331
|
} catch (e) {
|
|
326
|
-
return { success: false, error: `args_json 格式错误,不是有效的 JSON: ${args_json}` }
|
|
332
|
+
return { success: false, error: `args_json 格式错误,不是有效的 JSON: ${args_json}` };
|
|
327
333
|
}
|
|
328
334
|
}
|
|
329
335
|
|
|
330
336
|
// 检查参数是否为空
|
|
331
337
|
if (!finalArgs || Object.keys(finalArgs).length === 0) {
|
|
332
|
-
const toolInfo = clientInfo.tools.find(t => t.name === tool)
|
|
338
|
+
const toolInfo = clientInfo.tools.find((t) => t.name === tool);
|
|
333
339
|
if (toolInfo) {
|
|
334
|
-
const schema = this._extractSchema(toolInfo.inputSchema)
|
|
335
|
-
const required = schema.required || []
|
|
340
|
+
const schema = this._extractSchema(toolInfo.inputSchema);
|
|
341
|
+
const required = schema.required || [];
|
|
336
342
|
return {
|
|
337
343
|
success: false,
|
|
338
344
|
error: `参数为空!必须提供: ${required.join(', ')}`,
|
|
339
|
-
hint: `正确格式: {"${required[0]}": "具体值"}
|
|
340
|
-
}
|
|
345
|
+
hint: `正确格式: {"${required[0]}": "具体值"}`,
|
|
346
|
+
};
|
|
341
347
|
}
|
|
342
348
|
}
|
|
343
349
|
|
|
344
350
|
try {
|
|
345
351
|
// 使用缓存的工具对象
|
|
346
|
-
const mcpTool = clientInfo.toolObjects?.[tool]
|
|
352
|
+
const mcpTool = clientInfo.toolObjects?.[tool];
|
|
347
353
|
if (!mcpTool) {
|
|
348
|
-
return { success: false, error: `Tool '${tool}' not found on server '${server}'` }
|
|
354
|
+
return { success: false, error: `Tool '${tool}' not found on server '${server}'` };
|
|
349
355
|
}
|
|
350
|
-
const execResult = await mcpTool.execute(finalArgs)
|
|
351
|
-
return { success: true, result: execResult }
|
|
356
|
+
const execResult = await mcpTool.execute(finalArgs);
|
|
357
|
+
return { success: true, result: execResult };
|
|
352
358
|
} catch (err) {
|
|
353
|
-
|
|
354
|
-
return { success: false, error: err.message }
|
|
359
|
+
log.error(` Tool '${tool}' failed:`, err.message);
|
|
360
|
+
return { success: false, error: err.message };
|
|
355
361
|
}
|
|
356
|
-
}
|
|
357
|
-
})
|
|
362
|
+
},
|
|
363
|
+
});
|
|
358
364
|
|
|
359
365
|
// 注册 mcp_list_servers 工具
|
|
360
366
|
framework.registerTool({
|
|
@@ -362,89 +368,94 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
362
368
|
description: '列出已连接的 MCP 服务器及其工具',
|
|
363
369
|
inputSchema: z.object({}),
|
|
364
370
|
execute: async () => {
|
|
365
|
-
const servers = []
|
|
371
|
+
const servers = [];
|
|
366
372
|
for (const [name, info] of this._clients) {
|
|
367
373
|
servers.push({
|
|
368
374
|
name,
|
|
369
|
-
tools: info.tools.map(t => ({ name: t.name, description: t.description }))
|
|
370
|
-
})
|
|
375
|
+
tools: info.tools.map((t) => ({ name: t.name, description: t.description })),
|
|
376
|
+
});
|
|
371
377
|
}
|
|
372
|
-
return { success: true, servers }
|
|
373
|
-
}
|
|
374
|
-
})
|
|
378
|
+
return { success: true, servers };
|
|
379
|
+
},
|
|
380
|
+
});
|
|
375
381
|
|
|
376
382
|
// 注册 mcp_reload 工具
|
|
377
383
|
framework.registerTool({
|
|
378
384
|
name: 'mcp_reload',
|
|
379
|
-
description:
|
|
385
|
+
description:
|
|
386
|
+
'重新加载 MCP 服务器配置。当配置文件 .agent/mcp_config.json 修改后使用此工具重载配置',
|
|
380
387
|
inputSchema: z.object({}),
|
|
381
388
|
execute: async () => {
|
|
382
|
-
|
|
389
|
+
log.info(' mcp_reload called');
|
|
383
390
|
try {
|
|
384
|
-
const fs = require('fs')
|
|
385
|
-
const path = require('path')
|
|
386
|
-
const configPath = path.resolve('.agent/mcp_config.json')
|
|
391
|
+
const fs = require('fs');
|
|
392
|
+
const path = require('path');
|
|
393
|
+
const configPath = path.resolve('.agent/mcp_config.json');
|
|
387
394
|
|
|
388
|
-
|
|
395
|
+
log.info(' Reading config from:', configPath);
|
|
389
396
|
if (!fs.existsSync(configPath)) {
|
|
390
|
-
return { success: false, error: `配置文件不存在: ${configPath}` }
|
|
397
|
+
return { success: false, error: `配置文件不存在: ${configPath}` };
|
|
391
398
|
}
|
|
392
399
|
|
|
393
|
-
const configContent = fs.readFileSync(configPath, 'utf8')
|
|
394
|
-
const config = JSON.parse(configContent)
|
|
395
|
-
|
|
400
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
401
|
+
const config = JSON.parse(configContent);
|
|
402
|
+
log.info(' Config loaded, reloading...');
|
|
396
403
|
|
|
397
|
-
await this.reloadConfig(config)
|
|
398
|
-
|
|
404
|
+
await this.reloadConfig(config);
|
|
405
|
+
log.info(' Reload complete');
|
|
399
406
|
|
|
400
|
-
return {
|
|
407
|
+
return {
|
|
408
|
+
success: true,
|
|
409
|
+
message: 'MCP 配置已重载',
|
|
410
|
+
servers: Object.keys(config.mcpServers || {}),
|
|
411
|
+
};
|
|
401
412
|
} catch (err) {
|
|
402
|
-
|
|
403
|
-
return { success: false, error: `重载失败: ${err.message}` }
|
|
413
|
+
log.error(' Reload error:', err);
|
|
414
|
+
return { success: false, error: `重载失败: ${err.message}` };
|
|
404
415
|
}
|
|
405
|
-
}
|
|
406
|
-
})
|
|
416
|
+
},
|
|
417
|
+
});
|
|
407
418
|
|
|
408
419
|
// 监听 agent 创建事件,附加 MCP 信息到系统提示词
|
|
409
420
|
framework.on('agent:created', (agent) => {
|
|
410
421
|
if (!this._mainAgent) {
|
|
411
|
-
this._mainAgent = agent
|
|
422
|
+
this._mainAgent = agent;
|
|
412
423
|
}
|
|
413
|
-
this._refreshAgentMCPPrompt(agent)
|
|
414
|
-
})
|
|
424
|
+
this._refreshAgentMCPPrompt(agent);
|
|
425
|
+
});
|
|
415
426
|
|
|
416
427
|
// 等待框架就绪后,刷新所有已有 agent 的 MCP 提示词
|
|
417
428
|
if (framework._ready) {
|
|
418
|
-
this._refreshAllAgentsMCPPrompt(framework)
|
|
429
|
+
this._refreshAllAgentsMCPPrompt(framework);
|
|
419
430
|
} else {
|
|
420
431
|
framework.once('framework:ready', () => {
|
|
421
|
-
this._refreshAllAgentsMCPPrompt(framework)
|
|
422
|
-
})
|
|
432
|
+
this._refreshAllAgentsMCPPrompt(framework);
|
|
433
|
+
});
|
|
423
434
|
}
|
|
424
435
|
|
|
425
|
-
return this
|
|
436
|
+
return this;
|
|
426
437
|
}
|
|
427
438
|
|
|
428
439
|
/**
|
|
429
440
|
* 刷新所有 agent 的 MCP 提示词(包括子 agent)
|
|
430
441
|
*/
|
|
431
442
|
_refreshAllAgentsMCPPrompt(framework) {
|
|
432
|
-
const visited = new Set()
|
|
443
|
+
const visited = new Set();
|
|
433
444
|
|
|
434
445
|
const traverse = (agent) => {
|
|
435
|
-
if (!agent || visited.has(agent)) return
|
|
436
|
-
visited.add(agent)
|
|
437
|
-
this._refreshAgentMCPPrompt(agent)
|
|
446
|
+
if (!agent || visited.has(agent)) return;
|
|
447
|
+
visited.add(agent);
|
|
448
|
+
this._refreshAgentMCPPrompt(agent);
|
|
438
449
|
|
|
439
450
|
// 递归处理子 agent
|
|
440
|
-
const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map()
|
|
451
|
+
const subAgents = agent.getSubAgents?.() || agent._subAgents || new Map();
|
|
441
452
|
for (const [name, subAgentInfo] of subAgents) {
|
|
442
|
-
traverse(subAgentInfo.agent)
|
|
453
|
+
traverse(subAgentInfo.agent);
|
|
443
454
|
}
|
|
444
|
-
}
|
|
455
|
+
};
|
|
445
456
|
|
|
446
457
|
for (const agent of framework._agents || []) {
|
|
447
|
-
traverse(agent)
|
|
458
|
+
traverse(agent);
|
|
448
459
|
}
|
|
449
460
|
}
|
|
450
461
|
|
|
@@ -453,27 +464,27 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
453
464
|
*/
|
|
454
465
|
_refreshAgentMCPPrompt(agent) {
|
|
455
466
|
// 检查是否已刷新过(通过检查系统提示词是否已包含 MCP 描述)
|
|
456
|
-
const existingPrompt = agent._originalPrompt || ''
|
|
467
|
+
const existingPrompt = agent._originalPrompt || '';
|
|
457
468
|
if (existingPrompt.includes('[MCP Servers]')) {
|
|
458
|
-
return
|
|
469
|
+
return;
|
|
459
470
|
}
|
|
460
471
|
|
|
461
|
-
const mcpDesc = this._buildMCPServersDescription()
|
|
462
|
-
if (!mcpDesc) return
|
|
472
|
+
const mcpDesc = this._buildMCPServersDescription();
|
|
473
|
+
if (!mcpDesc) return;
|
|
463
474
|
|
|
464
475
|
// 将 MCP 描述追加到系统提示词
|
|
465
|
-
agent.setSystemPrompt(existingPrompt + '\n\n' + mcpDesc)
|
|
476
|
+
agent.setSystemPrompt(existingPrompt + '\n\n' + mcpDesc);
|
|
466
477
|
}
|
|
467
478
|
|
|
468
479
|
/**
|
|
469
480
|
* 从 inputSchema 中提取实际 schema
|
|
470
481
|
*/
|
|
471
482
|
_extractSchema(inputSchema) {
|
|
472
|
-
if (!inputSchema) return {}
|
|
483
|
+
if (!inputSchema) return {};
|
|
473
484
|
// 处理 {jsonSchema: {...}} 格式
|
|
474
|
-
if (inputSchema.jsonSchema) return inputSchema.jsonSchema
|
|
485
|
+
if (inputSchema.jsonSchema) return inputSchema.jsonSchema;
|
|
475
486
|
// 处理直接是 schema 的格式
|
|
476
|
-
return inputSchema
|
|
487
|
+
return inputSchema;
|
|
477
488
|
}
|
|
478
489
|
|
|
479
490
|
/**
|
|
@@ -481,61 +492,62 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
481
492
|
*/
|
|
482
493
|
_buildMCPServersDescription() {
|
|
483
494
|
if (this._clients.size === 0) {
|
|
484
|
-
return ''
|
|
495
|
+
return '';
|
|
485
496
|
}
|
|
486
497
|
|
|
487
|
-
let desc = '【MCP Servers - 可用工具服务】\n'
|
|
488
|
-
desc += '你可以通过 mcp_call 工具调用以下 MCP 服务器的工具。\n\n'
|
|
498
|
+
let desc = '【MCP Servers - 可用工具服务】\n';
|
|
499
|
+
desc += '你可以通过 mcp_call 工具调用以下 MCP 服务器的工具。\n\n';
|
|
489
500
|
|
|
490
501
|
for (const [serverName, info] of this._clients) {
|
|
491
|
-
desc += `【${serverName}】\n
|
|
502
|
+
desc += `【${serverName}】\n`;
|
|
492
503
|
for (const tool of info.tools) {
|
|
493
|
-
const schema = this._extractSchema(tool.inputSchema)
|
|
494
|
-
const props = schema.properties || {}
|
|
495
|
-
const required = schema.required || []
|
|
504
|
+
const schema = this._extractSchema(tool.inputSchema);
|
|
505
|
+
const props = schema.properties || {};
|
|
506
|
+
const required = schema.required || [];
|
|
496
507
|
|
|
497
508
|
// 简化参数列表
|
|
498
|
-
let paramStr = ''
|
|
509
|
+
let paramStr = '';
|
|
499
510
|
if (Object.keys(props).length > 0) {
|
|
500
511
|
const params = Object.entries(props).map(([key, prop]) => {
|
|
501
|
-
const isRequired = required.includes(key)
|
|
502
|
-
return `${key}${isRequired ? '(必填)' : ''}
|
|
503
|
-
})
|
|
504
|
-
paramStr = ` 参数: ${params.join(', ')}
|
|
512
|
+
const isRequired = required.includes(key);
|
|
513
|
+
return `${key}${isRequired ? '(必填)' : ''}`;
|
|
514
|
+
});
|
|
515
|
+
paramStr = ` 参数: ${params.join(', ')}`;
|
|
505
516
|
}
|
|
506
517
|
|
|
507
518
|
// 截取描述的第一句
|
|
508
|
-
const shortDesc = tool.description?.split('.')[0] || '无描述'
|
|
509
|
-
desc += `- ${tool.name}: ${shortDesc}${paramStr}\n
|
|
519
|
+
const shortDesc = tool.description?.split('.')[0] || '无描述';
|
|
520
|
+
desc += `- ${tool.name}: ${shortDesc}${paramStr}\n`;
|
|
510
521
|
}
|
|
511
|
-
desc += '\n'
|
|
522
|
+
desc += '\n';
|
|
512
523
|
}
|
|
513
524
|
|
|
514
|
-
desc +=
|
|
515
|
-
|
|
516
|
-
|
|
525
|
+
desc +=
|
|
526
|
+
'调用格式:mcp_call({ server: "服务器", tool: "工具", args_json: \'{"参数": "值"}\' })\n';
|
|
527
|
+
desc += '复杂参数时先用 mcp_tool_schema 查询完整参数结构\n';
|
|
528
|
+
return desc.trim();
|
|
517
529
|
}
|
|
518
530
|
|
|
519
531
|
_getParentAgent() {
|
|
520
532
|
// 优先返回已缓存的主 agent
|
|
521
533
|
if (this._mainAgent) {
|
|
522
|
-
return this._mainAgent
|
|
534
|
+
return this._mainAgent;
|
|
523
535
|
}
|
|
524
536
|
|
|
525
537
|
// 尝试从 framework 获取
|
|
526
538
|
if (this._framework._mainAgent) {
|
|
527
|
-
this._mainAgent = this._framework._mainAgent
|
|
528
|
-
return this._mainAgent
|
|
539
|
+
this._mainAgent = this._framework._mainAgent;
|
|
540
|
+
return this._mainAgent;
|
|
529
541
|
}
|
|
530
542
|
|
|
531
543
|
// 查找最后一个创建的 agent 作为父 agent
|
|
532
|
-
const agents = this._framework._agents || []
|
|
544
|
+
const agents = this._framework._agents || [];
|
|
533
545
|
if (agents.length > 0) {
|
|
534
|
-
this._mainAgent = agents[agents.length - 1]
|
|
535
|
-
return this._mainAgent
|
|
546
|
+
this._mainAgent = agents[agents.length - 1];
|
|
547
|
+
return this._mainAgent;
|
|
536
548
|
}
|
|
537
549
|
|
|
538
|
-
return null
|
|
550
|
+
return null;
|
|
539
551
|
}
|
|
540
552
|
|
|
541
553
|
/**
|
|
@@ -546,21 +558,21 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
546
558
|
promise,
|
|
547
559
|
new Promise((_, reject) =>
|
|
548
560
|
setTimeout(() => reject(new Error(`${name} timeout (${ms}ms)`)), ms)
|
|
549
|
-
)
|
|
550
|
-
])
|
|
561
|
+
),
|
|
562
|
+
]);
|
|
551
563
|
}
|
|
552
564
|
|
|
553
565
|
/**
|
|
554
566
|
* 添加 MCP 服务器
|
|
555
567
|
*/
|
|
556
568
|
async addServer(config) {
|
|
557
|
-
const { name, command, args = [], env, url, type, headers = {}, authProvider } = config
|
|
569
|
+
const { name, command, args = [], env, url, type, headers = {}, authProvider } = config;
|
|
558
570
|
if (this._clients.has(name)) {
|
|
559
|
-
|
|
560
|
-
return
|
|
571
|
+
log.warn(` Server '${name}' already exists, skipping`);
|
|
572
|
+
return;
|
|
561
573
|
}
|
|
562
|
-
|
|
563
|
-
let client
|
|
574
|
+
log.info(` Connecting to ${name}...`);
|
|
575
|
+
let client;
|
|
564
576
|
try {
|
|
565
577
|
if (['sse', 'http'].includes(type)) {
|
|
566
578
|
client = await this._withTimeout(
|
|
@@ -571,51 +583,51 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
571
583
|
headers: headers,
|
|
572
584
|
authProvider: authProvider,
|
|
573
585
|
redirect: 'error',
|
|
574
|
-
env: env
|
|
586
|
+
env: env,
|
|
575
587
|
},
|
|
576
588
|
}),
|
|
577
589
|
10000,
|
|
578
590
|
`createMCPClient ${name}`
|
|
579
|
-
)
|
|
591
|
+
);
|
|
580
592
|
} else {
|
|
581
593
|
client = await this._withTimeout(
|
|
582
594
|
createMCPClient({
|
|
583
595
|
transport: new StdioClientTransport({
|
|
584
596
|
command: command,
|
|
585
597
|
args: args,
|
|
586
|
-
env: env
|
|
598
|
+
env: env,
|
|
587
599
|
}),
|
|
588
600
|
}),
|
|
589
601
|
10000,
|
|
590
602
|
`createMCPClient ${name}`
|
|
591
|
-
)
|
|
603
|
+
);
|
|
592
604
|
}
|
|
593
605
|
} catch (err) {
|
|
594
|
-
|
|
595
|
-
return
|
|
606
|
+
log.error(` Failed to create client '${name}':`, err.message);
|
|
607
|
+
return;
|
|
596
608
|
}
|
|
597
609
|
|
|
598
610
|
// 获取 MCP 服务器的工具列表
|
|
599
|
-
let mcpTools
|
|
611
|
+
let mcpTools;
|
|
600
612
|
try {
|
|
601
|
-
mcpTools = await this._withTimeout(client.tools(), 10000, `client.tools ${name}`)
|
|
613
|
+
mcpTools = await this._withTimeout(client.tools(), 10000, `client.tools ${name}`);
|
|
602
614
|
} catch (err) {
|
|
603
|
-
|
|
604
|
-
return
|
|
615
|
+
log.error(` Failed to get tools from '${name}':`, err.message);
|
|
616
|
+
return;
|
|
605
617
|
}
|
|
606
618
|
|
|
607
619
|
// 提取工具信息和实际工具对象
|
|
608
|
-
const toolsInfo = []
|
|
609
|
-
const toolObjects = {} // 缓存实际工具对象
|
|
620
|
+
const toolsInfo = [];
|
|
621
|
+
const toolObjects = {}; // 缓存实际工具对象
|
|
610
622
|
if (mcpTools && typeof mcpTools === 'object') {
|
|
611
623
|
for (const [toolName, tool] of Object.entries(mcpTools)) {
|
|
612
|
-
if (!tool) continue
|
|
613
|
-
toolObjects[toolName] = tool // 保存实际工具对象
|
|
624
|
+
if (!tool) continue;
|
|
625
|
+
toolObjects[toolName] = tool; // 保存实际工具对象
|
|
614
626
|
toolsInfo.push({
|
|
615
627
|
name: toolName,
|
|
616
628
|
description: tool.description || '',
|
|
617
|
-
inputSchema: tool.inputSchema || {}
|
|
618
|
-
})
|
|
629
|
+
inputSchema: tool.inputSchema || {},
|
|
630
|
+
});
|
|
619
631
|
}
|
|
620
632
|
}
|
|
621
633
|
|
|
@@ -623,31 +635,31 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
623
635
|
this._clients.set(name, {
|
|
624
636
|
client,
|
|
625
637
|
tools: toolsInfo,
|
|
626
|
-
toolObjects // 实际工具对象,供 mcp_call 直接调用
|
|
627
|
-
})
|
|
638
|
+
toolObjects, // 实际工具对象,供 mcp_call 直接调用
|
|
639
|
+
});
|
|
628
640
|
|
|
629
|
-
|
|
641
|
+
log.info(` Added server '${name}' with ${toolsInfo.length} tools`);
|
|
630
642
|
}
|
|
631
643
|
|
|
632
644
|
/**
|
|
633
645
|
* 移除 MCP 服务器
|
|
634
646
|
*/
|
|
635
647
|
async removeServer(name) {
|
|
636
|
-
const clientInfo = this._clients.get(name)
|
|
648
|
+
const clientInfo = this._clients.get(name);
|
|
637
649
|
if (clientInfo) {
|
|
638
650
|
try {
|
|
639
651
|
// 添加超时保护,避免 close/destroy 阻塞
|
|
640
|
-
const closePromise = clientInfo.client.close?.() || clientInfo.client.destroy?.()
|
|
652
|
+
const closePromise = clientInfo.client.close?.() || clientInfo.client.destroy?.();
|
|
641
653
|
if (closePromise && typeof closePromise.then === 'function') {
|
|
642
654
|
await Promise.race([
|
|
643
655
|
closePromise,
|
|
644
|
-
new Promise(resolve => setTimeout(() => resolve(), 3000))
|
|
645
|
-
])
|
|
656
|
+
new Promise((resolve) => setTimeout(() => resolve(), 3000)),
|
|
657
|
+
]);
|
|
646
658
|
}
|
|
647
659
|
} catch (e) {
|
|
648
660
|
// ignore
|
|
649
661
|
}
|
|
650
|
-
this._clients.delete(name)
|
|
662
|
+
this._clients.delete(name);
|
|
651
663
|
}
|
|
652
664
|
}
|
|
653
665
|
|
|
@@ -657,13 +669,13 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
657
669
|
getServers() {
|
|
658
670
|
return Array.from(this._clients.entries()).map(([name, info]) => ({
|
|
659
671
|
name,
|
|
660
|
-
tools: info.tools.map(t => t.name)
|
|
661
|
-
}))
|
|
672
|
+
tools: info.tools.map((t) => t.name),
|
|
673
|
+
}));
|
|
662
674
|
}
|
|
663
675
|
|
|
664
676
|
reload(framework) {
|
|
665
|
-
|
|
666
|
-
this._framework = framework
|
|
677
|
+
log.info(' Reloading...');
|
|
678
|
+
this._framework = framework;
|
|
667
679
|
}
|
|
668
680
|
|
|
669
681
|
/**
|
|
@@ -671,21 +683,21 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
671
683
|
* @param {Object} config - 新的配置对象,包含 mcpServers
|
|
672
684
|
*/
|
|
673
685
|
async reloadConfig(config) {
|
|
674
|
-
|
|
675
|
-
const newServers = config?.mcpServers || {}
|
|
686
|
+
log.info(' reloadConfig start');
|
|
687
|
+
const newServers = config?.mcpServers || {};
|
|
676
688
|
|
|
677
689
|
// 1. 先快速清理所有旧服务器(不等待 close 完成)
|
|
678
|
-
|
|
690
|
+
log.info(' Clearing old servers...');
|
|
679
691
|
for (const [name, clientInfo] of this._clients) {
|
|
680
692
|
try {
|
|
681
693
|
// 不等待 close,直接删除
|
|
682
|
-
clientInfo.client.close?.()?.catch?.(() => {})
|
|
694
|
+
clientInfo.client.close?.()?.catch?.(() => {});
|
|
683
695
|
} catch (e) {}
|
|
684
696
|
}
|
|
685
|
-
this._clients.clear()
|
|
697
|
+
this._clients.clear();
|
|
686
698
|
|
|
687
699
|
// 2. 并行添加新服务器
|
|
688
|
-
|
|
700
|
+
log.info(' Adding new servers...');
|
|
689
701
|
const addPromises = Object.entries(newServers).map(async ([name, serverConfig]) => {
|
|
690
702
|
const normalizedConfig = {
|
|
691
703
|
name,
|
|
@@ -695,17 +707,17 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
695
707
|
authProvider: serverConfig.authProvider,
|
|
696
708
|
command: serverConfig.command,
|
|
697
709
|
args: serverConfig.args,
|
|
698
|
-
env: serverConfig.env
|
|
699
|
-
}
|
|
700
|
-
await this.addServer(normalizedConfig)
|
|
701
|
-
})
|
|
710
|
+
env: serverConfig.env,
|
|
711
|
+
};
|
|
712
|
+
await this.addServer(normalizedConfig);
|
|
713
|
+
});
|
|
702
714
|
|
|
703
|
-
await Promise.all(addPromises)
|
|
704
|
-
|
|
715
|
+
await Promise.all(addPromises);
|
|
716
|
+
log.info(' reloadConfig complete');
|
|
705
717
|
|
|
706
718
|
// 3. 刷新所有 agent 的 MCP 提示词
|
|
707
719
|
if (this._framework) {
|
|
708
|
-
this._refreshAllAgentsMCPPrompt(this._framework)
|
|
720
|
+
this._refreshAllAgentsMCPPrompt(this._framework);
|
|
709
721
|
}
|
|
710
722
|
}
|
|
711
723
|
|
|
@@ -713,17 +725,17 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
713
725
|
// 断开所有 MCP 连接
|
|
714
726
|
for (const [name, clientInfo] of this._clients) {
|
|
715
727
|
try {
|
|
716
|
-
await clientInfo.client.close?.() || clientInfo.client.destroy?.()
|
|
728
|
+
(await clientInfo.client.close?.()) || clientInfo.client.destroy?.();
|
|
717
729
|
} catch (e) {
|
|
718
730
|
// ignore
|
|
719
731
|
}
|
|
720
732
|
}
|
|
721
|
-
this._clients.clear()
|
|
722
|
-
this._framework = null
|
|
733
|
+
this._clients.clear();
|
|
734
|
+
this._framework = null;
|
|
723
735
|
}
|
|
724
736
|
}
|
|
725
737
|
|
|
726
738
|
module.exports = {
|
|
727
739
|
MCPExecutorPlugin,
|
|
728
|
-
MCPClientWrapper
|
|
729
|
-
}
|
|
740
|
+
MCPClientWrapper,
|
|
741
|
+
};
|