wave-agent-sdk 0.0.1
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/README.md +32 -0
- package/dist/agent.d.ts +96 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +286 -0
- package/dist/hooks/executor.d.ts +56 -0
- package/dist/hooks/executor.d.ts.map +1 -0
- package/dist/hooks/executor.js +312 -0
- package/dist/hooks/index.d.ts +17 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/manager.d.ts +90 -0
- package/dist/hooks/manager.d.ts.map +1 -0
- package/dist/hooks/manager.js +395 -0
- package/dist/hooks/matcher.d.ts +49 -0
- package/dist/hooks/matcher.d.ts.map +1 -0
- package/dist/hooks/matcher.js +147 -0
- package/dist/hooks/settings.d.ts +46 -0
- package/dist/hooks/settings.d.ts.map +1 -0
- package/dist/hooks/settings.js +100 -0
- package/dist/hooks/types.d.ts +80 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +59 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/managers/aiManager.d.ts +61 -0
- package/dist/managers/aiManager.d.ts.map +1 -0
- package/dist/managers/aiManager.js +415 -0
- package/dist/managers/backgroundBashManager.d.ts +27 -0
- package/dist/managers/backgroundBashManager.d.ts.map +1 -0
- package/dist/managers/backgroundBashManager.js +166 -0
- package/dist/managers/bashManager.d.ts +20 -0
- package/dist/managers/bashManager.d.ts.map +1 -0
- package/dist/managers/bashManager.js +66 -0
- package/dist/managers/mcpManager.d.ts +63 -0
- package/dist/managers/mcpManager.d.ts.map +1 -0
- package/dist/managers/mcpManager.js +378 -0
- package/dist/managers/messageManager.d.ts +85 -0
- package/dist/managers/messageManager.d.ts.map +1 -0
- package/dist/managers/messageManager.js +265 -0
- package/dist/managers/skillManager.d.ts +59 -0
- package/dist/managers/skillManager.d.ts.map +1 -0
- package/dist/managers/skillManager.js +317 -0
- package/dist/managers/slashCommandManager.d.ts +77 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -0
- package/dist/managers/slashCommandManager.js +208 -0
- package/dist/managers/toolManager.d.ts +23 -0
- package/dist/managers/toolManager.d.ts.map +1 -0
- package/dist/managers/toolManager.js +79 -0
- package/dist/services/aiService.d.ts +28 -0
- package/dist/services/aiService.d.ts.map +1 -0
- package/dist/services/aiService.js +180 -0
- package/dist/services/memory.d.ts +8 -0
- package/dist/services/memory.d.ts.map +1 -0
- package/dist/services/memory.js +128 -0
- package/dist/services/session.d.ts +54 -0
- package/dist/services/session.d.ts.map +1 -0
- package/dist/services/session.js +196 -0
- package/dist/tools/bashTool.d.ts +14 -0
- package/dist/tools/bashTool.d.ts.map +1 -0
- package/dist/tools/bashTool.js +351 -0
- package/dist/tools/deleteFileTool.d.ts +6 -0
- package/dist/tools/deleteFileTool.d.ts.map +1 -0
- package/dist/tools/deleteFileTool.js +67 -0
- package/dist/tools/editTool.d.ts +6 -0
- package/dist/tools/editTool.d.ts.map +1 -0
- package/dist/tools/editTool.js +168 -0
- package/dist/tools/globTool.d.ts +6 -0
- package/dist/tools/globTool.d.ts.map +1 -0
- package/dist/tools/globTool.js +113 -0
- package/dist/tools/grepTool.d.ts +6 -0
- package/dist/tools/grepTool.d.ts.map +1 -0
- package/dist/tools/grepTool.js +268 -0
- package/dist/tools/lsTool.d.ts +6 -0
- package/dist/tools/lsTool.d.ts.map +1 -0
- package/dist/tools/lsTool.js +160 -0
- package/dist/tools/multiEditTool.d.ts +6 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -0
- package/dist/tools/multiEditTool.js +222 -0
- package/dist/tools/readTool.d.ts +6 -0
- package/dist/tools/readTool.d.ts.map +1 -0
- package/dist/tools/readTool.js +136 -0
- package/dist/tools/types.d.ts +35 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +4 -0
- package/dist/tools/writeTool.d.ts +6 -0
- package/dist/tools/writeTool.d.ts.map +1 -0
- package/dist/tools/writeTool.js +138 -0
- package/dist/types.d.ts +212 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/utils/bashHistory.d.ts +46 -0
- package/dist/utils/bashHistory.d.ts.map +1 -0
- package/dist/utils/bashHistory.js +236 -0
- package/dist/utils/commandArgumentParser.d.ts +34 -0
- package/dist/utils/commandArgumentParser.d.ts.map +1 -0
- package/dist/utils/commandArgumentParser.js +123 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/convertMessagesForAPI.d.ts +9 -0
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -0
- package/dist/utils/convertMessagesForAPI.js +189 -0
- package/dist/utils/customCommands.d.ts +14 -0
- package/dist/utils/customCommands.d.ts.map +1 -0
- package/dist/utils/customCommands.js +71 -0
- package/dist/utils/fileFilter.d.ts +26 -0
- package/dist/utils/fileFilter.d.ts.map +1 -0
- package/dist/utils/fileFilter.js +177 -0
- package/dist/utils/markdownParser.d.ts +27 -0
- package/dist/utils/markdownParser.d.ts.map +1 -0
- package/dist/utils/markdownParser.js +109 -0
- package/dist/utils/mcpUtils.d.ts +24 -0
- package/dist/utils/mcpUtils.d.ts.map +1 -0
- package/dist/utils/mcpUtils.js +51 -0
- package/dist/utils/messageOperations.d.ts +118 -0
- package/dist/utils/messageOperations.d.ts.map +1 -0
- package/dist/utils/messageOperations.js +334 -0
- package/dist/utils/path.d.ts +25 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +109 -0
- package/dist/utils/skillParser.d.ts +18 -0
- package/dist/utils/skillParser.d.ts.map +1 -0
- package/dist/utils/skillParser.js +147 -0
- package/dist/utils/stringUtils.d.ts +13 -0
- package/dist/utils/stringUtils.d.ts.map +1 -0
- package/dist/utils/stringUtils.js +44 -0
- package/package.json +51 -0
- package/src/agent.ts +405 -0
- package/src/hooks/executor.ts +440 -0
- package/src/hooks/index.ts +52 -0
- package/src/hooks/manager.ts +618 -0
- package/src/hooks/matcher.ts +187 -0
- package/src/hooks/settings.ts +129 -0
- package/src/hooks/types.ts +169 -0
- package/src/index.ts +24 -0
- package/src/managers/aiManager.ts +573 -0
- package/src/managers/backgroundBashManager.ts +203 -0
- package/src/managers/bashManager.ts +97 -0
- package/src/managers/mcpManager.ts +493 -0
- package/src/managers/messageManager.ts +415 -0
- package/src/managers/skillManager.ts +404 -0
- package/src/managers/slashCommandManager.ts +293 -0
- package/src/managers/toolManager.ts +106 -0
- package/src/services/aiService.ts +252 -0
- package/src/services/memory.ts +149 -0
- package/src/services/session.ts +265 -0
- package/src/tools/bashTool.ts +402 -0
- package/src/tools/deleteFileTool.ts +81 -0
- package/src/tools/editTool.ts +192 -0
- package/src/tools/globTool.ts +135 -0
- package/src/tools/grepTool.ts +326 -0
- package/src/tools/lsTool.ts +187 -0
- package/src/tools/multiEditTool.ts +268 -0
- package/src/tools/readTool.ts +165 -0
- package/src/tools/types.ts +47 -0
- package/src/tools/writeTool.ts +163 -0
- package/src/types.ts +260 -0
- package/src/utils/bashHistory.ts +303 -0
- package/src/utils/commandArgumentParser.ts +153 -0
- package/src/utils/constants.ts +37 -0
- package/src/utils/convertMessagesForAPI.ts +236 -0
- package/src/utils/customCommands.ts +85 -0
- package/src/utils/fileFilter.ts +202 -0
- package/src/utils/markdownParser.ts +156 -0
- package/src/utils/mcpUtils.ts +81 -0
- package/src/utils/messageOperations.ts +506 -0
- package/src/utils/path.ts +118 -0
- package/src/utils/skillParser.ts +188 -0
- package/src/utils/stringUtils.ts +50 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
+
import { ChatCompletionFunctionTool } from "openai/resources.js";
|
|
6
|
+
import { createMcpToolPlugin, findToolServer } from "@/utils/mcpUtils.js";
|
|
7
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "../tools/types.js";
|
|
8
|
+
import type {
|
|
9
|
+
Logger,
|
|
10
|
+
McpServerConfig,
|
|
11
|
+
McpConfig,
|
|
12
|
+
McpTool,
|
|
13
|
+
McpServerStatus,
|
|
14
|
+
} from "../types.js";
|
|
15
|
+
|
|
16
|
+
interface McpConnection {
|
|
17
|
+
client: Client;
|
|
18
|
+
transport: StdioClientTransport;
|
|
19
|
+
process: null; // StdioClientTransport manages process internally
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface McpManagerCallbacks {
|
|
23
|
+
onServersChange?: (servers: McpServerStatus[]) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface McpManagerOptions {
|
|
27
|
+
callbacks?: McpManagerCallbacks;
|
|
28
|
+
logger?: Logger;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class McpManager {
|
|
32
|
+
private config: McpConfig | null = null;
|
|
33
|
+
private servers: Map<string, McpServerStatus> = new Map();
|
|
34
|
+
private connections: Map<string, McpConnection> = new Map();
|
|
35
|
+
private configPath: string = "";
|
|
36
|
+
private workdir: string = "";
|
|
37
|
+
private callbacks: McpManagerCallbacks;
|
|
38
|
+
private logger?: Logger;
|
|
39
|
+
|
|
40
|
+
constructor(options: McpManagerOptions = {}) {
|
|
41
|
+
this.callbacks = options.callbacks || {};
|
|
42
|
+
this.logger = options.logger;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Initialize MCP manager with working directory and optionally auto-connect
|
|
47
|
+
*/
|
|
48
|
+
async initialize(
|
|
49
|
+
workdir: string,
|
|
50
|
+
autoConnect: boolean = false,
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
this.configPath = join(workdir, ".mcp.json");
|
|
53
|
+
this.workdir = workdir;
|
|
54
|
+
|
|
55
|
+
if (autoConnect) {
|
|
56
|
+
this.logger?.info("Initializing MCP servers...");
|
|
57
|
+
|
|
58
|
+
// Ensure MCP configuration is loaded
|
|
59
|
+
const config = await this.ensureConfigLoaded();
|
|
60
|
+
|
|
61
|
+
if (config && config.mcpServers) {
|
|
62
|
+
// Connect to all configured servers
|
|
63
|
+
const connectionPromises = Object.keys(config.mcpServers).map(
|
|
64
|
+
async (serverName) => {
|
|
65
|
+
try {
|
|
66
|
+
this.logger?.info(`Connecting to MCP server: ${serverName}`);
|
|
67
|
+
const success = await this.connectServer(serverName);
|
|
68
|
+
if (success) {
|
|
69
|
+
this.logger?.info(
|
|
70
|
+
`Successfully connected to MCP server: ${serverName}`,
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
this.logger?.warn(
|
|
74
|
+
`Failed to connect to MCP server: ${serverName}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
this.logger?.error(
|
|
79
|
+
`Error connecting to MCP server ${serverName}`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Wait for all connection attempts to complete
|
|
86
|
+
await Promise.all(connectionPromises);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.logger?.info("MCP servers initialization completed");
|
|
90
|
+
// Trigger state change callback after initialization
|
|
91
|
+
this.callbacks.onServersChange?.(this.getAllServers());
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async ensureConfigLoaded(): Promise<McpConfig | null> {
|
|
96
|
+
if (!this.config) {
|
|
97
|
+
return this.loadConfig();
|
|
98
|
+
}
|
|
99
|
+
return this.config;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async loadConfig(): Promise<McpConfig | null> {
|
|
103
|
+
if (!this.configPath) {
|
|
104
|
+
this.logger?.warn("MCP config path not set. Call initialize() first.");
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const configContent = await fs.readFile(this.configPath, "utf-8");
|
|
110
|
+
this.config = JSON.parse(configContent);
|
|
111
|
+
|
|
112
|
+
// Initialize server statuses (preserve existing status for already known servers)
|
|
113
|
+
if (this.config) {
|
|
114
|
+
for (const [name, config] of Object.entries(this.config.mcpServers)) {
|
|
115
|
+
const existingServer = this.servers.get(name);
|
|
116
|
+
|
|
117
|
+
if (existingServer) {
|
|
118
|
+
// Update config but preserve status and other runtime info
|
|
119
|
+
this.servers.set(name, {
|
|
120
|
+
...existingServer,
|
|
121
|
+
config, // Update config in case it changed
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
// New server, initialize with disconnected status
|
|
125
|
+
this.servers.set(name, {
|
|
126
|
+
name,
|
|
127
|
+
config,
|
|
128
|
+
status: "disconnected",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return this.config;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Only log error if it's not a "file not found" error
|
|
137
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
138
|
+
this.logger?.error("Failed to load .mcp.json:", error);
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async saveConfig(config: McpConfig): Promise<boolean> {
|
|
145
|
+
try {
|
|
146
|
+
await fs.writeFile(this.configPath, JSON.stringify(config, null, 2));
|
|
147
|
+
this.config = config;
|
|
148
|
+
return true;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.logger?.error("Failed to save .mcp.json:", error);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
getConfig(): McpConfig | null {
|
|
156
|
+
return this.config;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getAllServers(): McpServerStatus[] {
|
|
160
|
+
return Array.from(this.servers.values());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getServer(name: string): McpServerStatus | undefined {
|
|
164
|
+
return this.servers.get(name);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
updateServerStatus(name: string, updates: Partial<McpServerStatus>): void {
|
|
168
|
+
const server = this.servers.get(name);
|
|
169
|
+
if (server) {
|
|
170
|
+
this.servers.set(name, { ...server, ...updates });
|
|
171
|
+
// Trigger state change callback
|
|
172
|
+
this.callbacks.onServersChange?.(this.getAllServers());
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
addServer(name: string, config: McpServerConfig): boolean {
|
|
177
|
+
if (this.servers.has(name)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const newServer: McpServerStatus = {
|
|
182
|
+
name,
|
|
183
|
+
config,
|
|
184
|
+
status: "disconnected",
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
this.servers.set(name, newServer);
|
|
188
|
+
|
|
189
|
+
// Update config
|
|
190
|
+
if (this.config) {
|
|
191
|
+
this.config.mcpServers[name] = config;
|
|
192
|
+
} else {
|
|
193
|
+
this.config = {
|
|
194
|
+
mcpServers: { [name]: config },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
removeServer(name: string): boolean {
|
|
202
|
+
// Disconnect if connected
|
|
203
|
+
if (this.connections.has(name)) {
|
|
204
|
+
this.disconnectServer(name);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const removed = this.servers.delete(name);
|
|
208
|
+
|
|
209
|
+
if (removed && this.config) {
|
|
210
|
+
delete this.config.mcpServers[name];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return removed;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Real MCP connection implementation
|
|
217
|
+
async connectServer(name: string): Promise<boolean> {
|
|
218
|
+
const server = this.servers.get(name);
|
|
219
|
+
if (!server) return false;
|
|
220
|
+
|
|
221
|
+
// Already connected
|
|
222
|
+
if (this.connections.has(name)) return true;
|
|
223
|
+
|
|
224
|
+
this.updateServerStatus(name, { status: "connecting" });
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
// Create transport - it will manage the process
|
|
228
|
+
const transport = new StdioClientTransport({
|
|
229
|
+
command: server.config.command,
|
|
230
|
+
args: server.config.args || [],
|
|
231
|
+
env: server.config.env || {},
|
|
232
|
+
cwd: this.workdir, // Use the agent's workdir as the process working directory
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Create client
|
|
236
|
+
const client = new Client(
|
|
237
|
+
{
|
|
238
|
+
name: "wave-code",
|
|
239
|
+
version: "1.0.0",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
capabilities: {
|
|
243
|
+
tools: {},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
// Handle transport errors
|
|
249
|
+
transport.onerror = (error: Error) => {
|
|
250
|
+
this.logger?.error(`MCP Server ${name} transport error:`, error);
|
|
251
|
+
this.updateServerStatus(name, {
|
|
252
|
+
status: "error",
|
|
253
|
+
error: error.message,
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
transport.onclose = () => {
|
|
258
|
+
this.logger?.info(`MCP Server ${name} transport closed`);
|
|
259
|
+
this.connections.delete(name);
|
|
260
|
+
this.updateServerStatus(name, {
|
|
261
|
+
status: "disconnected",
|
|
262
|
+
tools: [],
|
|
263
|
+
toolCount: 0,
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Connect to transport
|
|
268
|
+
await client.connect(transport);
|
|
269
|
+
|
|
270
|
+
// List available tools
|
|
271
|
+
const toolsResponse = await client.listTools();
|
|
272
|
+
|
|
273
|
+
const tools: McpTool[] =
|
|
274
|
+
toolsResponse.tools?.map((tool) => ({
|
|
275
|
+
name: tool.name,
|
|
276
|
+
description: tool.description,
|
|
277
|
+
inputSchema: tool.inputSchema,
|
|
278
|
+
})) || [];
|
|
279
|
+
|
|
280
|
+
// Store connection (we don't have direct process access with StdioClientTransport)
|
|
281
|
+
this.connections.set(name, {
|
|
282
|
+
client,
|
|
283
|
+
transport,
|
|
284
|
+
process: null, // StdioClientTransport manages the process internally
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Update status
|
|
288
|
+
this.updateServerStatus(name, {
|
|
289
|
+
status: "connected",
|
|
290
|
+
tools,
|
|
291
|
+
toolCount: tools.length,
|
|
292
|
+
capabilities: ["tools"],
|
|
293
|
+
lastConnected: Date.now(),
|
|
294
|
+
error: undefined,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
return true;
|
|
298
|
+
} catch (error) {
|
|
299
|
+
this.logger?.error(`Failed to connect to MCP server ${name}:`, error);
|
|
300
|
+
// updateServerStatus will trigger the callback
|
|
301
|
+
this.updateServerStatus(name, {
|
|
302
|
+
status: "error",
|
|
303
|
+
error: error instanceof Error ? error.message : String(error),
|
|
304
|
+
});
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async disconnectServer(name: string): Promise<boolean> {
|
|
310
|
+
const connection = this.connections.get(name);
|
|
311
|
+
if (!connection) return false;
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
// Close client connection and transport
|
|
315
|
+
await connection.client.close();
|
|
316
|
+
await connection.transport.close();
|
|
317
|
+
|
|
318
|
+
// Remove connection
|
|
319
|
+
this.connections.delete(name);
|
|
320
|
+
|
|
321
|
+
// updateServerStatus will trigger the callback
|
|
322
|
+
this.updateServerStatus(name, {
|
|
323
|
+
status: "disconnected",
|
|
324
|
+
tools: [],
|
|
325
|
+
toolCount: 0,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
return true;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
this.logger?.error(`Error disconnecting from MCP server ${name}:`, error);
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Get all tools from connected servers
|
|
336
|
+
getAllConnectedTools(): McpTool[] {
|
|
337
|
+
const allTools: McpTool[] = [];
|
|
338
|
+
for (const server of this.servers.values()) {
|
|
339
|
+
if (server.status === "connected" && server.tools) {
|
|
340
|
+
allTools.push(...server.tools);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return allTools;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Execute MCP tool
|
|
347
|
+
async executeMcpTool(
|
|
348
|
+
toolName: string,
|
|
349
|
+
args: Record<string, unknown>,
|
|
350
|
+
): Promise<{
|
|
351
|
+
success: boolean;
|
|
352
|
+
content: string;
|
|
353
|
+
serverName?: string;
|
|
354
|
+
images?: Array<{ data: string; mediaType?: string }>;
|
|
355
|
+
}> {
|
|
356
|
+
// Find which server has this tool
|
|
357
|
+
for (const [serverName, server] of this.servers.entries()) {
|
|
358
|
+
if (server.status === "connected" && server.tools) {
|
|
359
|
+
const tool = server.tools.find((t) => t.name === toolName);
|
|
360
|
+
if (tool) {
|
|
361
|
+
const connection = this.connections.get(serverName);
|
|
362
|
+
if (connection) {
|
|
363
|
+
try {
|
|
364
|
+
const result = await connection.client.callTool({
|
|
365
|
+
name: toolName,
|
|
366
|
+
arguments: args,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Separate text content and image data
|
|
370
|
+
const textContent: string[] = [];
|
|
371
|
+
const images: Array<{ data: string; mediaType?: string }> = [];
|
|
372
|
+
|
|
373
|
+
if (Array.isArray(result.content)) {
|
|
374
|
+
result.content.forEach(
|
|
375
|
+
(c: {
|
|
376
|
+
type: string;
|
|
377
|
+
text?: string;
|
|
378
|
+
data?: string;
|
|
379
|
+
resource?: { uri: string };
|
|
380
|
+
[key: string]: unknown;
|
|
381
|
+
}) => {
|
|
382
|
+
if (c.type === "text") {
|
|
383
|
+
textContent.push(c.text || "");
|
|
384
|
+
} else if (c.type === "image" && c.data) {
|
|
385
|
+
images.push({
|
|
386
|
+
data: c.data,
|
|
387
|
+
mediaType: "image/png", // Default to PNG, can be adjusted according to actual situation
|
|
388
|
+
});
|
|
389
|
+
} else if (c.type === "resource") {
|
|
390
|
+
textContent.push(`[Resource: ${c.resource?.uri || ""}]`);
|
|
391
|
+
} else {
|
|
392
|
+
textContent.push(JSON.stringify(c));
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
);
|
|
396
|
+
} else if (result.content) {
|
|
397
|
+
textContent.push(String(result.content));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
success: true,
|
|
402
|
+
content:
|
|
403
|
+
textContent.length > 0
|
|
404
|
+
? textContent.join("\n")
|
|
405
|
+
: "No content",
|
|
406
|
+
images: images.length > 0 ? images : undefined,
|
|
407
|
+
serverName,
|
|
408
|
+
};
|
|
409
|
+
} catch (error) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
throw new Error(`Tool ${toolName} not found on any connected MCP server`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Cleanup all connections
|
|
423
|
+
async cleanup(): Promise<void> {
|
|
424
|
+
const disconnectPromises = Array.from(this.connections.keys()).map((name) =>
|
|
425
|
+
this.disconnectServer(name),
|
|
426
|
+
);
|
|
427
|
+
await Promise.all(disconnectPromises);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ========== Tools Registry Methods ==========
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get all currently available MCP tools as plugins
|
|
434
|
+
*/
|
|
435
|
+
getMcpToolPlugins(): ToolPlugin[] {
|
|
436
|
+
const mcpTools = new Map<string, ToolPlugin>();
|
|
437
|
+
|
|
438
|
+
// Get all connected MCP tools
|
|
439
|
+
const connectedTools = this.getAllConnectedTools();
|
|
440
|
+
const servers = this.getAllServers();
|
|
441
|
+
|
|
442
|
+
// Find server name for each tool and create plugins
|
|
443
|
+
for (const tool of connectedTools) {
|
|
444
|
+
// Find which server this tool belongs to
|
|
445
|
+
const server = findToolServer(tool.name, servers);
|
|
446
|
+
|
|
447
|
+
if (server) {
|
|
448
|
+
const plugin = createMcpToolPlugin(tool, server.name, (name, args) =>
|
|
449
|
+
this.executeMcpTool(name, args),
|
|
450
|
+
);
|
|
451
|
+
mcpTools.set(tool.name, plugin);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return Array.from(mcpTools.values());
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Get all currently available MCP tools as OpenAI function tools
|
|
460
|
+
*/
|
|
461
|
+
getMcpToolsConfig(): ChatCompletionFunctionTool[] {
|
|
462
|
+
return this.getMcpToolPlugins().map((tool) => tool.config);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Execute an MCP tool by name (registry version)
|
|
467
|
+
*/
|
|
468
|
+
async executeMcpToolByRegistry(
|
|
469
|
+
name: string,
|
|
470
|
+
args: Record<string, unknown>,
|
|
471
|
+
context: ToolContext,
|
|
472
|
+
): Promise<ToolResult> {
|
|
473
|
+
const plugins = this.getMcpToolPlugins();
|
|
474
|
+
const tool = plugins.find((plugin) => plugin.name === name);
|
|
475
|
+
|
|
476
|
+
if (!tool) {
|
|
477
|
+
return {
|
|
478
|
+
success: false,
|
|
479
|
+
content: "",
|
|
480
|
+
error: `MCP tool '${name}' not found or server disconnected`,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
return tool.execute(args, context);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Check if a tool name belongs to an MCP tool
|
|
488
|
+
*/
|
|
489
|
+
isMcpTool(name: string): boolean {
|
|
490
|
+
const connectedTools = this.getAllConnectedTools();
|
|
491
|
+
return connectedTools.some((tool) => tool.name === name);
|
|
492
|
+
}
|
|
493
|
+
}
|