@mcp-use/cli 1.0.0 → 1.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/dist/InputPrompt.d.ts +13 -0
- package/dist/InputPrompt.js +188 -0
- package/dist/MultilineInput.d.ts +13 -0
- package/dist/MultilineInput.js +154 -0
- package/dist/MultilineTextInput.d.ts +11 -0
- package/dist/MultilineTextInput.js +97 -0
- package/dist/PasteAwareInput.d.ts +13 -0
- package/dist/PasteAwareInput.js +183 -0
- package/dist/SimpleMultilineInput.d.ts +11 -0
- package/dist/SimpleMultilineInput.js +125 -0
- package/dist/app.d.ts +1 -5
- package/dist/app.js +291 -186
- package/dist/cli.js +2 -5
- package/dist/commands.d.ts +15 -30
- package/dist/commands.js +308 -568
- package/dist/components/AsciiLogo.d.ts +2 -0
- package/dist/components/AsciiLogo.js +7 -0
- package/dist/components/Footer.d.ts +5 -0
- package/dist/components/Footer.js +19 -0
- package/dist/components/InputPrompt.d.ts +13 -0
- package/dist/components/InputPrompt.js +188 -0
- package/dist/components/Messages.d.ts +21 -0
- package/dist/components/Messages.js +80 -0
- package/dist/components/ServerStatus.d.ts +7 -0
- package/dist/components/ServerStatus.js +36 -0
- package/dist/components/Spinner.d.ts +16 -0
- package/dist/components/Spinner.js +63 -0
- package/dist/components/ToolStatus.d.ts +8 -0
- package/dist/components/ToolStatus.js +33 -0
- package/dist/components/textInput.d.ts +1 -0
- package/dist/components/textInput.js +1 -0
- package/dist/logger.d.ts +10 -0
- package/dist/logger.js +48 -0
- package/dist/mcp-service.d.ts +5 -4
- package/dist/mcp-service.js +98 -207
- package/dist/services/agent-service.d.ts +56 -0
- package/dist/services/agent-service.js +203 -0
- package/dist/services/cli-service.d.ts +132 -0
- package/dist/services/cli-service.js +591 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +4 -0
- package/dist/services/llm-service.d.ts +174 -0
- package/dist/services/llm-service.js +567 -0
- package/dist/services/mcp-config-service.d.ts +69 -0
- package/dist/services/mcp-config-service.js +426 -0
- package/dist/services/mcp-service.d.ts +1 -0
- package/dist/services/mcp-service.js +1 -0
- package/dist/services/utility-service.d.ts +47 -0
- package/dist/services/utility-service.js +208 -0
- package/dist/storage.js +4 -4
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/package.json +22 -8
- package/readme.md +68 -39
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
3
|
+
import { AgentService } from './agent-service.js';
|
|
4
|
+
import { LLMService } from './llm-service.js';
|
|
5
|
+
import { MCPConfigService } from './mcp-config-service.js';
|
|
6
|
+
import { UtilityService } from './utility-service.js';
|
|
7
|
+
// Load environment variables
|
|
8
|
+
config();
|
|
9
|
+
export class CLIService {
|
|
10
|
+
/**
|
|
11
|
+
* Initializes the CLIService and its dependencies. This acts as the
|
|
12
|
+
* composition root for the application's services.
|
|
13
|
+
*/
|
|
14
|
+
constructor() {
|
|
15
|
+
Object.defineProperty(this, "isInitialized", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: false
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "agentService", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: void 0
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "llmService", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: void 0
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "mcpConfigService", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: void 0
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(this, "utilityService", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
value: void 0
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(this, "commandRegistry", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: void 0
|
|
50
|
+
});
|
|
51
|
+
// Initialize services
|
|
52
|
+
this.llmService = new LLMService();
|
|
53
|
+
this.mcpConfigService = new MCPConfigService();
|
|
54
|
+
this.agentService = new AgentService({
|
|
55
|
+
llmService: this.llmService,
|
|
56
|
+
});
|
|
57
|
+
this.utilityService = new UtilityService({
|
|
58
|
+
llmService: this.llmService,
|
|
59
|
+
mcpConfigService: this.mcpConfigService,
|
|
60
|
+
});
|
|
61
|
+
// Initialize command registry
|
|
62
|
+
this.commandRegistry = new Map();
|
|
63
|
+
this.registerCommands();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Registers all available commands in the command registry.
|
|
67
|
+
* This creates a centralized mapping of command names to their handlers.
|
|
68
|
+
*/
|
|
69
|
+
registerCommands() {
|
|
70
|
+
// LLM-related commands
|
|
71
|
+
this.commandRegistry.set('/model', {
|
|
72
|
+
handler: args => this.llmService.handleModelCommand(args),
|
|
73
|
+
description: 'Choose your LLM provider and model',
|
|
74
|
+
});
|
|
75
|
+
this.commandRegistry.set('/models', {
|
|
76
|
+
handler: args => this.llmService.handleListModelsCommand(args),
|
|
77
|
+
description: 'List available models',
|
|
78
|
+
});
|
|
79
|
+
this.commandRegistry.set('/setkey', {
|
|
80
|
+
handler: args => this.llmService.handleSetKeyCommand(args),
|
|
81
|
+
description: 'Set API key manually',
|
|
82
|
+
});
|
|
83
|
+
this.commandRegistry.set('/clearkeys', {
|
|
84
|
+
handler: () => this.llmService.handleClearKeysCommand(),
|
|
85
|
+
description: 'Clear all stored API keys',
|
|
86
|
+
});
|
|
87
|
+
this.commandRegistry.set('/config', {
|
|
88
|
+
handler: args => this.llmService.handleConfigCommand(args),
|
|
89
|
+
description: 'Configure temperature and max tokens',
|
|
90
|
+
});
|
|
91
|
+
// MCP server commands with subcommands
|
|
92
|
+
this.commandRegistry.set('/server', {
|
|
93
|
+
handler: args => this.handleServerCommand(args),
|
|
94
|
+
description: 'Manage MCP servers',
|
|
95
|
+
});
|
|
96
|
+
this.commandRegistry.set('/server add', {
|
|
97
|
+
handler: () => this.handleServerAddCommand(),
|
|
98
|
+
description: 'Configure a new server',
|
|
99
|
+
});
|
|
100
|
+
this.commandRegistry.set('/server connect', {
|
|
101
|
+
handler: async (args) => this.handleServerConnectCommand(args),
|
|
102
|
+
description: 'Connect to a configured server',
|
|
103
|
+
});
|
|
104
|
+
this.commandRegistry.set('/server disconnect', {
|
|
105
|
+
handler: async (args) => this.handleServerDisconnectCommand(args),
|
|
106
|
+
description: 'Disconnect from a server',
|
|
107
|
+
});
|
|
108
|
+
this.commandRegistry.set('/servers', {
|
|
109
|
+
handler: () => this.handleListServersCommand(),
|
|
110
|
+
description: 'List configured servers',
|
|
111
|
+
});
|
|
112
|
+
this.commandRegistry.set('/test-server', {
|
|
113
|
+
handler: args => this.mcpConfigService.handleTestServerCommand(args),
|
|
114
|
+
description: 'Test server configuration',
|
|
115
|
+
});
|
|
116
|
+
this.commandRegistry.set('/tools', {
|
|
117
|
+
handler: () => this.agentService.handleListToolsCommand(),
|
|
118
|
+
description: 'Show available MCP tools',
|
|
119
|
+
});
|
|
120
|
+
// Utility commands
|
|
121
|
+
this.commandRegistry.set('/help', {
|
|
122
|
+
handler: () => this.utilityService.handleHelpCommand(),
|
|
123
|
+
description: 'Show this help',
|
|
124
|
+
});
|
|
125
|
+
this.commandRegistry.set('/status', {
|
|
126
|
+
handler: () => this.utilityService.handleStatusCommand(),
|
|
127
|
+
description: 'Show current configuration',
|
|
128
|
+
});
|
|
129
|
+
this.commandRegistry.set('/logs', {
|
|
130
|
+
handler: args => this.utilityService.handleLogsCommand(args),
|
|
131
|
+
description: 'View debug logs',
|
|
132
|
+
});
|
|
133
|
+
this.commandRegistry.set('/clearlogs', {
|
|
134
|
+
handler: () => this.utilityService.handleClearLogsCommand(),
|
|
135
|
+
description: 'Clear debug logs',
|
|
136
|
+
});
|
|
137
|
+
this.commandRegistry.set('/history', {
|
|
138
|
+
handler: () => this.utilityService.handleHistoryCommand(),
|
|
139
|
+
description: 'Info about input history navigation',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Initializes the CLI service, ensuring the agent is ready.
|
|
144
|
+
* This method is idempotent and will only run once.
|
|
145
|
+
*/
|
|
146
|
+
async initialize() {
|
|
147
|
+
if (this.isInitialized) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
await this.initializeAgent();
|
|
151
|
+
this.isInitialized = true;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Initializes the underlying MCP agent via the AgentService.
|
|
155
|
+
* If initialization fails, an error is logged, but the CLI can continue
|
|
156
|
+
* to operate for command-line tasks.
|
|
157
|
+
*/
|
|
158
|
+
async initializeAgent() {
|
|
159
|
+
try {
|
|
160
|
+
Logger.info('Agent service initialized successfully.');
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
Logger.error('Agent service initialization failed.', {
|
|
164
|
+
error: error instanceof Error ? error.message : String(error),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Re-initializes the agent. This is useful when the configuration
|
|
170
|
+
* (e.g., model or servers) has changed.
|
|
171
|
+
*/
|
|
172
|
+
async refreshAgent() {
|
|
173
|
+
await this.initializeAgent();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Checks if the input is a slash command.
|
|
177
|
+
* @param input - The user input to check
|
|
178
|
+
* @returns True if the input starts with /
|
|
179
|
+
*/
|
|
180
|
+
isCommand(input) {
|
|
181
|
+
return input.trim().startsWith('/');
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Handles command execution by routing to the appropriate service.
|
|
185
|
+
* @param input - The full command input including the slash
|
|
186
|
+
* @returns A promise that resolves to the command result
|
|
187
|
+
*/
|
|
188
|
+
async handleCommand(input) {
|
|
189
|
+
const parts = input.trim().split(/\s+/);
|
|
190
|
+
const command = parts[0];
|
|
191
|
+
let args = parts.slice(1);
|
|
192
|
+
// Try to match with subcommand first
|
|
193
|
+
let commandEntry = null;
|
|
194
|
+
if (args.length > 0) {
|
|
195
|
+
// Try matching with one subcommand (e.g., "/server add")
|
|
196
|
+
const withSubcommand = `${command} ${args[0]}`;
|
|
197
|
+
commandEntry = this.commandRegistry.get(withSubcommand);
|
|
198
|
+
if (commandEntry) {
|
|
199
|
+
// Remove the subcommand from args since it's part of the command
|
|
200
|
+
args = args.slice(1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// If no subcommand match, try the base command
|
|
204
|
+
if (!commandEntry) {
|
|
205
|
+
commandEntry = this.commandRegistry.get(command);
|
|
206
|
+
}
|
|
207
|
+
if (!commandEntry) {
|
|
208
|
+
return {
|
|
209
|
+
type: 'error',
|
|
210
|
+
message: `Unknown command: ${command}. Type /help for available commands.`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Execute the command handler
|
|
214
|
+
try {
|
|
215
|
+
return await commandEntry.handler(args);
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
Logger.error(`Error executing command ${command}`, {
|
|
219
|
+
error: error instanceof Error ? error.message : String(error),
|
|
220
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
221
|
+
});
|
|
222
|
+
return {
|
|
223
|
+
type: 'error',
|
|
224
|
+
message: `Command failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Processes a user's message. It determines if the message is a command
|
|
230
|
+
* or a prompt for the agent and routes it accordingly. It also handles
|
|
231
|
+
* special input modes like API key entry or server configuration.
|
|
232
|
+
* @param message The raw input string from the user.
|
|
233
|
+
* @param isApiKeyInput True if the input is an API key.
|
|
234
|
+
* @param pendingProvider The provider for which the API key is being entered.
|
|
235
|
+
* @param pendingModel The model for which the API key is being entered.
|
|
236
|
+
* @param isServerConfigInput True if the input is part of the server config flow.
|
|
237
|
+
* @param serverConfigStep The current step in the server configuration flow.
|
|
238
|
+
* @param serverConfig The server configuration object being built.
|
|
239
|
+
* @returns A promise that resolves to the result of the message processing.
|
|
240
|
+
*/
|
|
241
|
+
async *sendMessage(message, isApiKeyInput, pendingProvider, pendingModel, isServerConfigInput, serverConfigStep, serverConfig) {
|
|
242
|
+
// Handle server configuration input (non-streaming)
|
|
243
|
+
if (isServerConfigInput && serverConfigStep) {
|
|
244
|
+
const commandResult = this.mcpConfigService.handleServerConfigInput(message.trim(), serverConfigStep, serverConfig);
|
|
245
|
+
yield {
|
|
246
|
+
response: commandResult.message,
|
|
247
|
+
toolCalls: [],
|
|
248
|
+
isCommand: true,
|
|
249
|
+
commandResult,
|
|
250
|
+
done: true,
|
|
251
|
+
};
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Handle API key input (non-streaming)
|
|
255
|
+
if (isApiKeyInput && pendingProvider && pendingModel) {
|
|
256
|
+
const commandResult = this.llmService.handleApiKeyInput(message.trim(), pendingProvider, pendingModel);
|
|
257
|
+
// If successful, reinitialize the agent
|
|
258
|
+
if (commandResult.data?.llmConfig) {
|
|
259
|
+
await this.initializeAgent();
|
|
260
|
+
}
|
|
261
|
+
yield {
|
|
262
|
+
response: commandResult.message,
|
|
263
|
+
toolCalls: [],
|
|
264
|
+
isCommand: true,
|
|
265
|
+
commandResult,
|
|
266
|
+
done: true,
|
|
267
|
+
};
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
// Check if it's a slash command (non-streaming)
|
|
271
|
+
if (this.isCommand(message)) {
|
|
272
|
+
try {
|
|
273
|
+
const commandResult = await this.handleCommand(message);
|
|
274
|
+
// Handle special commands that need coordination
|
|
275
|
+
if (commandResult.data?.checkTools) {
|
|
276
|
+
const toolsResult = await this.agentService.getAvailableTools();
|
|
277
|
+
yield {
|
|
278
|
+
response: 'Available MCP Tools',
|
|
279
|
+
toolCalls: [],
|
|
280
|
+
isCommand: true,
|
|
281
|
+
commandResult: {
|
|
282
|
+
type: 'list_tools',
|
|
283
|
+
message: 'Available MCP Tools',
|
|
284
|
+
data: toolsResult,
|
|
285
|
+
},
|
|
286
|
+
done: true,
|
|
287
|
+
};
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// If the command changed the LLM config, added servers, or needs agent reinitialization
|
|
291
|
+
if (commandResult.data?.llmConfig ||
|
|
292
|
+
commandResult.data?.serversAdded ||
|
|
293
|
+
commandResult.data?.reinitializeAgent) {
|
|
294
|
+
await this.initializeAgent();
|
|
295
|
+
}
|
|
296
|
+
yield {
|
|
297
|
+
response: commandResult.message,
|
|
298
|
+
toolCalls: [],
|
|
299
|
+
isCommand: true,
|
|
300
|
+
commandResult,
|
|
301
|
+
done: true,
|
|
302
|
+
};
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
yield {
|
|
307
|
+
response: `Command error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
308
|
+
toolCalls: [],
|
|
309
|
+
isCommand: true,
|
|
310
|
+
commandResult: { type: 'error', message: 'Command failed' },
|
|
311
|
+
done: true,
|
|
312
|
+
};
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (!this.agentService.isReady()) {
|
|
317
|
+
const availableProviders = this.llmService.getAvailableProviders();
|
|
318
|
+
if (availableProviders.length === 0) {
|
|
319
|
+
yield {
|
|
320
|
+
response: `🤖 Choose a model to get started!\n\nTry one of these popular options:\n• /model openai gpt-4o-mini\n• /model anthropic claude-3-5-sonnet-20241022\n• /model google gemini-1.5-pro\n\nThe CLI will help you set up the API key when needed.\nUse /models to see all available models.`,
|
|
321
|
+
toolCalls: [],
|
|
322
|
+
done: true,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
const firstProvider = availableProviders[0];
|
|
327
|
+
const exampleModel = firstProvider
|
|
328
|
+
? this.getExampleModel(firstProvider)
|
|
329
|
+
: 'model-name';
|
|
330
|
+
yield {
|
|
331
|
+
response: `🔧 No model selected.\n\nAvailable providers: ${availableProviders.join(', ')}\n\nUse /model <provider> <model> to get started.\n\nExample: /model ${firstProvider} ${exampleModel}`,
|
|
332
|
+
toolCalls: [],
|
|
333
|
+
done: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
// Handle agent messages (streaming)
|
|
339
|
+
try {
|
|
340
|
+
const generator = this.agentService.sendMessage(message);
|
|
341
|
+
for await (const chunk of generator) {
|
|
342
|
+
yield {
|
|
343
|
+
response: chunk.response,
|
|
344
|
+
toolCalls: chunk.toolCalls,
|
|
345
|
+
thought: chunk.thought,
|
|
346
|
+
isCommand: false,
|
|
347
|
+
done: false,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
yield { done: true };
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
Logger.error('Error sending message via Agent service', {
|
|
354
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
355
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
356
|
+
});
|
|
357
|
+
yield {
|
|
358
|
+
response: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
359
|
+
thought: undefined,
|
|
360
|
+
done: true,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Returns an example model name for a given provider.
|
|
366
|
+
* @param provider The name of the LLM provider.
|
|
367
|
+
* @returns An example model name string.
|
|
368
|
+
*/
|
|
369
|
+
getExampleModel(provider) {
|
|
370
|
+
const examples = {
|
|
371
|
+
openai: 'gpt-4o-mini',
|
|
372
|
+
anthropic: 'claude-3-5-sonnet-20241022',
|
|
373
|
+
google: 'gemini-1.5-pro',
|
|
374
|
+
mistral: 'mistral-large-latest',
|
|
375
|
+
};
|
|
376
|
+
return examples[provider] || 'model-name';
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Checks if the CLI service has been initialized.
|
|
380
|
+
* @returns True if the service is initialized, false otherwise.
|
|
381
|
+
*/
|
|
382
|
+
isReady() {
|
|
383
|
+
// The CLI is always "ready" to take commands, but the agent might not be.
|
|
384
|
+
// We rely on agentService.isReady() inside sendMessage.
|
|
385
|
+
return this.isInitialized;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Gets a formatted string representing the current model.
|
|
389
|
+
* @returns A string like "provider/model-name" or a status message.
|
|
390
|
+
*/
|
|
391
|
+
getCurrentModel() {
|
|
392
|
+
const config = this.llmService.getCurrentConfig();
|
|
393
|
+
if (!config) {
|
|
394
|
+
const availableProviders = this.llmService.getAvailableProviders();
|
|
395
|
+
if (availableProviders.length === 0) {
|
|
396
|
+
return 'No API keys configured';
|
|
397
|
+
}
|
|
398
|
+
return 'No model selected';
|
|
399
|
+
}
|
|
400
|
+
return `${config.provider}/${config.model}`;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Gets a list of all configured server names from persistent storage.
|
|
404
|
+
* @returns An array of configured server names.
|
|
405
|
+
*/
|
|
406
|
+
getConfiguredServers() {
|
|
407
|
+
const storedServers = this.mcpConfigService.getConfiguredServers();
|
|
408
|
+
return Object.keys(storedServers);
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Gets a list of currently connected server names.
|
|
412
|
+
* @returns An array of connected server names.
|
|
413
|
+
*/
|
|
414
|
+
getConnectedServers() {
|
|
415
|
+
return this.agentService.getConnectedServerNames();
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Gets the list of available tools from the agent.
|
|
419
|
+
* @returns A promise that resolves to an object containing the tools or an error.
|
|
420
|
+
*/
|
|
421
|
+
async getAvailableTools() {
|
|
422
|
+
return this.agentService.getAvailableTools();
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Handles the /servers command by combining configuration and connection status.
|
|
426
|
+
* @returns A CommandResult with the server list including connection status
|
|
427
|
+
*/
|
|
428
|
+
handleListServersCommand() {
|
|
429
|
+
const configuredServers = this.mcpConfigService.getAllServers();
|
|
430
|
+
const connectedServerNames = this.agentService.getConnectedServerNames();
|
|
431
|
+
if (configuredServers.length === 0) {
|
|
432
|
+
return {
|
|
433
|
+
type: 'info',
|
|
434
|
+
message: 'No custom servers configured.\n\nUse /server add to configure servers, then /server connect <name> to connect.',
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
// Add connection status to each server
|
|
438
|
+
const serversWithStatus = configuredServers.map(server => ({
|
|
439
|
+
...server,
|
|
440
|
+
isConnected: connectedServerNames.includes(server.name),
|
|
441
|
+
}));
|
|
442
|
+
return {
|
|
443
|
+
type: 'list_servers',
|
|
444
|
+
message: 'MCP Server Status:',
|
|
445
|
+
data: { servers: serversWithStatus },
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Handles the base /server command to show subcommands.
|
|
450
|
+
* @param args - Command arguments
|
|
451
|
+
* @returns A CommandResult with help information
|
|
452
|
+
*/
|
|
453
|
+
handleServerCommand(args) {
|
|
454
|
+
if (args.length === 0) {
|
|
455
|
+
return {
|
|
456
|
+
type: 'info',
|
|
457
|
+
message: 'Server management commands:\n\n/server add - Configure a new server (stored but not connected)\n/server connect <name> - Connect to a configured server by name\n/server disconnect <name> - Disconnect from a connected server\n/servers - List configured servers and connection status\n\nUse /server <command> for specific help.',
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
// If we get here, it means the subcommand wasn't recognized
|
|
461
|
+
return {
|
|
462
|
+
type: 'error',
|
|
463
|
+
message: 'Usage: /server <command>\n\nCommands:\n add - Configure server\n connect <name> - Connect to server\n disconnect <name> - Disconnect server\n\nExample: /server connect airbnb',
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Handles the /server add subcommand.
|
|
468
|
+
* @returns A CommandResult to start server configuration
|
|
469
|
+
*/
|
|
470
|
+
handleServerAddCommand() {
|
|
471
|
+
return {
|
|
472
|
+
type: 'prompt_server_config',
|
|
473
|
+
message: 'Let\'s configure a new MCP server!\n\nYou can either:\n1. Enter a server name for interactive setup\n2. Paste a complete JSON configuration\n\nExample JSON:\n{\n "mcpServers": {\n "myserver": {\n "command": "npx",\n "args": ["-y", "@example/server"]\n }\n }\n}\n\nEnter server name or paste JSON:',
|
|
474
|
+
data: { step: 'name_or_json' },
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Handles the /server connect subcommand.
|
|
479
|
+
* @param args - Array where args[0] is the server name
|
|
480
|
+
* @returns A CommandResult with connection status
|
|
481
|
+
*/
|
|
482
|
+
async handleServerConnectCommand(args) {
|
|
483
|
+
if (args.length === 0) {
|
|
484
|
+
const configuredServers = Object.keys(this.mcpConfigService.getConfiguredServers());
|
|
485
|
+
if (configuredServers.length === 0) {
|
|
486
|
+
return {
|
|
487
|
+
type: 'error',
|
|
488
|
+
message: 'No servers configured. Use /server add to configure servers first.\n\nUsage: /server connect <server_name>',
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
type: 'error',
|
|
493
|
+
message: `Usage: /server connect <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}`,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
const serverName = args[0];
|
|
497
|
+
if (!serverName) {
|
|
498
|
+
return {
|
|
499
|
+
type: 'error',
|
|
500
|
+
message: 'Server name is required.\n\nUsage: /server connect <server_name>',
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
// Get the server configuration
|
|
504
|
+
const serverConfig = this.mcpConfigService.getServerConfig(serverName);
|
|
505
|
+
if (!serverConfig) {
|
|
506
|
+
return {
|
|
507
|
+
type: 'error',
|
|
508
|
+
message: `Server "${serverName}" is not configured.`,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
// Check if already connected
|
|
512
|
+
if (this.agentService.isServerConnected(serverName)) {
|
|
513
|
+
return {
|
|
514
|
+
type: 'info',
|
|
515
|
+
message: `Server "${serverName}" is already connected.`,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
try {
|
|
519
|
+
// Get current sessions and add the new server
|
|
520
|
+
await this.agentService.connectServer(serverName, serverConfig);
|
|
521
|
+
return {
|
|
522
|
+
type: 'success',
|
|
523
|
+
message: `✅ Connected to server "${serverName}"!`,
|
|
524
|
+
data: { reinitializeAgent: true },
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
Logger.error(`Failed to connect to server ${serverName}`, {
|
|
529
|
+
error: error instanceof Error ? error.message : String(error),
|
|
530
|
+
});
|
|
531
|
+
return {
|
|
532
|
+
type: 'error',
|
|
533
|
+
message: `Failed to connect to server "${serverName}": ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Handles the /server disconnect subcommand.
|
|
539
|
+
* @param args - Array where args[0] is the server name
|
|
540
|
+
* @returns A CommandResult with disconnection status
|
|
541
|
+
*/
|
|
542
|
+
async handleServerDisconnectCommand(args) {
|
|
543
|
+
if (args.length === 0) {
|
|
544
|
+
const connectedServers = this.agentService.getConnectedServerNames();
|
|
545
|
+
if (connectedServers.length === 0) {
|
|
546
|
+
return {
|
|
547
|
+
type: 'info',
|
|
548
|
+
message: 'No servers currently connected.\n\nUsage: /server disconnect <server_name>',
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
type: 'error',
|
|
553
|
+
message: `Usage: /server disconnect <server_name>\n\nConnected servers: ${connectedServers.join(', ')}`,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
const serverName = args[0];
|
|
557
|
+
if (!serverName) {
|
|
558
|
+
return {
|
|
559
|
+
type: 'error',
|
|
560
|
+
message: 'Server name is required.\n\nUsage: /server disconnect <server_name>',
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
// Check if server is connected
|
|
564
|
+
if (!this.agentService.isServerConnected(serverName)) {
|
|
565
|
+
return {
|
|
566
|
+
type: 'error',
|
|
567
|
+
message: `Server "${serverName}" is not connected.`,
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
// Get current sessions and remove the server
|
|
572
|
+
this.agentService.disconnectServer(serverName);
|
|
573
|
+
return {
|
|
574
|
+
type: 'success',
|
|
575
|
+
message: `✅ Disconnected from server "${serverName}".`,
|
|
576
|
+
data: { reinitializeAgent: true },
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
Logger.error(`Failed to disconnect from server ${serverName}`, {
|
|
581
|
+
error: error instanceof Error ? error.message : String(error),
|
|
582
|
+
});
|
|
583
|
+
return {
|
|
584
|
+
type: 'error',
|
|
585
|
+
message: `Failed to disconnect from server "${serverName}": ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Export a singleton instance
|
|
591
|
+
export const cliService = new CLIService();
|