@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,426 @@
|
|
|
1
|
+
import { SecureStorage } from '../storage.js';
|
|
2
|
+
export class MCPConfigService {
|
|
3
|
+
constructor() {
|
|
4
|
+
Object.defineProperty(this, "persistentConfig", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: void 0
|
|
9
|
+
});
|
|
10
|
+
this.persistentConfig = SecureStorage.loadConfig();
|
|
11
|
+
}
|
|
12
|
+
getConfiguredServers() {
|
|
13
|
+
return this.persistentConfig.mcpServers || {};
|
|
14
|
+
}
|
|
15
|
+
isServerConfigured(serverName) {
|
|
16
|
+
return !!this.persistentConfig.mcpServers?.[serverName];
|
|
17
|
+
}
|
|
18
|
+
addServerFromJSON(jsonConfig) {
|
|
19
|
+
try {
|
|
20
|
+
const parsedConfig = JSON.parse(jsonConfig);
|
|
21
|
+
// Validate JSON structure
|
|
22
|
+
if (!parsedConfig.mcpServers ||
|
|
23
|
+
typeof parsedConfig.mcpServers !== 'object') {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
message: 'Invalid JSON format. Expected format:\n{\n "mcpServers": {\n "servername": {\n "command": "...",\n "args": [...]\n }\n }\n}',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const servers = parsedConfig.mcpServers;
|
|
30
|
+
const serverNames = Object.keys(servers);
|
|
31
|
+
if (serverNames.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
message: 'No servers found in JSON configuration.',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Check for conflicts with existing servers
|
|
38
|
+
const existingServers = this.persistentConfig.mcpServers || {};
|
|
39
|
+
const conflicts = serverNames.filter(name => existingServers[name]);
|
|
40
|
+
if (conflicts.length > 0) {
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
message: `Server(s) already exist: ${conflicts.join(', ')}. Please use different names.`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Validate each server config
|
|
47
|
+
for (const [name, serverConfig] of Object.entries(servers)) {
|
|
48
|
+
const server = serverConfig;
|
|
49
|
+
if (!server.command || typeof server.command !== 'string') {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
message: `Server "${name}" missing required "command" field.`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// All validation passed, save the servers
|
|
57
|
+
if (!this.persistentConfig.mcpServers) {
|
|
58
|
+
this.persistentConfig.mcpServers = {};
|
|
59
|
+
}
|
|
60
|
+
// Add all servers from JSON
|
|
61
|
+
Object.assign(this.persistentConfig.mcpServers, servers);
|
|
62
|
+
SecureStorage.saveConfig(this.persistentConfig);
|
|
63
|
+
const serverList = serverNames.map(name => `⢠${name}`).join('\n');
|
|
64
|
+
return {
|
|
65
|
+
success: true,
|
|
66
|
+
message: `Configured ${serverNames.length} server(s)!\n\n${serverList}`,
|
|
67
|
+
data: {
|
|
68
|
+
serversAdded: true,
|
|
69
|
+
serverNames,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
message: `Invalid JSON format: ${error instanceof Error ? error.message : 'Parse error'}\n\nPlease check your JSON syntax and try again.`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
addServer(name, config) {
|
|
81
|
+
// Check if server name already exists
|
|
82
|
+
if (this.persistentConfig.mcpServers?.[name]) {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
message: `Server "${name}" already exists. Use a different name.`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Add server to persistent configuration
|
|
89
|
+
if (!this.persistentConfig.mcpServers) {
|
|
90
|
+
this.persistentConfig.mcpServers = {};
|
|
91
|
+
}
|
|
92
|
+
this.persistentConfig.mcpServers[name] = config;
|
|
93
|
+
// Save configuration
|
|
94
|
+
SecureStorage.saveConfig(this.persistentConfig);
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
message: `Server "${name}" configured!`,
|
|
98
|
+
data: {
|
|
99
|
+
serverAdded: true,
|
|
100
|
+
serverName: name,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Gets the configuration for a specific server.
|
|
106
|
+
* @param serverName - Name of the server
|
|
107
|
+
* @returns The server configuration or null if not found
|
|
108
|
+
*/
|
|
109
|
+
getServerConfig(serverName) {
|
|
110
|
+
return this.persistentConfig.mcpServers?.[serverName] || null;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Gets all configured servers with their configurations.
|
|
114
|
+
* Note: Connection status should be determined by the caller using AgentService.
|
|
115
|
+
* @returns Array of server configurations
|
|
116
|
+
*/
|
|
117
|
+
getAllServers() {
|
|
118
|
+
const persistentServers = this.persistentConfig.mcpServers || {};
|
|
119
|
+
const servers = [];
|
|
120
|
+
for (const [name, config] of Object.entries(persistentServers)) {
|
|
121
|
+
servers.push({
|
|
122
|
+
name,
|
|
123
|
+
config,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return servers;
|
|
127
|
+
}
|
|
128
|
+
getServerTestCommand(serverName) {
|
|
129
|
+
const serverConfig = this.persistentConfig.mcpServers?.[serverName];
|
|
130
|
+
if (!serverConfig) {
|
|
131
|
+
const configuredServers = Object.keys(this.persistentConfig.mcpServers || {});
|
|
132
|
+
return {
|
|
133
|
+
success: false,
|
|
134
|
+
message: `Server "${serverName}" is not configured.\n\nConfigured servers: ${configuredServers.length > 0 ? configuredServers.join(', ') : 'none'}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const command = serverConfig.command;
|
|
138
|
+
const args_str = serverConfig.args ? serverConfig.args.join(' ') : '';
|
|
139
|
+
const full_command = `${command} ${args_str}`.trim();
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
command: full_command,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
validateServerName(name) {
|
|
146
|
+
if (!name || !name.trim()) {
|
|
147
|
+
return { valid: false, message: 'Server name cannot be empty.' };
|
|
148
|
+
}
|
|
149
|
+
if (this.persistentConfig.mcpServers?.[name.trim()]) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
message: `Server "${name.trim()}" already exists. Use a different name.`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { valid: true };
|
|
156
|
+
}
|
|
157
|
+
parseEnvironmentVariables(envString) {
|
|
158
|
+
const env = {};
|
|
159
|
+
if (envString.trim()) {
|
|
160
|
+
const envLines = envString.trim().split('\n');
|
|
161
|
+
for (const line of envLines) {
|
|
162
|
+
const [key, ...valueParts] = line.split('=');
|
|
163
|
+
if (key && valueParts.length > 0) {
|
|
164
|
+
env[key.trim()] = valueParts.join('=').trim();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return env;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Handles the /server command and its subcommands.
|
|
172
|
+
* @param args - Array of arguments where args[0] is the subcommand
|
|
173
|
+
* @returns A CommandResult with the appropriate response
|
|
174
|
+
*/
|
|
175
|
+
handleServerCommand(args) {
|
|
176
|
+
if (args.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
type: 'info',
|
|
179
|
+
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.',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (args[0] === 'add') {
|
|
183
|
+
return {
|
|
184
|
+
type: 'prompt_server_config',
|
|
185
|
+
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:',
|
|
186
|
+
data: { step: 'name_or_json' },
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (args[0] === 'connect') {
|
|
190
|
+
if (args.length < 2) {
|
|
191
|
+
const configuredServers = Object.keys(this.getConfiguredServers());
|
|
192
|
+
if (configuredServers.length === 0) {
|
|
193
|
+
return {
|
|
194
|
+
type: 'error',
|
|
195
|
+
message: 'No servers configured. Use /server add to configure servers first.\n\nUsage: /server connect <server_name>',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
type: 'error',
|
|
200
|
+
message: `Usage: /server connect <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const serverName = args[1];
|
|
204
|
+
if (!serverName) {
|
|
205
|
+
return {
|
|
206
|
+
type: 'error',
|
|
207
|
+
message: 'Server name is required.\n\nUsage: /server connect <server_name>',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
type: 'info',
|
|
212
|
+
message: `Server connect command received for: ${serverName}`,
|
|
213
|
+
data: { connectServer: true, serverName },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (args[0] === 'disconnect') {
|
|
217
|
+
if (args.length < 2) {
|
|
218
|
+
return {
|
|
219
|
+
type: 'error',
|
|
220
|
+
message: 'Usage: /server disconnect <server_name>',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const serverName = args[1];
|
|
224
|
+
if (!serverName) {
|
|
225
|
+
return {
|
|
226
|
+
type: 'error',
|
|
227
|
+
message: 'Server name is required.\n\nUsage: /server disconnect <server_name>',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
type: 'info',
|
|
232
|
+
message: `Server disconnect command received for: ${serverName}`,
|
|
233
|
+
data: { disconnectServer: true, serverName },
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
type: 'error',
|
|
238
|
+
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',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Handles the /servers command to list all servers and their connection status.
|
|
243
|
+
* @returns A CommandResult with the server list
|
|
244
|
+
*/
|
|
245
|
+
handleListServersCommand() {
|
|
246
|
+
const servers = this.getAllServers();
|
|
247
|
+
if (servers.length === 0) {
|
|
248
|
+
return {
|
|
249
|
+
type: 'info',
|
|
250
|
+
message: 'No custom servers configured.\n\nUse /server add to configure servers, then /server connect <name> to connect.',
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
type: 'list_servers',
|
|
255
|
+
message: 'MCP Server Status:',
|
|
256
|
+
data: { servers },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Handles the /test-server command to test server configuration.
|
|
261
|
+
* @param args - Array where args[0] is the server name
|
|
262
|
+
* @returns A CommandResult with test information
|
|
263
|
+
*/
|
|
264
|
+
handleTestServerCommand(args) {
|
|
265
|
+
if (args.length === 0) {
|
|
266
|
+
const configuredServers = Object.keys(this.getConfiguredServers());
|
|
267
|
+
if (configuredServers.length === 0) {
|
|
268
|
+
return {
|
|
269
|
+
type: 'error',
|
|
270
|
+
message: 'No servers configured to test.\n\nUsage: /test-server <server_name>\n\nUse /server add to configure servers first.',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
type: 'info',
|
|
275
|
+
message: `Usage: /test-server <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}\n\nThis command will test if the server package can be started manually.`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const serverName = args[0];
|
|
279
|
+
if (!serverName) {
|
|
280
|
+
return {
|
|
281
|
+
type: 'error',
|
|
282
|
+
message: 'Server name is required.\n\nUsage: /test-server <server_name>',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const result = this.getServerTestCommand(serverName);
|
|
286
|
+
if (!result.success) {
|
|
287
|
+
return {
|
|
288
|
+
type: 'error',
|
|
289
|
+
message: result.message,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
type: 'info',
|
|
294
|
+
message: `š§Ŗ Testing server "${serverName}"...\n\nCommand: ${result.command}\n\nā ļø Note: This will attempt to run the server command manually.\nCheck the console for output and errors.\n\nš” Try running this command manually in your terminal:\n${result.command}`,
|
|
295
|
+
data: { testServer: true, serverName, command: result.command },
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Handles server configuration input during the interactive setup flow.
|
|
300
|
+
* @param input - User input string
|
|
301
|
+
* @param step - Current step in the configuration flow
|
|
302
|
+
* @param serverConfig - Partial server configuration being built
|
|
303
|
+
* @returns A CommandResult to continue or complete the flow
|
|
304
|
+
*/
|
|
305
|
+
handleServerConfigInput(input, step, serverConfig) {
|
|
306
|
+
const config = serverConfig || {};
|
|
307
|
+
switch (step) {
|
|
308
|
+
case 'name_or_json':
|
|
309
|
+
// Check if input looks like JSON
|
|
310
|
+
const trimmedInput = input.trim();
|
|
311
|
+
if (trimmedInput.startsWith('{') &&
|
|
312
|
+
trimmedInput.includes('mcpServers')) {
|
|
313
|
+
const result = this.addServerFromJSON(trimmedInput);
|
|
314
|
+
if (!result.success) {
|
|
315
|
+
return {
|
|
316
|
+
type: 'error',
|
|
317
|
+
message: result.message,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
type: 'success',
|
|
322
|
+
message: `${result.message}.`,
|
|
323
|
+
data: result.data,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
// Not JSON, treat as server name for interactive setup
|
|
327
|
+
const validation = this.validateServerName(trimmedInput);
|
|
328
|
+
if (!validation.valid) {
|
|
329
|
+
return {
|
|
330
|
+
type: 'error',
|
|
331
|
+
message: validation.message,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
config.name = trimmedInput;
|
|
335
|
+
return {
|
|
336
|
+
type: 'prompt_server_config',
|
|
337
|
+
message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
|
|
338
|
+
data: { step: 'command', config },
|
|
339
|
+
};
|
|
340
|
+
case 'name':
|
|
341
|
+
const nameValidation = this.validateServerName(input.trim());
|
|
342
|
+
if (!nameValidation.valid) {
|
|
343
|
+
return {
|
|
344
|
+
type: 'error',
|
|
345
|
+
message: nameValidation.message,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
config.name = input.trim();
|
|
349
|
+
return {
|
|
350
|
+
type: 'prompt_server_config',
|
|
351
|
+
message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
|
|
352
|
+
data: { step: 'command', config },
|
|
353
|
+
};
|
|
354
|
+
case 'command':
|
|
355
|
+
if (!input.trim()) {
|
|
356
|
+
return {
|
|
357
|
+
type: 'error',
|
|
358
|
+
message: 'Command cannot be empty.',
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
config.command = input.trim();
|
|
362
|
+
return {
|
|
363
|
+
type: 'prompt_server_config',
|
|
364
|
+
message: `Server name: ${config.name}\nCommand: ${config.command}\n\nEnter arguments (space-separated, or press Enter for none):\nExample: "-y @modelcontextprotocol/server-filesystem /tmp"`,
|
|
365
|
+
data: { step: 'args', config },
|
|
366
|
+
};
|
|
367
|
+
case 'args':
|
|
368
|
+
config.args = input.trim() ? input.trim().split(/\s+/) : [];
|
|
369
|
+
return {
|
|
370
|
+
type: 'prompt_server_config',
|
|
371
|
+
message: `Server name: ${config.name}\nCommand: ${config.command}\nArgs: ${config.args.length > 0 ? config.args.join(' ') : 'none'}\n\nEnter environment variables (KEY=VALUE format, one per line, or press Enter for none):\nExample: "DEBUG=1" or press Enter to skip:`,
|
|
372
|
+
data: { step: 'env', config },
|
|
373
|
+
};
|
|
374
|
+
case 'env':
|
|
375
|
+
config.env = this.parseEnvironmentVariables(input);
|
|
376
|
+
return {
|
|
377
|
+
type: 'prompt_server_config',
|
|
378
|
+
message: `Server Configuration Summary:\n\nName: ${config.name}\nCommand: ${config.command}\nArgs: ${config.args.length > 0 ? config.args.join(' ') : 'none'}\nEnv: ${Object.keys(config.env).length > 0
|
|
379
|
+
? Object.entries(config.env)
|
|
380
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
381
|
+
.join(', ')
|
|
382
|
+
: 'none'}\n\nConfirm to add this server? (y/n):`,
|
|
383
|
+
data: { step: 'confirm', config },
|
|
384
|
+
};
|
|
385
|
+
case 'confirm':
|
|
386
|
+
if (input.trim().toLowerCase() === 'y' ||
|
|
387
|
+
input.trim().toLowerCase() === 'yes') {
|
|
388
|
+
const serverConfig = {
|
|
389
|
+
command: config.command,
|
|
390
|
+
args: config.args,
|
|
391
|
+
env: config.env,
|
|
392
|
+
};
|
|
393
|
+
const result = this.addServer(config.name, serverConfig);
|
|
394
|
+
if (!result.success) {
|
|
395
|
+
return {
|
|
396
|
+
type: 'error',
|
|
397
|
+
message: result.message,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
type: 'success',
|
|
402
|
+
message: `${result.message}.`,
|
|
403
|
+
data: result.data,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
else if (input.trim().toLowerCase() === 'n' ||
|
|
407
|
+
input.trim().toLowerCase() === 'no') {
|
|
408
|
+
return {
|
|
409
|
+
type: 'info',
|
|
410
|
+
message: 'Server configuration cancelled.',
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
return {
|
|
415
|
+
type: 'error',
|
|
416
|
+
message: 'Please enter "y" for yes or "n" for no.',
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
default:
|
|
420
|
+
return {
|
|
421
|
+
type: 'error',
|
|
422
|
+
message: 'Invalid server configuration step.',
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CommandResult } from '../types.js';
|
|
2
|
+
import type { LLMService } from './llm-service.js';
|
|
3
|
+
import type { MCPConfigService } from './mcp-config-service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Service that handles utility commands like help, logs, history, and status.
|
|
6
|
+
* These commands provide information and debugging capabilities but don't
|
|
7
|
+
* modify the core application state.
|
|
8
|
+
*/
|
|
9
|
+
export declare class UtilityService {
|
|
10
|
+
private llmService;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new UtilityService instance.
|
|
13
|
+
* @param deps - Dependencies required by the service
|
|
14
|
+
* @param deps.llmService - Service for LLM operations (used for status)
|
|
15
|
+
* @param deps.mcpConfigService - Service for MCP config (used for status)
|
|
16
|
+
*/
|
|
17
|
+
constructor(deps: {
|
|
18
|
+
llmService: LLMService;
|
|
19
|
+
mcpConfigService: MCPConfigService;
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Handles the /help command to show available commands.
|
|
23
|
+
* @returns A CommandResult with the help text
|
|
24
|
+
*/
|
|
25
|
+
handleHelpCommand(): CommandResult;
|
|
26
|
+
/**
|
|
27
|
+
* Handles the /status command to show current configuration.
|
|
28
|
+
* @returns A CommandResult with the current status
|
|
29
|
+
*/
|
|
30
|
+
handleStatusCommand(): CommandResult;
|
|
31
|
+
/**
|
|
32
|
+
* Handles the /logs command and its subcommands.
|
|
33
|
+
* @param args - Array of arguments where args[0] is the subcommand
|
|
34
|
+
* @returns A CommandResult with log information
|
|
35
|
+
*/
|
|
36
|
+
handleLogsCommand(args: string[]): CommandResult;
|
|
37
|
+
/**
|
|
38
|
+
* Handles the /clearlogs command to clear debug logs.
|
|
39
|
+
* @returns A CommandResult indicating success or failure
|
|
40
|
+
*/
|
|
41
|
+
handleClearLogsCommand(): CommandResult;
|
|
42
|
+
/**
|
|
43
|
+
* Handles the /history command to show input history navigation info.
|
|
44
|
+
* @returns A CommandResult with history navigation instructions
|
|
45
|
+
*/
|
|
46
|
+
handleHistoryCommand(): CommandResult;
|
|
47
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Logger } from '../logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Service that handles utility commands like help, logs, history, and status.
|
|
4
|
+
* These commands provide information and debugging capabilities but don't
|
|
5
|
+
* modify the core application state.
|
|
6
|
+
*/
|
|
7
|
+
export class UtilityService {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new UtilityService instance.
|
|
10
|
+
* @param deps - Dependencies required by the service
|
|
11
|
+
* @param deps.llmService - Service for LLM operations (used for status)
|
|
12
|
+
* @param deps.mcpConfigService - Service for MCP config (used for status)
|
|
13
|
+
*/
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
Object.defineProperty(this, "llmService", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
this.llmService = deps.llmService;
|
|
22
|
+
// mcpConfigService is passed but not used currently
|
|
23
|
+
// Kept in constructor signature for potential future use and API consistency
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Handles the /help command to show available commands.
|
|
27
|
+
* @returns A CommandResult with the help text
|
|
28
|
+
*/
|
|
29
|
+
handleHelpCommand() {
|
|
30
|
+
const helpText = `
|
|
31
|
+
Available slash commands:
|
|
32
|
+
|
|
33
|
+
š¤ Get Started:
|
|
34
|
+
/model <provider> <model> - Choose your LLM (CLI handles API key setup)
|
|
35
|
+
/models [provider] - List available models for a provider
|
|
36
|
+
|
|
37
|
+
š MCP Servers:
|
|
38
|
+
/server add - Configure a new server (auto-connects)
|
|
39
|
+
/server connect <name> - Connect to a configured server by name
|
|
40
|
+
/server disconnect <name> - Disconnect from a connected server
|
|
41
|
+
/servers - List servers and their connection status
|
|
42
|
+
/tools - Show available tools from connected servers
|
|
43
|
+
/test-server <name> - Test if a server package can be started
|
|
44
|
+
|
|
45
|
+
š API Keys (automatic):
|
|
46
|
+
/setkey <provider> <key> - Set API key manually (stored securely)
|
|
47
|
+
/clearkeys - Clear all stored API keys
|
|
48
|
+
|
|
49
|
+
āļø Configuration:
|
|
50
|
+
/config temp <value> - Set temperature (0.0-2.0)
|
|
51
|
+
/config tokens <value> - Set max tokens
|
|
52
|
+
/help - Show this help
|
|
53
|
+
|
|
54
|
+
š ļø Debugging & History:
|
|
55
|
+
/logs [path|tail] - View debug logs (written to ~/.mcp-use-cli/debug.log)
|
|
56
|
+
/clearlogs - Clear debug logs
|
|
57
|
+
/history - Info about input history navigation (āā arrows)
|
|
58
|
+
|
|
59
|
+
š Quick Start Examples:
|
|
60
|
+
/model openai gpt-4o-mini
|
|
61
|
+
/server add # Interactive server setup
|
|
62
|
+
/servers
|
|
63
|
+
/config temp 0.5
|
|
64
|
+
`.trim();
|
|
65
|
+
return {
|
|
66
|
+
type: 'info',
|
|
67
|
+
message: helpText,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Handles the /status command to show current configuration.
|
|
72
|
+
* @returns A CommandResult with the current status
|
|
73
|
+
*/
|
|
74
|
+
handleStatusCommand() {
|
|
75
|
+
const availableProviders = this.llmService.getAvailableProviders();
|
|
76
|
+
const currentConfig = this.llmService.getCurrentConfig();
|
|
77
|
+
const apiKeyStatus = this.llmService.getApiKeyStatus();
|
|
78
|
+
let statusText = 'š¤ Current Configuration:\n\n';
|
|
79
|
+
// API Keys status
|
|
80
|
+
statusText += 'š API Keys:\n';
|
|
81
|
+
Object.entries(apiKeyStatus).forEach(([provider, status]) => {
|
|
82
|
+
if (status.status === 'set') {
|
|
83
|
+
statusText += ` ⢠${provider}: ${status.masked} (${status.source})\n`;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
statusText += ` ⢠${provider}: ā not set\n`;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
statusText += '\n';
|
|
90
|
+
// Current model
|
|
91
|
+
if (!currentConfig) {
|
|
92
|
+
if (availableProviders.length === 0) {
|
|
93
|
+
statusText += 'ā ļø No model selected\n';
|
|
94
|
+
statusText += '\nChoose a model to get started:\n';
|
|
95
|
+
statusText += '⢠/model openai gpt-4o-mini\n';
|
|
96
|
+
statusText += '⢠/model anthropic claude-3-5-sonnet-20241022\n';
|
|
97
|
+
statusText += '⢠/model google gemini-1.5-pro\n';
|
|
98
|
+
statusText += '\nThe CLI will help you set up API keys when needed.';
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
statusText += 'ā ļø No model selected\n';
|
|
102
|
+
statusText += `\nAvailable providers: ${availableProviders.join(', ')}\n`;
|
|
103
|
+
statusText += 'Use /model <provider> <model> to get started';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
statusText += `šÆ Active Model:\n`;
|
|
108
|
+
statusText += ` Provider: ${currentConfig.provider}\n`;
|
|
109
|
+
statusText += ` Model: ${currentConfig.model}\n`;
|
|
110
|
+
statusText += ` Temperature: ${currentConfig.temperature || 0.7}\n`;
|
|
111
|
+
statusText += ` Max Tokens: ${currentConfig.maxTokens || 'default'}\n`;
|
|
112
|
+
statusText +=
|
|
113
|
+
'\nUse /model to switch models or /config to adjust settings';
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
type: 'info',
|
|
117
|
+
message: statusText,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Handles the /logs command and its subcommands.
|
|
122
|
+
* @param args - Array of arguments where args[0] is the subcommand
|
|
123
|
+
* @returns A CommandResult with log information
|
|
124
|
+
*/
|
|
125
|
+
handleLogsCommand(args) {
|
|
126
|
+
const logPath = Logger.getLogPath();
|
|
127
|
+
if (args.length === 0) {
|
|
128
|
+
return {
|
|
129
|
+
type: 'info',
|
|
130
|
+
message: `š Debug logs are written to:\n${logPath}\n\nCommands:\n /logs path - Show log file path\n /logs tail - Show recent log entries\n /clearlogs - Clear all logs\n\nTo view logs in real-time:\n tail -f ${logPath}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const subcommand = args[0];
|
|
134
|
+
switch (subcommand) {
|
|
135
|
+
case 'path':
|
|
136
|
+
return {
|
|
137
|
+
type: 'info',
|
|
138
|
+
message: `š Log file location:\n${logPath}`,
|
|
139
|
+
};
|
|
140
|
+
case 'tail':
|
|
141
|
+
try {
|
|
142
|
+
const fs = require('fs');
|
|
143
|
+
if (!fs.existsSync(logPath)) {
|
|
144
|
+
return {
|
|
145
|
+
type: 'info',
|
|
146
|
+
message: 'š No log file found yet. Logs will be created when the app starts logging.',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const logContent = fs.readFileSync(logPath, 'utf8');
|
|
150
|
+
const lines = logContent
|
|
151
|
+
.split('\n')
|
|
152
|
+
.filter((line) => line.trim());
|
|
153
|
+
const recentLines = lines.slice(-20); // Show last 20 lines
|
|
154
|
+
if (recentLines.length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
type: 'info',
|
|
157
|
+
message: 'š Log file is empty.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
type: 'info',
|
|
162
|
+
message: `š Recent log entries (last ${recentLines.length} lines):\n\n${recentLines.join('\n')}`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return {
|
|
167
|
+
type: 'error',
|
|
168
|
+
message: `ā Failed to read logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
default:
|
|
172
|
+
return {
|
|
173
|
+
type: 'error',
|
|
174
|
+
message: `Unknown logs subcommand: ${subcommand}. Use /logs for help.`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Handles the /clearlogs command to clear debug logs.
|
|
180
|
+
* @returns A CommandResult indicating success or failure
|
|
181
|
+
*/
|
|
182
|
+
handleClearLogsCommand() {
|
|
183
|
+
try {
|
|
184
|
+
Logger.clearLogs();
|
|
185
|
+
Logger.info('Logs cleared by user command');
|
|
186
|
+
return {
|
|
187
|
+
type: 'success',
|
|
188
|
+
message: 'ā
Debug logs cleared successfully.',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return {
|
|
193
|
+
type: 'error',
|
|
194
|
+
message: `ā Failed to clear logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Handles the /history command to show input history navigation info.
|
|
200
|
+
* @returns A CommandResult with history navigation instructions
|
|
201
|
+
*/
|
|
202
|
+
handleHistoryCommand() {
|
|
203
|
+
return {
|
|
204
|
+
type: 'info',
|
|
205
|
+
message: `š Input History Navigation:\n\nš¼ Arrow Up - Navigate to previous inputs\nš½ Arrow Down - Navigate to newer inputs\n\nš” Tips:\n⢠Your input history is automatically saved during the session\n⢠Use ā to recall previous commands and messages\n⢠Use ā to navigate back to newer inputs\n⢠History is reset when you restart the CLI\n\nšÆ Try it now: Press the up arrow key in the input box!`,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|