foliko 1.0.74 → 1.0.76

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