aegiscode 3.1.8 → 3.1.10

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 (206) hide show
  1. package/README.md +23 -15
  2. package/dist/main.js +549 -552
  3. package/package.json +4 -2
  4. package/src/agent/Agent.ts +903 -0
  5. package/src/agent/SimpleAgent.ts +48 -0
  6. package/src/agent/index.ts +54 -0
  7. package/src/agent/orchestrator/AppBuilder.ts +443 -0
  8. package/src/agent/orchestrator/CouncilAgent.ts +310 -0
  9. package/src/agent/orchestrator/DiscussionRoom.ts +462 -0
  10. package/src/agent/orchestrator/OrchestratorAgent.ts +637 -0
  11. package/src/agent/orchestrator/index.ts +38 -0
  12. package/src/agent/orchestrator/utils.ts +397 -0
  13. package/src/agent/pricing.ts +115 -0
  14. package/src/agent/router.ts +74 -0
  15. package/src/agent/routerStats.ts +121 -0
  16. package/src/agent/types.ts +318 -0
  17. package/src/auth/login.ts +383 -0
  18. package/src/cli/config.ts +189 -0
  19. package/src/cli/index.ts +17 -0
  20. package/src/cli/middleware.ts +119 -0
  21. package/src/cli/types.ts +75 -0
  22. package/src/config/ConfigManager.ts +587 -0
  23. package/src/config/index.ts +7 -0
  24. package/src/config/types.ts +584 -0
  25. package/src/context/CompactionService.ts +300 -0
  26. package/src/context/ContextManager.ts +450 -0
  27. package/src/context/FileAnalyzer.ts +267 -0
  28. package/src/context/TokenCounter.ts +265 -0
  29. package/src/context/index.ts +27 -0
  30. package/src/context/storage/CacheStore.ts +176 -0
  31. package/src/context/storage/JSONLStore.ts +201 -0
  32. package/src/context/storage/MemoryStore.ts +205 -0
  33. package/src/context/storage/PersistentStore.ts +327 -0
  34. package/src/context/storage/index.ts +9 -0
  35. package/src/context/storage/pathUtils.ts +114 -0
  36. package/src/context/test.ts +309 -0
  37. package/src/context/types.ts +268 -0
  38. package/src/hooks/HookExecutor.ts +434 -0
  39. package/src/hooks/HookManager.ts +596 -0
  40. package/src/hooks/HookService.ts +269 -0
  41. package/src/hooks/Matcher.ts +157 -0
  42. package/src/hooks/index.ts +63 -0
  43. package/src/hooks/types.ts +424 -0
  44. package/src/main.tsx +596 -0
  45. package/src/mcp/HealthMonitor.ts +150 -0
  46. package/src/mcp/McpClient.ts +491 -0
  47. package/src/mcp/McpRegistry.ts +321 -0
  48. package/src/mcp/createMcpTool.ts +251 -0
  49. package/src/mcp/index.ts +15 -0
  50. package/src/mcp/server.ts +334 -0
  51. package/src/mcp/test-server.ts +88 -0
  52. package/src/mcp/test.ts +372 -0
  53. package/src/mcp/types.ts +247 -0
  54. package/src/memory/AgentMemoryBus.ts +432 -0
  55. package/src/memory/CloudSync.ts +99 -0
  56. package/src/memory/DriveSync.ts +106 -0
  57. package/src/memory/SharedMemory.ts +951 -0
  58. package/src/memory/index.ts +14 -0
  59. package/src/memory/machineFingerprint.ts +40 -0
  60. package/src/orchestrator/SubAgentMetadata.ts +136 -0
  61. package/src/prompts/builder.ts +213 -0
  62. package/src/prompts/default.ts +144 -0
  63. package/src/prompts/index.ts +16 -0
  64. package/src/prompts/plan.ts +64 -0
  65. package/src/prompts/test.ts +78 -0
  66. package/src/services/AnthropicChatService.ts +341 -0
  67. package/src/services/ChatService.ts +347 -0
  68. package/src/services/ClaudeCliChatService.ts +256 -0
  69. package/src/services/CloudSync.ts +168 -0
  70. package/src/services/CostLedger.ts +211 -0
  71. package/src/services/Heartbeat.ts +135 -0
  72. package/src/services/LearningCollector.ts +291 -0
  73. package/src/services/OllamaInstaller.ts +342 -0
  74. package/src/services/VersionChecker.ts +445 -0
  75. package/src/services/index.ts +57 -0
  76. package/src/services/streaming/OpenAIEventAdapter.ts +126 -0
  77. package/src/services/streaming/RenderingProfile.ts +90 -0
  78. package/src/services/streaming/StreamEventParser.ts +181 -0
  79. package/src/services/streaming/ThrottledRenderer.ts +139 -0
  80. package/src/services/streaming/TranscriptBuffer.ts +574 -0
  81. package/src/services/streaming/eventStatusMap.ts +52 -0
  82. package/src/services/streaming/index.ts +46 -0
  83. package/src/services/streaming/renderFormatting.ts +79 -0
  84. package/src/services/streaming/types.ts +234 -0
  85. package/src/skills/SkillLoader.ts +126 -0
  86. package/src/skills/SkillRegistry.ts +366 -0
  87. package/src/skills/index.ts +48 -0
  88. package/src/skills/types.ts +146 -0
  89. package/src/slash-commands/billing.ts +70 -0
  90. package/src/slash-commands/build.ts +413 -0
  91. package/src/slash-commands/builtinCommands.ts +2733 -0
  92. package/src/slash-commands/clone.ts +242 -0
  93. package/src/slash-commands/council.ts +125 -0
  94. package/src/slash-commands/custom/CustomCommandExecutor.ts +143 -0
  95. package/src/slash-commands/custom/CustomCommandLoader.ts +251 -0
  96. package/src/slash-commands/custom/CustomCommandRegistry.ts +144 -0
  97. package/src/slash-commands/custom/index.ts +7 -0
  98. package/src/slash-commands/debate.ts +254 -0
  99. package/src/slash-commands/gmail.ts +105 -0
  100. package/src/slash-commands/index.ts +388 -0
  101. package/src/slash-commands/mcpCommand.ts +205 -0
  102. package/src/slash-commands/types.ts +201 -0
  103. package/src/store/index.ts +76 -0
  104. package/src/store/selectors.ts +246 -0
  105. package/src/store/slices/appSlice.ts +205 -0
  106. package/src/store/slices/commandSlice.ts +115 -0
  107. package/src/store/slices/configSlice.ts +46 -0
  108. package/src/store/slices/focusSlice.ts +64 -0
  109. package/src/store/slices/index.ts +9 -0
  110. package/src/store/slices/sessionSlice.ts +424 -0
  111. package/src/store/streaming-buffer.ts +425 -0
  112. package/src/store/test.ts +296 -0
  113. package/src/store/types.ts +274 -0
  114. package/src/store/vanilla.ts +186 -0
  115. package/src/tools/builtin/bash.ts +236 -0
  116. package/src/tools/builtin/council.ts +105 -0
  117. package/src/tools/builtin/edit.ts +213 -0
  118. package/src/tools/builtin/glob.ts +136 -0
  119. package/src/tools/builtin/grep.ts +263 -0
  120. package/src/tools/builtin/index.ts +61 -0
  121. package/src/tools/builtin/memory.ts +66 -0
  122. package/src/tools/builtin/read.ts +168 -0
  123. package/src/tools/builtin/skill.ts +97 -0
  124. package/src/tools/builtin/snapshot.ts +40 -0
  125. package/src/tools/builtin/task.ts +106 -0
  126. package/src/tools/builtin/write.ts +134 -0
  127. package/src/tools/createTool.ts +221 -0
  128. package/src/tools/execution/ExecutionPipeline.ts +263 -0
  129. package/src/tools/execution/index.ts +40 -0
  130. package/src/tools/execution/stages/CacheStage.ts +131 -0
  131. package/src/tools/execution/stages/ConfirmationStage.ts +203 -0
  132. package/src/tools/execution/stages/DiscoveryStage.ts +46 -0
  133. package/src/tools/execution/stages/ExecutionStage.ts +48 -0
  134. package/src/tools/execution/stages/FormattingStage.ts +44 -0
  135. package/src/tools/execution/stages/HookStage.ts +72 -0
  136. package/src/tools/execution/stages/PermissionStage.ts +287 -0
  137. package/src/tools/execution/stages/PostHookStage.ts +71 -0
  138. package/src/tools/execution/stages/index.ts +12 -0
  139. package/src/tools/execution/test.ts +266 -0
  140. package/src/tools/execution/types.ts +273 -0
  141. package/src/tools/index.ts +81 -0
  142. package/src/tools/registry.ts +304 -0
  143. package/src/tools/schemas.ts +109 -0
  144. package/src/tools/test.ts +220 -0
  145. package/src/tools/types.ts +175 -0
  146. package/src/tools/validation/PermissionChecker.ts +242 -0
  147. package/src/tools/validation/SensitiveFileDetector.ts +210 -0
  148. package/src/tools/validation/index.ts +11 -0
  149. package/src/ui/App.tsx +166 -0
  150. package/src/ui/components/AegisInterface.tsx +484 -0
  151. package/src/ui/components/common/ChatSearch.tsx +150 -0
  152. package/src/ui/components/common/ErrorBoundary.tsx +82 -0
  153. package/src/ui/components/common/ExitMessage.tsx +120 -0
  154. package/src/ui/components/common/LoadingIndicator.tsx +49 -0
  155. package/src/ui/components/common/index.ts +6 -0
  156. package/src/ui/components/dialog/ConfirmationPrompt.tsx +208 -0
  157. package/src/ui/components/dialog/InteractiveSelector.tsx +149 -0
  158. package/src/ui/components/dialog/SetupWizard.tsx +297 -0
  159. package/src/ui/components/dialog/UpdatePrompt.tsx +155 -0
  160. package/src/ui/components/dialog/index.ts +8 -0
  161. package/src/ui/components/index.ts +28 -0
  162. package/src/ui/components/input/CommandSuggestions.tsx +139 -0
  163. package/src/ui/components/input/CustomTextInput.tsx +220 -0
  164. package/src/ui/components/input/InputArea.tsx +361 -0
  165. package/src/ui/components/input/PromptSuggestions.tsx +66 -0
  166. package/src/ui/components/input/index.ts +6 -0
  167. package/src/ui/components/layout/ChatStatusBar.tsx +90 -0
  168. package/src/ui/components/layout/ContextBar.tsx +79 -0
  169. package/src/ui/components/layout/MessageArea.tsx +96 -0
  170. package/src/ui/components/layout/MessageList.tsx +647 -0
  171. package/src/ui/components/layout/MessageSeparator.tsx +26 -0
  172. package/src/ui/components/layout/WelcomeMessage.tsx +93 -0
  173. package/src/ui/components/layout/index.ts +7 -0
  174. package/src/ui/components/markdown/CodeHighlighter.tsx +292 -0
  175. package/src/ui/components/markdown/MessageRenderer.tsx +1211 -0
  176. package/src/ui/components/markdown/index.ts +8 -0
  177. package/src/ui/components/markdown/parser.ts +336 -0
  178. package/src/ui/components/markdown/types.ts +66 -0
  179. package/src/ui/focus/FocusManager.ts +137 -0
  180. package/src/ui/focus/index.ts +13 -0
  181. package/src/ui/focus/types.ts +54 -0
  182. package/src/ui/focus/useFocus.ts +75 -0
  183. package/src/ui/hooks/index.ts +11 -0
  184. package/src/ui/hooks/useAgent.ts +284 -0
  185. package/src/ui/hooks/useCommandHistory.ts +87 -0
  186. package/src/ui/hooks/useCommandProcessor.ts +443 -0
  187. package/src/ui/hooks/useConfirmation.ts +99 -0
  188. package/src/ui/hooks/useCtrlCHandler.ts +100 -0
  189. package/src/ui/hooks/useInputBuffer.ts +122 -0
  190. package/src/ui/hooks/useTerminalSize.ts +68 -0
  191. package/src/ui/hooks/useTerminalWidth.ts +5 -0
  192. package/src/ui/hooks/useWindowedList.ts +118 -0
  193. package/src/ui/render-debugger.ts +621 -0
  194. package/src/ui/test.ts +189 -0
  195. package/src/ui/themes/ThemeManager.ts +332 -0
  196. package/src/ui/themes/aegisTheme.ts +87 -0
  197. package/src/ui/themes/darkTheme.ts +87 -0
  198. package/src/ui/themes/defaultTheme.ts +85 -0
  199. package/src/ui/themes/index.ts +10 -0
  200. package/src/ui/themes/lightTheme.ts +89 -0
  201. package/src/ui/themes/popularThemes.ts +187 -0
  202. package/src/ui/themes/types.ts +130 -0
  203. package/src/utils/clipboard.ts +48 -0
  204. package/src/utils/debug.ts +43 -0
  205. package/src/utils/environment.ts +68 -0
  206. package/src/utils/index.ts +10 -0
@@ -0,0 +1,321 @@
1
+ /**
2
+ * MCP 服务器注册中心
3
+ *
4
+ */
5
+
6
+ import { EventEmitter } from 'events';
7
+ import {
8
+ McpConnectionStatus,
9
+ McpServerConfig,
10
+ McpServerInfo,
11
+ McpToolDefinition,
12
+ McpRegistryStatistics,
13
+ } from './types.js';
14
+ import { McpClient } from './McpClient.js';
15
+ import { createMcpTool } from './createMcpTool.js';
16
+ import type { Tool } from '../tools/types.js';
17
+ import { mcpRegistryDebug } from '../utils/debug.js';
18
+
19
+ /**
20
+ * MCP 注册中心(单例)
21
+ */
22
+ export class McpRegistry extends EventEmitter {
23
+ private static instance: McpRegistry | null = null;
24
+ private servers: Map<string, McpServerInfo> = new Map();
25
+
26
+ private constructor() {
27
+ super();
28
+ // 增加监听器上限,因为可能有多个 Agent 实例监听同一事
29
+ this.setMaxListeners(20);
30
+ }
31
+
32
+ /**
33
+ *
34
+ */
35
+ static getInstance(): McpRegistry {
36
+ if (!McpRegistry.instance) {
37
+ McpRegistry.instance = new McpRegistry();
38
+ }
39
+ return McpRegistry.instance;
40
+ }
41
+
42
+ /**
43
+ *
44
+ */
45
+ static resetInstance(): void {
46
+ if (McpRegistry.instance) {
47
+ McpRegistry.instance.disconnectAll().catch(() => {});
48
+ McpRegistry.instance = null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ *
54
+ */
55
+ async registerServer(name: string, config: McpServerConfig): Promise<void> {
56
+ // 检查是否已禁
57
+ if (config.enabled === false) {
58
+ mcpRegistryDebug.log(`服务器 "${name}" 已禁用,跳过注册`);
59
+ return;
60
+ }
61
+
62
+ if (this.servers.has(name)) {
63
+ throw new Error(`MCP服务器 "${name}" 已经注册`);
64
+ }
65
+
66
+ const client = new McpClient(config, name, config.healthCheck);
67
+ const serverInfo: McpServerInfo = {
68
+ config,
69
+ client,
70
+ status: McpConnectionStatus.DISCONNECTED,
71
+ tools: [],
72
+ };
73
+
74
+ // 设置事件处理
75
+ this.setupClientEventHandlers(client, serverInfo, name);
76
+
77
+ this.servers.set(name, serverInfo);
78
+ this.emit('serverRegistered', name, serverInfo);
79
+
80
+ mcpRegistryDebug.log(`已注册服务器: ${name} (${config.type})`);
81
+
82
+ // 尝试连
83
+ try {
84
+ await this.connectServer(name);
85
+ } catch (error) {
86
+ mcpRegistryDebug.warn(`服务器 "${name}" 连接失败:`, (error as Error).message);
87
+ }
88
+ }
89
+
90
+ /**
91
+ *
92
+ */
93
+ async registerServers(servers: Record<string, McpServerConfig>): Promise<void> {
94
+ const promises = Object.entries(servers).map(([name, config]) =>
95
+ this.registerServer(name, config).catch(error => {
96
+ mcpRegistryDebug.warn(`注册服务器 "${name}" 失败:`, (error as Error).message);
97
+ return error;
98
+ })
99
+ );
100
+
101
+ await Promise.allSettled(promises);
102
+ }
103
+
104
+ /**
105
+ *
106
+ */
107
+ async connectServer(name: string): Promise<void> {
108
+ const serverInfo = this.servers.get(name);
109
+ if (!serverInfo) {
110
+ throw new Error(`服务器 "${name}" 未注册`);
111
+ }
112
+
113
+ if (serverInfo.status === McpConnectionStatus.CONNECTED) {
114
+ mcpRegistryDebug.log(`服务器 "${name}" 已连接`);
115
+ return;
116
+ }
117
+
118
+ await serverInfo.client.connectWithRetry();
119
+ }
120
+
121
+ /**
122
+ *
123
+ */
124
+ async disconnectServer(name: string): Promise<void> {
125
+ const serverInfo = this.servers.get(name);
126
+ if (!serverInfo) {
127
+ throw new Error(`服务器 "${name}" 未注册`);
128
+ }
129
+
130
+ await serverInfo.client.disconnect();
131
+ }
132
+
133
+ /**
134
+ *
135
+ */
136
+ async disconnectAll(): Promise<void> {
137
+ const promises = Array.from(this.servers.keys()).map(name =>
138
+ this.disconnectServer(name).catch(() => {})
139
+ );
140
+ await Promise.allSettled(promises);
141
+ }
142
+
143
+ /**
144
+ *
145
+ */
146
+ async removeServer(name: string): Promise<void> {
147
+ const serverInfo = this.servers.get(name);
148
+ if (!serverInfo) {
149
+ return;
150
+ }
151
+
152
+ await serverInfo.client.disconnect().catch(() => {});
153
+ this.servers.delete(name);
154
+
155
+ mcpRegistryDebug.log(`已移除服务器: ${name}`);
156
+ }
157
+
158
+ /**
159
+ *
160
+ */
161
+ private setupClientEventHandlers(
162
+ client: McpClient,
163
+ serverInfo: McpServerInfo,
164
+ name: string
165
+ ): void {
166
+ client.on('connected', (server: { name: string; version: string }) => {
167
+ serverInfo.status = McpConnectionStatus.CONNECTED;
168
+ serverInfo.connectedAt = new Date();
169
+ serverInfo.serverName = server.name;
170
+ serverInfo.serverVersion = server.version;
171
+ serverInfo.tools = client.availableTools;
172
+ serverInfo.lastError = undefined;
173
+ this.emit('serverConnected', name, server);
174
+ });
175
+
176
+ client.on('disconnected', () => {
177
+ serverInfo.status = McpConnectionStatus.DISCONNECTED;
178
+ serverInfo.connectedAt = undefined;
179
+ serverInfo.tools = [];
180
+ this.emit('serverDisconnected', name);
181
+ });
182
+
183
+ client.on('error', (error: Error) => {
184
+ serverInfo.status = McpConnectionStatus.ERROR;
185
+ serverInfo.lastError = error;
186
+ this.emit('serverError', name, error);
187
+ });
188
+
189
+ client.on('toolsUpdated', (tools: McpToolDefinition[]) => {
190
+ const oldCount = serverInfo.tools.length;
191
+ serverInfo.tools = tools;
192
+ this.emit('toolsUpdated', name, tools, oldCount);
193
+ });
194
+
195
+ client.on('reconnecting', (attempt: number) => {
196
+ serverInfo.status = McpConnectionStatus.CONNECTING;
197
+ mcpRegistryDebug.log(`服务器 "${name}" 正在重连 (第 ${attempt} 次)`);
198
+ });
199
+
200
+ client.on('reconnected', () => {
201
+ mcpRegistryDebug.log(`服务器 "${name}" 重连成功`);
202
+ });
203
+
204
+ client.on('reconnectFailed', () => {
205
+ serverInfo.status = McpConnectionStatus.ERROR;
206
+ mcpRegistryDebug.error(`服务器 "${name}" 重连失败`);
207
+ });
208
+ }
209
+
210
+ /**
211
+ *
212
+ *
213
+ *
214
+ * - 无冲突: toolName
215
+ * - 有冲突: serverName__toolName
216
+ */
217
+ async getAvailableTools(): Promise<Tool[]> {
218
+ const tools: Tool[] = [];
219
+ const nameConflicts = new Map<string, number>();
220
+
221
+ // 第一遍:检测冲
222
+ for (const [, serverInfo] of this.servers) {
223
+ if (serverInfo.status === McpConnectionStatus.CONNECTED) {
224
+ for (const mcpTool of serverInfo.tools) {
225
+ const count = nameConflicts.get(mcpTool.name) || 0;
226
+ nameConflicts.set(mcpTool.name, count + 1);
227
+ }
228
+ }
229
+ }
230
+
231
+ // 第二遍:创建工具(冲突时添加前
232
+ for (const [serverName, serverInfo] of this.servers) {
233
+ if (serverInfo.status === McpConnectionStatus.CONNECTED) {
234
+ for (const mcpTool of serverInfo.tools) {
235
+ const hasConflict = (nameConflicts.get(mcpTool.name) || 0) > 1;
236
+ const toolName = hasConflict
237
+ ? `${serverName}__${mcpTool.name}` // 冲突
238
+ : mcpTool.name; // 无冲
239
+
240
+ try {
241
+ const tool = createMcpTool(
242
+ serverInfo.client,
243
+ serverName,
244
+ mcpTool,
245
+ toolName
246
+ );
247
+ tools.push(tool);
248
+ } catch (error) {
249
+ mcpRegistryDebug.warn(
250
+ `创建工具 "${mcpTool.name}" 失败:`,
251
+ (error as Error).message
252
+ );
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ return tools;
259
+ }
260
+
261
+ /**
262
+ *
263
+ */
264
+ getServer(name: string): McpServerInfo | undefined {
265
+ return this.servers.get(name);
266
+ }
267
+
268
+ /**
269
+ *
270
+ */
271
+ getAllServers(): Map<string, McpServerInfo> {
272
+ return new Map(this.servers);
273
+ }
274
+
275
+ /**
276
+ *
277
+ */
278
+ getStatistics(): McpRegistryStatistics {
279
+ let connectedServers = 0;
280
+ let disconnectedServers = 0;
281
+ let errorServers = 0;
282
+ let totalTools = 0;
283
+
284
+ for (const serverInfo of this.servers.values()) {
285
+ switch (serverInfo.status) {
286
+ case McpConnectionStatus.CONNECTED:
287
+ connectedServers++;
288
+ totalTools += serverInfo.tools.length;
289
+ break;
290
+ case McpConnectionStatus.DISCONNECTED:
291
+ disconnectedServers++;
292
+ break;
293
+ case McpConnectionStatus.ERROR:
294
+ errorServers++;
295
+ break;
296
+ }
297
+ }
298
+
299
+ return {
300
+ totalServers: this.servers.size,
301
+ connectedServers,
302
+ disconnectedServers,
303
+ errorServers,
304
+ totalTools,
305
+ };
306
+ }
307
+
308
+ /**
309
+ *
310
+ */
311
+ hasServer(name: string): boolean {
312
+ return this.servers.has(name);
313
+ }
314
+
315
+ /**
316
+ *
317
+ */
318
+ getServerStatus(name: string): McpConnectionStatus | undefined {
319
+ return this.servers.get(name)?.status;
320
+ }
321
+ }
@@ -0,0 +1,251 @@
1
+ /**
2
+ * MCP Tool 转换器
3
+ *
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import type {
8
+ McpToolDefinition,
9
+ McpClientInterface,
10
+ JSONSchemaProperty,
11
+ } from './types.js';
12
+ import { createTool } from '../tools/createTool.js';
13
+ import { ToolKind, ToolErrorType } from '../tools/types.js';
14
+ import { mcpDebug } from '../utils/debug.js';
15
+
16
+ /**
17
+ *
18
+ */
19
+ function convertJsonSchemaToZod(jsonSchema: JSONSchemaProperty): z.ZodSchema {
20
+ // null
21
+ if (!jsonSchema || typeof jsonSchema !== 'object') {
22
+ return z.any();
23
+ }
24
+
25
+ const type = Array.isArray(jsonSchema.type) ? jsonSchema.type[0] : jsonSchema.type;
26
+
27
+ // object 类
28
+ if (type === 'object' || jsonSchema.properties) {
29
+ const shape: Record<string, z.ZodSchema> = {};
30
+ const required = jsonSchema.required || [];
31
+
32
+ if (jsonSchema.properties) {
33
+ for (const [key, value] of Object.entries(jsonSchema.properties)) {
34
+ if (typeof value === 'object' && value !== null) {
35
+ let fieldSchema = convertJsonSchemaToZod(value);
36
+
37
+ // 非必填字段标记为可
38
+ if (!required.includes(key)) {
39
+ fieldSchema = fieldSchema.optional();
40
+ }
41
+
42
+ shape[key] = fieldSchema;
43
+ }
44
+ }
45
+ }
46
+
47
+ return z.object(shape);
48
+ }
49
+
50
+ // array 类
51
+ if (type === 'array') {
52
+ if (jsonSchema.items && typeof jsonSchema.items === 'object') {
53
+ return z.array(convertJsonSchemaToZod(jsonSchema.items));
54
+ }
55
+ return z.array(z.any());
56
+ }
57
+
58
+ // string 类
59
+ if (type === 'string') {
60
+ // 枚
61
+ if (jsonSchema.enum && jsonSchema.enum.length > 0) {
62
+ return z.enum(jsonSchema.enum as [string, ...string[]]);
63
+ }
64
+
65
+ let schema = z.string();
66
+
67
+ // 长度限
68
+ if (jsonSchema.minLength !== undefined) {
69
+ schema = schema.min(jsonSchema.minLength);
70
+ }
71
+ if (jsonSchema.maxLength !== undefined) {
72
+ schema = schema.max(jsonSchema.maxLength);
73
+ }
74
+
75
+ // 正则模
76
+ if (jsonSchema.pattern) {
77
+ try {
78
+ schema = schema.regex(new RegExp(jsonSchema.pattern));
79
+ } catch {
80
+ // 忽略无效的正
81
+ }
82
+ }
83
+
84
+ return schema;
85
+ }
86
+
87
+ // number / integer 类型
88
+ if (type === 'number' || type === 'integer') {
89
+ let schema = z.number();
90
+
91
+ if (jsonSchema.minimum !== undefined) {
92
+ schema = schema.min(jsonSchema.minimum);
93
+ }
94
+ if (jsonSchema.maximum !== undefined) {
95
+ schema = schema.max(jsonSchema.maximum);
96
+ }
97
+
98
+ return schema;
99
+ }
100
+
101
+ // boolean 类
102
+ if (type === 'boolean') {
103
+ return z.boolean();
104
+ }
105
+
106
+ // oneOf / anyOf
107
+ if (jsonSchema.oneOf && jsonSchema.oneOf.length >= 2) {
108
+ const schemas = jsonSchema.oneOf
109
+ .filter((s): s is JSONSchemaProperty => typeof s === 'object' && s !== null)
110
+ .map(s => convertJsonSchemaToZod(s));
111
+
112
+ if (schemas.length >= 2) {
113
+ return z.union(schemas as [z.ZodSchema, z.ZodSchema, ...z.ZodSchema[]]);
114
+ }
115
+ }
116
+
117
+ if (jsonSchema.anyOf && jsonSchema.anyOf.length >= 2) {
118
+ const schemas = jsonSchema.anyOf
119
+ .filter((s): s is JSONSchemaProperty => typeof s === 'object' && s !== null)
120
+ .map(s => convertJsonSchemaToZod(s));
121
+
122
+ if (schemas.length >= 2) {
123
+ return z.union(schemas as [z.ZodSchema, z.ZodSchema, ...z.ZodSchema[]]);
124
+ }
125
+ }
126
+
127
+ // 默
128
+ return z.any();
129
+ }
130
+
131
+ /**
132
+ *
133
+ */
134
+ export function createMcpTool(
135
+ mcpClient: McpClientInterface,
136
+ serverName: string,
137
+ toolDef: McpToolDefinition,
138
+ customName?: string
139
+ ) {
140
+ // 1. JSON Schema → Zod Schema
141
+ let zodSchema: z.ZodSchema;
142
+ try {
143
+ zodSchema = convertJsonSchemaToZod(toolDef.inputSchema);
144
+ } catch (error) {
145
+ mcpDebug.warn(
146
+ `Schema 转换失败,使用降级 schema: ${toolDef.name}`,
147
+ (error as Error).message
148
+ );
149
+ zodSchema = z.any(); // 降级方
150
+ }
151
+
152
+ // 2. 决定工具名
153
+ const toolName = customName || toolDef.name;
154
+
155
+ // 3. 创
156
+ return createTool({
157
+ name: toolName,
158
+ displayName: `${serverName}: ${toolDef.name}`,
159
+ kind: ToolKind.Execute, // MCP 工具视为 Execute 类型(需要确
160
+ schema: zodSchema,
161
+ description: {
162
+ short: toolDef.description || `MCP Tool: ${toolDef.name}`,
163
+ long: [
164
+ `MCP 工具,来自服务器: ${serverName}`,
165
+ toolDef.description || '',
166
+ '',
167
+ '执行外部工具,需要用户确认。',
168
+ ].filter(Boolean).join('\n'),
169
+ important: [
170
+ `From MCP server: ${serverName}`,
171
+ 'Executes external tools; user confirmation required',
172
+ ],
173
+ },
174
+ category: 'mcp',
175
+ tags: ['mcp', 'external', serverName],
176
+
177
+ async execute(params) {
178
+ try {
179
+ const result = await mcpClient.callTool(toolDef.name, params);
180
+
181
+ // 处理响应内
182
+ let llmContent = '';
183
+ let displayContent = '';
184
+
185
+ if (result.content && Array.isArray(result.content)) {
186
+ for (const item of result.content) {
187
+ if (item.type === 'text' && item.text) {
188
+ llmContent += item.text;
189
+ displayContent += item.text;
190
+ } else if (item.type === 'image') {
191
+ displayContent += `[图片: ${item.mimeType || 'unknown'}]\n`;
192
+ llmContent += `[image: ${item.mimeType || 'unknown'}]\n`;
193
+ } else if (item.type === 'resource') {
194
+ displayContent += `[资源: ${item.uri || item.mimeType || 'unknown'}]\n`;
195
+ llmContent += `[resource: ${item.uri || item.mimeType || 'unknown'}]\n`;
196
+ }
197
+ }
198
+ }
199
+
200
+ if (result.isError) {
201
+ return {
202
+ success: false,
203
+ llmContent: llmContent || 'MCP tool execution failed',
204
+ displayContent: `❌ ${displayContent || 'MCP工具执行失败'}`,
205
+ error: {
206
+ type: ToolErrorType.EXECUTION_ERROR,
207
+ message: llmContent || 'MCP tool execution failed',
208
+ },
209
+ };
210
+ }
211
+
212
+ return {
213
+ success: true,
214
+ llmContent: llmContent || 'Execution succeeded',
215
+ displayContent: `✅ MCP工具 ${toolDef.name} 执行成功\n${displayContent}`,
216
+ metadata: {
217
+ serverName,
218
+ toolName: toolDef.name,
219
+ mcpResult: result,
220
+ },
221
+ };
222
+ } catch (error) {
223
+ const errorMessage = (error as Error).message;
224
+ return {
225
+ success: false,
226
+ llmContent: `MCP tool execution failed: ${errorMessage}`,
227
+ displayContent: `❌ MCP工具执行失败: ${errorMessage}`,
228
+ error: {
229
+ type: ToolErrorType.EXECUTION_ERROR,
230
+ message: errorMessage,
231
+ },
232
+ };
233
+ }
234
+ },
235
+ });
236
+ }
237
+
238
+ /**
239
+ *
240
+ */
241
+ export function createMcpTools(
242
+ mcpClient: McpClientInterface,
243
+ serverName: string,
244
+ toolDefs: McpToolDefinition[],
245
+ namePrefix?: string
246
+ ) {
247
+ return toolDefs.map(toolDef => {
248
+ const customName = namePrefix ? `${namePrefix}__${toolDef.name}` : undefined;
249
+ return createMcpTool(mcpClient, serverName, toolDef, customName);
250
+ });
251
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * MCP 协议模块
3
+ * Model Context Protocol - Anthropic 推出的 AI 工具扩展协议
4
+ */
5
+
6
+ // 类型导
7
+ export * from './types.js';
8
+
9
+ // 核心组
10
+ export { McpClient } from './McpClient.js';
11
+ export { McpRegistry } from './McpRegistry.js';
12
+ export { HealthMonitor } from './HealthMonitor.js';
13
+
14
+ // 工具转
15
+ export { createMcpTool, createMcpTools } from './createMcpTool.js';