@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
package/dist/commands.js
CHANGED
|
@@ -1,129 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ChatAnthropic } from '@langchain/anthropic';
|
|
3
|
-
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
|
|
4
|
-
import { ChatMistralAI } from '@langchain/mistralai';
|
|
5
|
-
import { SecureStorage } from './storage.js';
|
|
1
|
+
import { Logger } from './logger.js';
|
|
6
2
|
export class CommandHandler {
|
|
7
|
-
constructor() {
|
|
8
|
-
Object.defineProperty(this, "
|
|
9
|
-
enumerable: true,
|
|
10
|
-
configurable: true,
|
|
11
|
-
writable: true,
|
|
12
|
-
value: null
|
|
13
|
-
});
|
|
14
|
-
Object.defineProperty(this, "sessionApiKeys", {
|
|
15
|
-
enumerable: true,
|
|
16
|
-
configurable: true,
|
|
17
|
-
writable: true,
|
|
18
|
-
value: {}
|
|
19
|
-
});
|
|
20
|
-
Object.defineProperty(this, "persistentConfig", {
|
|
3
|
+
constructor(deps) {
|
|
4
|
+
Object.defineProperty(this, "llmService", {
|
|
21
5
|
enumerable: true,
|
|
22
6
|
configurable: true,
|
|
23
7
|
writable: true,
|
|
24
8
|
value: void 0
|
|
25
9
|
});
|
|
26
|
-
Object.defineProperty(this, "
|
|
27
|
-
enumerable: true,
|
|
28
|
-
configurable: true,
|
|
29
|
-
writable: true,
|
|
30
|
-
value: {}
|
|
31
|
-
});
|
|
32
|
-
Object.defineProperty(this, "availableModels", {
|
|
10
|
+
Object.defineProperty(this, "mcpService", {
|
|
33
11
|
enumerable: true,
|
|
34
12
|
configurable: true,
|
|
35
13
|
writable: true,
|
|
36
|
-
value:
|
|
37
|
-
openai: [
|
|
38
|
-
'gpt-4o',
|
|
39
|
-
'gpt-4o-mini',
|
|
40
|
-
'gpt-4-turbo',
|
|
41
|
-
'gpt-4',
|
|
42
|
-
'gpt-3.5-turbo'
|
|
43
|
-
],
|
|
44
|
-
anthropic: [
|
|
45
|
-
'claude-3-5-sonnet-20241022',
|
|
46
|
-
'claude-3-5-haiku-20241022',
|
|
47
|
-
'claude-3-opus-20240229',
|
|
48
|
-
'claude-3-sonnet-20240229',
|
|
49
|
-
'claude-3-haiku-20240307'
|
|
50
|
-
],
|
|
51
|
-
google: [
|
|
52
|
-
'gemini-1.5-pro',
|
|
53
|
-
'gemini-1.5-flash',
|
|
54
|
-
'gemini-pro'
|
|
55
|
-
],
|
|
56
|
-
mistral: [
|
|
57
|
-
'mistral-large-latest',
|
|
58
|
-
'mistral-medium-latest',
|
|
59
|
-
'mistral-small-latest'
|
|
60
|
-
]
|
|
61
|
-
}
|
|
14
|
+
value: void 0
|
|
62
15
|
});
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
// Auto-detect available provider and set default
|
|
66
|
-
this.initializeDefaultProvider();
|
|
67
|
-
}
|
|
68
|
-
initializeDefaultProvider() {
|
|
69
|
-
// First, try to load the last used model from persistent config
|
|
70
|
-
if (this.persistentConfig.lastModel) {
|
|
71
|
-
const lastModel = this.persistentConfig.lastModel;
|
|
72
|
-
const envVar = {
|
|
73
|
-
openai: 'OPENAI_API_KEY',
|
|
74
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
75
|
-
google: 'GOOGLE_API_KEY',
|
|
76
|
-
mistral: 'MISTRAL_API_KEY'
|
|
77
|
-
}[lastModel.provider];
|
|
78
|
-
// Check if we still have the API key for the last used model
|
|
79
|
-
if (envVar && this.getApiKey(envVar)) {
|
|
80
|
-
this.currentLLMConfig = {
|
|
81
|
-
provider: lastModel.provider,
|
|
82
|
-
model: lastModel.model,
|
|
83
|
-
temperature: lastModel.temperature || 0.7,
|
|
84
|
-
maxTokens: lastModel.maxTokens
|
|
85
|
-
};
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const providers = [
|
|
90
|
-
{ name: 'openai', envVar: 'OPENAI_API_KEY', defaultModel: 'gpt-4o-mini' },
|
|
91
|
-
{ name: 'anthropic', envVar: 'ANTHROPIC_API_KEY', defaultModel: 'claude-3-5-sonnet-20241022' },
|
|
92
|
-
{ name: 'google', envVar: 'GOOGLE_API_KEY', defaultModel: 'gemini-1.5-pro' },
|
|
93
|
-
{ name: 'mistral', envVar: 'MISTRAL_API_KEY', defaultModel: 'mistral-large-latest' }
|
|
94
|
-
];
|
|
95
|
-
// Find first available provider
|
|
96
|
-
for (const provider of providers) {
|
|
97
|
-
if (this.getApiKey(provider.envVar)) {
|
|
98
|
-
this.currentLLMConfig = {
|
|
99
|
-
provider: provider.name,
|
|
100
|
-
model: provider.defaultModel,
|
|
101
|
-
temperature: 0.7
|
|
102
|
-
};
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// No provider found - leave null, will show setup message
|
|
107
|
-
this.currentLLMConfig = null;
|
|
108
|
-
}
|
|
109
|
-
getAvailableProviders() {
|
|
110
|
-
const providers = [];
|
|
111
|
-
if (this.getApiKey('OPENAI_API_KEY'))
|
|
112
|
-
providers.push('openai');
|
|
113
|
-
if (this.getApiKey('ANTHROPIC_API_KEY'))
|
|
114
|
-
providers.push('anthropic');
|
|
115
|
-
if (this.getApiKey('GOOGLE_API_KEY'))
|
|
116
|
-
providers.push('google');
|
|
117
|
-
if (this.getApiKey('MISTRAL_API_KEY'))
|
|
118
|
-
providers.push('mistral');
|
|
119
|
-
return providers;
|
|
120
|
-
}
|
|
121
|
-
getApiKey(envVar) {
|
|
122
|
-
// Check session keys first, then persistent storage, then environment variables
|
|
123
|
-
return this.sessionApiKeys[envVar] || this.persistentConfig.apiKeys[envVar] || process.env[envVar];
|
|
124
|
-
}
|
|
125
|
-
isAnyProviderAvailable() {
|
|
126
|
-
return this.getAvailableProviders().length > 0;
|
|
16
|
+
this.llmService = deps.llmService;
|
|
17
|
+
this.mcpService = deps.mcpService;
|
|
127
18
|
}
|
|
128
19
|
async handleCommand(input) {
|
|
129
20
|
const parts = input.trim().split(/\s+/);
|
|
@@ -152,10 +43,16 @@ export class CommandHandler {
|
|
|
152
43
|
return this.handleListTools();
|
|
153
44
|
case '/test-server':
|
|
154
45
|
return this.handleTestServer(args);
|
|
46
|
+
case '/logs':
|
|
47
|
+
return this.handleLogs(args);
|
|
48
|
+
case '/clearlogs':
|
|
49
|
+
return this.handleClearLogs();
|
|
50
|
+
case '/history':
|
|
51
|
+
return this.handleHistory();
|
|
155
52
|
default:
|
|
156
53
|
return {
|
|
157
54
|
type: 'error',
|
|
158
|
-
message: `Unknown command: ${command}. Type /help for available commands
|
|
55
|
+
message: `Unknown command: ${command}. Type /help for available commands.`,
|
|
159
56
|
};
|
|
160
57
|
}
|
|
161
58
|
}
|
|
@@ -185,6 +82,11 @@ Available slash commands:
|
|
|
185
82
|
/config tokens <value> - Set max tokens
|
|
186
83
|
/help - Show this help
|
|
187
84
|
|
|
85
|
+
š ļø Debugging & History:
|
|
86
|
+
/logs [path|tail] - View debug logs (written to ~/.mcp-use-cli/debug.log)
|
|
87
|
+
/clearlogs - Clear debug logs
|
|
88
|
+
/history - Info about input history navigation (āā arrows)
|
|
89
|
+
|
|
188
90
|
š Quick Start Examples:
|
|
189
91
|
/model openai gpt-4o-mini
|
|
190
92
|
/server add # Interactive server setup
|
|
@@ -193,142 +95,123 @@ Available slash commands:
|
|
|
193
95
|
`.trim();
|
|
194
96
|
return {
|
|
195
97
|
type: 'info',
|
|
196
|
-
message: helpText
|
|
98
|
+
message: helpText,
|
|
197
99
|
};
|
|
198
100
|
}
|
|
199
101
|
handleModel(args) {
|
|
200
102
|
if (args.length < 2) {
|
|
201
|
-
const availableProviders = this.getAvailableProviders();
|
|
103
|
+
const availableProviders = this.llmService.getAvailableProviders();
|
|
202
104
|
if (availableProviders.length === 0) {
|
|
203
105
|
return {
|
|
204
106
|
type: 'info',
|
|
205
|
-
message: 'Usage: /model <provider> <model>\n\nPopular models to try:\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 prompt for your API key when needed.\nUse /models to see all available options.'
|
|
107
|
+
message: 'Usage: /model <provider> <model>\n\nPopular models to try:\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 prompt for your API key when needed.\nUse /models to see all available options.',
|
|
206
108
|
};
|
|
207
109
|
}
|
|
208
110
|
return {
|
|
209
111
|
type: 'error',
|
|
210
|
-
message: `Usage: /model <provider> <model>\nExample: /model openai gpt-4o\n\nAvailable providers: ${availableProviders.join(', ')}
|
|
112
|
+
message: `Usage: /model <provider> <model>\nExample: /model openai gpt-4o\n\nAvailable providers: ${availableProviders.join(', ')}`,
|
|
211
113
|
};
|
|
212
114
|
}
|
|
213
115
|
const provider = args[0];
|
|
214
116
|
const model = args[1];
|
|
215
|
-
if (!provider || !
|
|
117
|
+
if (!provider || !model) {
|
|
216
118
|
return {
|
|
217
119
|
type: 'error',
|
|
218
|
-
message:
|
|
120
|
+
message: 'Both provider and model are required',
|
|
219
121
|
};
|
|
220
122
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (!availableProviders.includes(provider)) {
|
|
224
|
-
const envVarMap = {
|
|
225
|
-
openai: 'OPENAI_API_KEY',
|
|
226
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
227
|
-
google: 'GOOGLE_API_KEY',
|
|
228
|
-
mistral: 'MISTRAL_API_KEY'
|
|
229
|
-
};
|
|
230
|
-
// Prompt for API key instead of showing error
|
|
123
|
+
const availableModels = this.llmService.getAvailableModels();
|
|
124
|
+
if (!availableModels[provider]) {
|
|
231
125
|
return {
|
|
232
|
-
type: '
|
|
233
|
-
message: `
|
|
234
|
-
data: {
|
|
235
|
-
provider,
|
|
236
|
-
model,
|
|
237
|
-
envVar: envVarMap[provider]
|
|
238
|
-
}
|
|
126
|
+
type: 'error',
|
|
127
|
+
message: `Unknown provider: ${provider}\nAvailable providers: ${Object.keys(availableModels).join(', ')}`,
|
|
239
128
|
};
|
|
240
129
|
}
|
|
241
|
-
|
|
130
|
+
// Try to set the model
|
|
131
|
+
const result = this.llmService.setModel(provider, model);
|
|
132
|
+
if (!result.success) {
|
|
133
|
+
if (result.requiresApiKey) {
|
|
134
|
+
// Prompt for API key instead of showing error
|
|
135
|
+
return {
|
|
136
|
+
type: 'prompt_key',
|
|
137
|
+
message: `Please enter your ${provider.toUpperCase()} API key:`,
|
|
138
|
+
data: {
|
|
139
|
+
provider,
|
|
140
|
+
model,
|
|
141
|
+
envVar: result.envVar,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
242
145
|
return {
|
|
243
146
|
type: 'error',
|
|
244
|
-
message:
|
|
147
|
+
message: result.message,
|
|
245
148
|
};
|
|
246
149
|
}
|
|
247
|
-
this.currentLLMConfig = {
|
|
248
|
-
provider,
|
|
249
|
-
model,
|
|
250
|
-
temperature: this.currentLLMConfig?.temperature || 0.7,
|
|
251
|
-
maxTokens: this.currentLLMConfig?.maxTokens
|
|
252
|
-
};
|
|
253
|
-
// Save the current model to persistent storage
|
|
254
|
-
this.persistentConfig.lastModel = this.currentLLMConfig;
|
|
255
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
256
150
|
return {
|
|
257
151
|
type: 'success',
|
|
258
|
-
message: `ā
|
|
259
|
-
data: { llmConfig: this.
|
|
152
|
+
message: `ā
${result.message}`,
|
|
153
|
+
data: { llmConfig: this.llmService.getCurrentConfig() },
|
|
260
154
|
};
|
|
261
155
|
}
|
|
262
156
|
handleListModels(args) {
|
|
157
|
+
const currentConfig = this.llmService.getCurrentConfig();
|
|
263
158
|
if (args.length === 0) {
|
|
264
159
|
let modelList = 'š Available models by provider:\n\n';
|
|
265
|
-
|
|
160
|
+
const availableModels = this.llmService.getAvailableModels();
|
|
161
|
+
Object.entries(availableModels).forEach(([provider, models]) => {
|
|
266
162
|
modelList += `šø ${provider}:\n`;
|
|
267
163
|
models.forEach(model => {
|
|
268
|
-
const current = provider ===
|
|
164
|
+
const current = provider === currentConfig?.provider &&
|
|
165
|
+
model === currentConfig?.model
|
|
166
|
+
? ' ā current'
|
|
167
|
+
: '';
|
|
269
168
|
modelList += ` ⢠${model}${current}\n`;
|
|
270
169
|
});
|
|
271
170
|
modelList += '\n';
|
|
272
171
|
});
|
|
273
172
|
return {
|
|
274
173
|
type: 'info',
|
|
275
|
-
message: modelList.trim()
|
|
174
|
+
message: modelList.trim(),
|
|
276
175
|
};
|
|
277
176
|
}
|
|
278
177
|
const provider = args[0];
|
|
279
|
-
if (!
|
|
178
|
+
if (!provider) {
|
|
280
179
|
return {
|
|
281
180
|
type: 'error',
|
|
282
|
-
message:
|
|
181
|
+
message: 'Provider is required',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const availableModels = this.llmService.getAvailableModels();
|
|
185
|
+
if (!availableModels[provider]) {
|
|
186
|
+
return {
|
|
187
|
+
type: 'error',
|
|
188
|
+
message: `Unknown provider: ${provider}\nAvailable providers: ${Object.keys(availableModels).join(', ')}`,
|
|
283
189
|
};
|
|
284
190
|
}
|
|
285
191
|
let modelList = `š Available ${provider} models:\n\n`;
|
|
286
|
-
this.
|
|
287
|
-
|
|
192
|
+
const models = this.llmService.getAvailableModels(provider);
|
|
193
|
+
models.forEach(model => {
|
|
194
|
+
const current = provider === currentConfig?.provider && model === currentConfig?.model
|
|
195
|
+
? ' ā current'
|
|
196
|
+
: '';
|
|
288
197
|
modelList += `⢠${model}${current}\n`;
|
|
289
198
|
});
|
|
199
|
+
modelList += `\n Don't see your model/provider? Submit a PR to add it at https://github.com/mcp-use/mcp-use-cli/`;
|
|
290
200
|
return {
|
|
291
201
|
type: 'info',
|
|
292
|
-
message: modelList.trim()
|
|
202
|
+
message: modelList.trim(),
|
|
293
203
|
};
|
|
294
204
|
}
|
|
295
205
|
handleStatus() {
|
|
296
|
-
const availableProviders = this.getAvailableProviders();
|
|
206
|
+
const availableProviders = this.llmService.getAvailableProviders();
|
|
207
|
+
const currentConfig = this.llmService.getCurrentConfig();
|
|
208
|
+
const apiKeyStatus = this.llmService.getApiKeyStatus();
|
|
297
209
|
let statusText = 'š¤ Current Configuration:\n\n';
|
|
298
210
|
// API Keys status
|
|
299
211
|
statusText += 'š API Keys:\n';
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
openai: 'OPENAI_API_KEY',
|
|
304
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
305
|
-
google: 'GOOGLE_API_KEY',
|
|
306
|
-
mistral: 'MISTRAL_API_KEY'
|
|
307
|
-
};
|
|
308
|
-
const envVar = envVarMap[provider];
|
|
309
|
-
const hasEnvKey = !!process.env[envVar];
|
|
310
|
-
const hasSessionKey = !!this.sessionApiKeys[envVar];
|
|
311
|
-
const hasPersistentKey = !!this.persistentConfig.apiKeys[envVar];
|
|
312
|
-
if (hasSessionKey) {
|
|
313
|
-
const key = this.sessionApiKeys[envVar];
|
|
314
|
-
if (key) {
|
|
315
|
-
const maskedKey = this.maskApiKey(key);
|
|
316
|
-
statusText += ` ⢠${provider}: ${maskedKey} (session)\n`;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else if (hasPersistentKey) {
|
|
320
|
-
const key = this.persistentConfig.apiKeys[envVar];
|
|
321
|
-
if (key) {
|
|
322
|
-
const maskedKey = this.maskApiKey(key);
|
|
323
|
-
statusText += ` ⢠${provider}: ${maskedKey} (stored)\n`;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
else if (hasEnvKey) {
|
|
327
|
-
const key = process.env[envVar];
|
|
328
|
-
if (key) {
|
|
329
|
-
const maskedKey = this.maskApiKey(key);
|
|
330
|
-
statusText += ` ⢠${provider}: ${maskedKey} (env)\n`;
|
|
331
|
-
}
|
|
212
|
+
Object.entries(apiKeyStatus).forEach(([provider, status]) => {
|
|
213
|
+
if (status.status === 'set') {
|
|
214
|
+
statusText += ` ⢠${provider}: ${status.masked} (${status.source})\n`;
|
|
332
215
|
}
|
|
333
216
|
else {
|
|
334
217
|
statusText += ` ⢠${provider}: ā not set\n`;
|
|
@@ -336,7 +219,7 @@ Available slash commands:
|
|
|
336
219
|
});
|
|
337
220
|
statusText += '\n';
|
|
338
221
|
// Current model
|
|
339
|
-
if (!
|
|
222
|
+
if (!currentConfig) {
|
|
340
223
|
if (availableProviders.length === 0) {
|
|
341
224
|
statusText += 'ā ļø No model selected\n';
|
|
342
225
|
statusText += '\nChoose a model to get started:\n';
|
|
@@ -352,24 +235,24 @@ Available slash commands:
|
|
|
352
235
|
}
|
|
353
236
|
}
|
|
354
237
|
else {
|
|
355
|
-
const config = this.currentLLMConfig;
|
|
356
238
|
statusText += `šÆ Active Model:\n`;
|
|
357
|
-
statusText += ` Provider: ${
|
|
358
|
-
statusText += ` Model: ${
|
|
359
|
-
statusText += ` Temperature: ${
|
|
360
|
-
statusText += ` Max Tokens: ${
|
|
361
|
-
statusText +=
|
|
239
|
+
statusText += ` Provider: ${currentConfig.provider}\n`;
|
|
240
|
+
statusText += ` Model: ${currentConfig.model}\n`;
|
|
241
|
+
statusText += ` Temperature: ${currentConfig.temperature || 0.7}\n`;
|
|
242
|
+
statusText += ` Max Tokens: ${currentConfig.maxTokens || 'default'}\n`;
|
|
243
|
+
statusText +=
|
|
244
|
+
'\nUse /model to switch models or /config to adjust settings';
|
|
362
245
|
}
|
|
363
246
|
return {
|
|
364
247
|
type: 'info',
|
|
365
|
-
message: statusText
|
|
248
|
+
message: statusText,
|
|
366
249
|
};
|
|
367
250
|
}
|
|
368
251
|
handleConfig(args) {
|
|
369
252
|
if (args.length < 2) {
|
|
370
253
|
return {
|
|
371
254
|
type: 'error',
|
|
372
|
-
message: 'Usage: /config <setting> <value>\nAvailable settings: temp, tokens'
|
|
255
|
+
message: 'Usage: /config <setting> <value>\nAvailable settings: temp, tokens',
|
|
373
256
|
};
|
|
374
257
|
}
|
|
375
258
|
const setting = args[0];
|
|
@@ -377,50 +260,62 @@ Available slash commands:
|
|
|
377
260
|
if (!value) {
|
|
378
261
|
return {
|
|
379
262
|
type: 'error',
|
|
380
|
-
message: 'Value is required'
|
|
263
|
+
message: 'Value is required',
|
|
381
264
|
};
|
|
382
265
|
}
|
|
383
|
-
if (!this.
|
|
266
|
+
if (!this.llmService.getCurrentConfig()) {
|
|
384
267
|
return {
|
|
385
268
|
type: 'error',
|
|
386
|
-
message: 'No model configured. Use /model to select a provider and model first.'
|
|
269
|
+
message: 'No model configured. Use /model to select a provider and model first.',
|
|
387
270
|
};
|
|
388
271
|
}
|
|
389
272
|
switch (setting) {
|
|
390
273
|
case 'temp':
|
|
391
274
|
case 'temperature':
|
|
392
275
|
const temp = parseFloat(value);
|
|
393
|
-
if (isNaN(temp)
|
|
276
|
+
if (isNaN(temp)) {
|
|
277
|
+
return {
|
|
278
|
+
type: 'error',
|
|
279
|
+
message: 'Temperature must be a number',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const tempResult = this.llmService.setTemperature(temp);
|
|
283
|
+
if (!tempResult.success) {
|
|
394
284
|
return {
|
|
395
285
|
type: 'error',
|
|
396
|
-
message:
|
|
286
|
+
message: tempResult.message,
|
|
397
287
|
};
|
|
398
288
|
}
|
|
399
|
-
this.currentLLMConfig.temperature = temp;
|
|
400
289
|
return {
|
|
401
290
|
type: 'success',
|
|
402
|
-
message: `ā
|
|
403
|
-
data: { llmConfig: this.
|
|
291
|
+
message: `ā
${tempResult.message}`,
|
|
292
|
+
data: { llmConfig: this.llmService.getCurrentConfig() },
|
|
404
293
|
};
|
|
405
294
|
case 'tokens':
|
|
406
295
|
case 'max-tokens':
|
|
407
296
|
const tokens = parseInt(value);
|
|
408
|
-
if (isNaN(tokens)
|
|
297
|
+
if (isNaN(tokens)) {
|
|
409
298
|
return {
|
|
410
299
|
type: 'error',
|
|
411
|
-
message: 'Max tokens must be a
|
|
300
|
+
message: 'Max tokens must be a number',
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const tokensResult = this.llmService.setMaxTokens(tokens);
|
|
304
|
+
if (!tokensResult.success) {
|
|
305
|
+
return {
|
|
306
|
+
type: 'error',
|
|
307
|
+
message: tokensResult.message,
|
|
412
308
|
};
|
|
413
309
|
}
|
|
414
|
-
this.currentLLMConfig.maxTokens = tokens;
|
|
415
310
|
return {
|
|
416
311
|
type: 'success',
|
|
417
|
-
message: `ā
|
|
418
|
-
data: { llmConfig: this.
|
|
312
|
+
message: `ā
${tokensResult.message}`,
|
|
313
|
+
data: { llmConfig: this.llmService.getCurrentConfig() },
|
|
419
314
|
};
|
|
420
315
|
default:
|
|
421
316
|
return {
|
|
422
317
|
type: 'error',
|
|
423
|
-
message: `Unknown setting: ${setting}\nAvailable settings: temp, tokens
|
|
318
|
+
message: `Unknown setting: ${setting}\nAvailable settings: temp, tokens`,
|
|
424
319
|
};
|
|
425
320
|
}
|
|
426
321
|
}
|
|
@@ -428,7 +323,7 @@ Available slash commands:
|
|
|
428
323
|
if (args.length < 2) {
|
|
429
324
|
return {
|
|
430
325
|
type: 'error',
|
|
431
|
-
message: 'Usage: /setkey <provider> <api_key>\n\nSupported providers: openai, anthropic, google, mistral\n\nExample:\n/setkey openai sk-1234567890abcdef...'
|
|
326
|
+
message: 'Usage: /setkey <provider> <api_key>\n\nSupported providers: openai, anthropic, google, mistral\n\nExample:\n/setkey openai sk-1234567890abcdef...',
|
|
432
327
|
};
|
|
433
328
|
}
|
|
434
329
|
const provider = args[0]?.toLowerCase();
|
|
@@ -436,7 +331,7 @@ Available slash commands:
|
|
|
436
331
|
if (!provider || !apiKey) {
|
|
437
332
|
return {
|
|
438
333
|
type: 'error',
|
|
439
|
-
message: 'Both provider and API key are required'
|
|
334
|
+
message: 'Both provider and API key are required',
|
|
440
335
|
};
|
|
441
336
|
}
|
|
442
337
|
// Validate provider
|
|
@@ -444,216 +339,97 @@ Available slash commands:
|
|
|
444
339
|
if (!validProviders.includes(provider)) {
|
|
445
340
|
return {
|
|
446
341
|
type: 'error',
|
|
447
|
-
message: `Invalid provider: ${provider}\nSupported providers: ${validProviders.join(', ')}
|
|
342
|
+
message: `Invalid provider: ${provider}\nSupported providers: ${validProviders.join(', ')}`,
|
|
448
343
|
};
|
|
449
344
|
}
|
|
450
|
-
//
|
|
451
|
-
const
|
|
452
|
-
|
|
345
|
+
// Check if we should auto-select this provider
|
|
346
|
+
const shouldAutoSelect = !this.llmService.getCurrentConfig();
|
|
347
|
+
// Set the API key
|
|
348
|
+
const result = this.llmService.setApiKey(provider, apiKey, shouldAutoSelect);
|
|
349
|
+
if (!result.success) {
|
|
453
350
|
return {
|
|
454
351
|
type: 'error',
|
|
455
|
-
message:
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
// Map provider to environment variable name
|
|
459
|
-
const envVarMap = {
|
|
460
|
-
openai: 'OPENAI_API_KEY',
|
|
461
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
462
|
-
google: 'GOOGLE_API_KEY',
|
|
463
|
-
mistral: 'MISTRAL_API_KEY'
|
|
464
|
-
};
|
|
465
|
-
const envVar = envVarMap[provider];
|
|
466
|
-
// Store the API key in persistent storage
|
|
467
|
-
this.persistentConfig.apiKeys[envVar] = apiKey;
|
|
468
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
469
|
-
// Check if we can auto-select this provider
|
|
470
|
-
const shouldAutoSelect = !this.currentLLMConfig;
|
|
471
|
-
if (shouldAutoSelect) {
|
|
472
|
-
const defaultModels = {
|
|
473
|
-
openai: 'gpt-4o-mini',
|
|
474
|
-
anthropic: 'claude-3-5-sonnet-20241022',
|
|
475
|
-
google: 'gemini-1.5-pro',
|
|
476
|
-
mistral: 'mistral-large-latest'
|
|
477
|
-
};
|
|
478
|
-
this.currentLLMConfig = {
|
|
479
|
-
provider: provider,
|
|
480
|
-
model: defaultModels[provider],
|
|
481
|
-
temperature: 0.7
|
|
352
|
+
message: result.message,
|
|
482
353
|
};
|
|
483
354
|
}
|
|
484
|
-
const maskedKey = this.maskApiKey(apiKey);
|
|
355
|
+
const maskedKey = this.llmService.maskApiKey(apiKey);
|
|
485
356
|
let message = `ā
${provider} API key set (${maskedKey})`;
|
|
486
|
-
if (
|
|
487
|
-
message += `\nš¤ Auto-selected ${
|
|
357
|
+
if (result.autoSelected) {
|
|
358
|
+
message += `\nš¤ Auto-selected ${result.autoSelected.provider}/${result.autoSelected.model}`;
|
|
488
359
|
}
|
|
489
360
|
return {
|
|
490
361
|
type: 'success',
|
|
491
362
|
message,
|
|
492
|
-
data:
|
|
363
|
+
data: result.autoSelected ? { llmConfig: result.autoSelected } : undefined,
|
|
493
364
|
};
|
|
494
365
|
}
|
|
495
|
-
validateApiKey(provider, apiKey) {
|
|
496
|
-
if (!apiKey || apiKey.trim().length === 0) {
|
|
497
|
-
return { valid: false, message: 'API key cannot be empty' };
|
|
498
|
-
}
|
|
499
|
-
// Basic format validation for each provider
|
|
500
|
-
switch (provider) {
|
|
501
|
-
case 'openai':
|
|
502
|
-
if (!apiKey.startsWith('sk-') || apiKey.length < 20) {
|
|
503
|
-
return { valid: false, message: 'OpenAI API keys should start with "sk-" and be at least 20 characters long' };
|
|
504
|
-
}
|
|
505
|
-
break;
|
|
506
|
-
case 'anthropic':
|
|
507
|
-
if (!apiKey.startsWith('ant_') && !apiKey.startsWith('sk-ant-') || apiKey.length < 20) {
|
|
508
|
-
return { valid: false, message: 'Anthropic API keys should start with "ant_" or "sk-ant-" and be at least 20 characters long' };
|
|
509
|
-
}
|
|
510
|
-
break;
|
|
511
|
-
case 'google':
|
|
512
|
-
if (apiKey.length < 20) {
|
|
513
|
-
return { valid: false, message: 'Google API keys should be at least 20 characters long' };
|
|
514
|
-
}
|
|
515
|
-
break;
|
|
516
|
-
case 'mistral':
|
|
517
|
-
if (apiKey.length < 20) {
|
|
518
|
-
return { valid: false, message: 'Mistral API keys should be at least 20 characters long' };
|
|
519
|
-
}
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
return { valid: true, message: '' };
|
|
523
|
-
}
|
|
524
|
-
maskApiKey(apiKey) {
|
|
525
|
-
if (apiKey.length <= 8) {
|
|
526
|
-
return '*'.repeat(apiKey.length);
|
|
527
|
-
}
|
|
528
|
-
const start = apiKey.substring(0, 4);
|
|
529
|
-
const end = apiKey.substring(apiKey.length - 4);
|
|
530
|
-
const middle = '*'.repeat(Math.min(12, apiKey.length - 8));
|
|
531
|
-
return `${start}${middle}${end}`;
|
|
532
|
-
}
|
|
533
366
|
createLLM() {
|
|
534
|
-
|
|
535
|
-
throw new Error('No LLM configured. Use /model command to select a provider and model.');
|
|
536
|
-
}
|
|
537
|
-
const config = this.currentLLMConfig;
|
|
538
|
-
const baseConfig = {
|
|
539
|
-
temperature: config.temperature || 0.7,
|
|
540
|
-
maxTokens: config.maxTokens
|
|
541
|
-
};
|
|
542
|
-
switch (config.provider) {
|
|
543
|
-
case 'openai':
|
|
544
|
-
const openaiKey = this.getApiKey('OPENAI_API_KEY');
|
|
545
|
-
if (!openaiKey) {
|
|
546
|
-
throw new Error('OPENAI_API_KEY is required for OpenAI models. Use /setkey openai <your-key>');
|
|
547
|
-
}
|
|
548
|
-
return new ChatOpenAI({
|
|
549
|
-
modelName: config.model,
|
|
550
|
-
openAIApiKey: openaiKey,
|
|
551
|
-
...baseConfig
|
|
552
|
-
});
|
|
553
|
-
case 'anthropic':
|
|
554
|
-
const anthropicKey = this.getApiKey('ANTHROPIC_API_KEY');
|
|
555
|
-
if (!anthropicKey) {
|
|
556
|
-
throw new Error('ANTHROPIC_API_KEY is required for Anthropic models. Use /setkey anthropic <your-key>');
|
|
557
|
-
}
|
|
558
|
-
return new ChatAnthropic({
|
|
559
|
-
modelName: config.model,
|
|
560
|
-
anthropicApiKey: anthropicKey,
|
|
561
|
-
...baseConfig
|
|
562
|
-
});
|
|
563
|
-
case 'google':
|
|
564
|
-
const googleKey = this.getApiKey('GOOGLE_API_KEY');
|
|
565
|
-
if (!googleKey) {
|
|
566
|
-
throw new Error('GOOGLE_API_KEY is required for Google models. Use /setkey google <your-key>');
|
|
567
|
-
}
|
|
568
|
-
return new ChatGoogleGenerativeAI({
|
|
569
|
-
model: config.model,
|
|
570
|
-
apiKey: googleKey,
|
|
571
|
-
...baseConfig
|
|
572
|
-
});
|
|
573
|
-
case 'mistral':
|
|
574
|
-
const mistralKey = this.getApiKey('MISTRAL_API_KEY');
|
|
575
|
-
if (!mistralKey) {
|
|
576
|
-
throw new Error('MISTRAL_API_KEY is required for Mistral models. Use /setkey mistral <your-key>');
|
|
577
|
-
}
|
|
578
|
-
return new ChatMistralAI({
|
|
579
|
-
modelName: config.model,
|
|
580
|
-
apiKey: mistralKey,
|
|
581
|
-
...baseConfig
|
|
582
|
-
});
|
|
583
|
-
default:
|
|
584
|
-
throw new Error(`Unsupported provider: ${config.provider}`);
|
|
585
|
-
}
|
|
367
|
+
return this.llmService.createLLM();
|
|
586
368
|
}
|
|
587
369
|
getCurrentConfig() {
|
|
588
|
-
return this.
|
|
370
|
+
return this.llmService.getCurrentConfig();
|
|
589
371
|
}
|
|
590
372
|
getCurrentStoredConfig() {
|
|
591
|
-
|
|
373
|
+
// Return a minimal StoredConfig for backward compatibility
|
|
374
|
+
return {
|
|
375
|
+
apiKeys: {},
|
|
376
|
+
mcpServers: this.mcpService.getConfiguredServers(),
|
|
377
|
+
lastModel: this.llmService.getCurrentConfig(),
|
|
378
|
+
};
|
|
592
379
|
}
|
|
593
380
|
getSessionServers() {
|
|
594
|
-
return this.
|
|
381
|
+
return this.mcpService.getSessionServers();
|
|
595
382
|
}
|
|
596
383
|
handleListTools() {
|
|
597
384
|
return {
|
|
598
385
|
type: 'info',
|
|
599
386
|
message: 'š§ Checking available MCP tools...\n\nThis command will show tools available from connected MCP servers.\nNote: This requires the MCP service to provide tool listing functionality.',
|
|
600
|
-
data: { checkTools: true }
|
|
387
|
+
data: { checkTools: true },
|
|
601
388
|
};
|
|
602
389
|
}
|
|
603
390
|
handleTestServer(args) {
|
|
604
391
|
if (args.length === 0) {
|
|
605
|
-
const configuredServers = Object.keys(this.
|
|
392
|
+
const configuredServers = Object.keys(this.mcpService.getConfiguredServers());
|
|
606
393
|
if (configuredServers.length === 0) {
|
|
607
394
|
return {
|
|
608
395
|
type: 'error',
|
|
609
|
-
message: 'No servers configured to test.\n\nUsage: /test-server <server_name>\n\nUse /server add to configure servers first.'
|
|
396
|
+
message: 'No servers configured to test.\n\nUsage: /test-server <server_name>\n\nUse /server add to configure servers first.',
|
|
610
397
|
};
|
|
611
398
|
}
|
|
612
399
|
return {
|
|
613
400
|
type: 'info',
|
|
614
|
-
message: `Usage: /test-server <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}\n\nThis command will test if the server package can be started manually
|
|
401
|
+
message: `Usage: /test-server <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}\n\nThis command will test if the server package can be started manually.`,
|
|
615
402
|
};
|
|
616
403
|
}
|
|
617
404
|
const serverName = args[0];
|
|
618
405
|
if (!serverName) {
|
|
619
406
|
return {
|
|
620
407
|
type: 'error',
|
|
621
|
-
message: 'Server name is required.\n\nUsage: /test-server <server_name>'
|
|
408
|
+
message: 'Server name is required.\n\nUsage: /test-server <server_name>',
|
|
622
409
|
};
|
|
623
410
|
}
|
|
624
|
-
const
|
|
625
|
-
if (!
|
|
626
|
-
const configuredServers = Object.keys(this.persistentConfig.mcpServers || {});
|
|
411
|
+
const result = this.mcpService.getServerTestCommand(serverName);
|
|
412
|
+
if (!result.success) {
|
|
627
413
|
return {
|
|
628
414
|
type: 'error',
|
|
629
|
-
message:
|
|
415
|
+
message: result.message,
|
|
630
416
|
};
|
|
631
417
|
}
|
|
632
|
-
const command = serverConfig.command;
|
|
633
|
-
const args_str = serverConfig.args ? serverConfig.args.join(' ') : '';
|
|
634
|
-
const full_command = `${command} ${args_str}`.trim();
|
|
635
418
|
return {
|
|
636
419
|
type: 'info',
|
|
637
|
-
message: `š§Ŗ Testing server "${serverName}"...\n\nCommand: ${
|
|
638
|
-
data: { testServer: true, serverName, command:
|
|
420
|
+
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}`,
|
|
421
|
+
data: { testServer: true, serverName, command: result.command },
|
|
639
422
|
};
|
|
640
423
|
}
|
|
641
424
|
isCommand(input) {
|
|
642
425
|
return input.trim().startsWith('/');
|
|
643
426
|
}
|
|
644
427
|
handleClearKeys() {
|
|
645
|
-
|
|
646
|
-
this.sessionApiKeys = {};
|
|
647
|
-
this.persistentConfig.apiKeys = {};
|
|
648
|
-
this.persistentConfig.lastModel = undefined;
|
|
649
|
-
// Clear the current LLM config since we have no keys
|
|
650
|
-
this.currentLLMConfig = null;
|
|
651
|
-
// Save the cleared config
|
|
652
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
428
|
+
this.llmService.clearApiKeys();
|
|
653
429
|
return {
|
|
654
430
|
type: 'success',
|
|
655
431
|
message: 'ā
All API keys cleared from storage.\n\nUse /setkey or /model to set up a new provider.',
|
|
656
|
-
data: { llmConfig: null }
|
|
432
|
+
data: { llmConfig: null },
|
|
657
433
|
};
|
|
658
434
|
}
|
|
659
435
|
// Method to handle server configuration input
|
|
@@ -663,181 +439,118 @@ Available slash commands:
|
|
|
663
439
|
case 'name_or_json':
|
|
664
440
|
// Check if input looks like JSON
|
|
665
441
|
const trimmedInput = input.trim();
|
|
666
|
-
if (trimmedInput.startsWith('{') &&
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (!parsedConfig.mcpServers || typeof parsedConfig.mcpServers !== 'object') {
|
|
671
|
-
return {
|
|
672
|
-
type: 'error',
|
|
673
|
-
message: 'Invalid JSON format. Expected format:\n{\n "mcpServers": {\n "servername": {\n "command": "...",\n "args": [...]\n }\n }\n}'
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
const servers = parsedConfig.mcpServers;
|
|
677
|
-
const serverNames = Object.keys(servers);
|
|
678
|
-
if (serverNames.length === 0) {
|
|
679
|
-
return {
|
|
680
|
-
type: 'error',
|
|
681
|
-
message: 'No servers found in JSON configuration.'
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
// Check for conflicts with existing servers
|
|
685
|
-
const existingServers = this.persistentConfig.mcpServers || {};
|
|
686
|
-
const conflicts = serverNames.filter(name => existingServers[name]);
|
|
687
|
-
if (conflicts.length > 0) {
|
|
688
|
-
return {
|
|
689
|
-
type: 'error',
|
|
690
|
-
message: `Server(s) already exist: ${conflicts.join(', ')}. Please use different names.`
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
// Validate each server config
|
|
694
|
-
for (const [name, serverConfig] of Object.entries(servers)) {
|
|
695
|
-
const server = serverConfig;
|
|
696
|
-
if (!server.command || typeof server.command !== 'string') {
|
|
697
|
-
return {
|
|
698
|
-
type: 'error',
|
|
699
|
-
message: `Server "${name}" missing required "command" field.`
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
// All validation passed, save the servers
|
|
704
|
-
if (!this.persistentConfig.mcpServers) {
|
|
705
|
-
this.persistentConfig.mcpServers = {};
|
|
706
|
-
}
|
|
707
|
-
// Add all servers from JSON
|
|
708
|
-
Object.assign(this.persistentConfig.mcpServers, servers);
|
|
709
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
710
|
-
// Auto-connect all newly configured servers
|
|
711
|
-
Object.assign(this.sessionServers, servers);
|
|
712
|
-
const addedCount = serverNames.length;
|
|
713
|
-
const serverList = serverNames.map(name => `⢠${name}`).join('\n');
|
|
714
|
-
return {
|
|
715
|
-
type: 'success',
|
|
716
|
-
message: `ā
Configured and connected ${addedCount} server(s)!\n\n${serverList}\n\nš Agent will be reinitialized with these servers - attempting to establish connections...\nUse /tools to verify the server tools are available.`,
|
|
717
|
-
data: { serversAdded: true, serverConnected: true, serverNames }
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
catch (error) {
|
|
442
|
+
if (trimmedInput.startsWith('{') &&
|
|
443
|
+
trimmedInput.includes('mcpServers')) {
|
|
444
|
+
const result = this.mcpService.addServerFromJSON(trimmedInput);
|
|
445
|
+
if (!result.success) {
|
|
721
446
|
return {
|
|
722
447
|
type: 'error',
|
|
723
|
-
message:
|
|
448
|
+
message: result.message,
|
|
724
449
|
};
|
|
725
450
|
}
|
|
726
|
-
}
|
|
727
|
-
// Not JSON, treat as server name for interactive setup
|
|
728
|
-
if (!trimmedInput) {
|
|
729
451
|
return {
|
|
730
|
-
type: '
|
|
731
|
-
message:
|
|
452
|
+
type: 'success',
|
|
453
|
+
message: `${result.message}.`,
|
|
454
|
+
data: result.data,
|
|
732
455
|
};
|
|
733
456
|
}
|
|
734
|
-
//
|
|
735
|
-
|
|
457
|
+
// Not JSON, treat as server name for interactive setup
|
|
458
|
+
const validation = this.mcpService.validateServerName(trimmedInput);
|
|
459
|
+
if (!validation.valid) {
|
|
736
460
|
return {
|
|
737
461
|
type: 'error',
|
|
738
|
-
message:
|
|
462
|
+
message: validation.message,
|
|
739
463
|
};
|
|
740
464
|
}
|
|
741
465
|
config.name = trimmedInput;
|
|
742
466
|
return {
|
|
743
467
|
type: 'prompt_server_config',
|
|
744
468
|
message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
|
|
745
|
-
data: { step: 'command', config }
|
|
469
|
+
data: { step: 'command', config },
|
|
746
470
|
};
|
|
747
471
|
case 'name':
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
type: 'error',
|
|
751
|
-
message: 'Server name cannot be empty.'
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
// Check if server name already exists
|
|
755
|
-
if (this.persistentConfig.mcpServers?.[input.trim()]) {
|
|
472
|
+
const nameValidation = this.mcpService.validateServerName(input.trim());
|
|
473
|
+
if (!nameValidation.valid) {
|
|
756
474
|
return {
|
|
757
475
|
type: 'error',
|
|
758
|
-
message:
|
|
476
|
+
message: nameValidation.message,
|
|
759
477
|
};
|
|
760
478
|
}
|
|
761
479
|
config.name = input.trim();
|
|
762
480
|
return {
|
|
763
481
|
type: 'prompt_server_config',
|
|
764
482
|
message: `Server name: ${config.name}\n\nEnter the command to run this server (e.g., "npx", "node", "python"):`,
|
|
765
|
-
data: { step: 'command', config }
|
|
483
|
+
data: { step: 'command', config },
|
|
766
484
|
};
|
|
767
485
|
case 'command':
|
|
768
486
|
if (!input.trim()) {
|
|
769
487
|
return {
|
|
770
488
|
type: 'error',
|
|
771
|
-
message: 'Command cannot be empty.'
|
|
489
|
+
message: 'Command cannot be empty.',
|
|
772
490
|
};
|
|
773
491
|
}
|
|
774
492
|
config.command = input.trim();
|
|
775
493
|
return {
|
|
776
494
|
type: 'prompt_server_config',
|
|
777
495
|
message: `Server name: ${config.name}\nCommand: ${config.command}\n\nEnter arguments (space-separated, or press Enter for none):\nExample: "-y @modelcontextprotocol/server-filesystem /tmp"`,
|
|
778
|
-
data: { step: 'args', config }
|
|
496
|
+
data: { step: 'args', config },
|
|
779
497
|
};
|
|
780
498
|
case 'args':
|
|
781
499
|
config.args = input.trim() ? input.trim().split(/\s+/) : [];
|
|
782
500
|
return {
|
|
783
501
|
type: 'prompt_server_config',
|
|
784
502
|
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:`,
|
|
785
|
-
data: { step: 'env', config }
|
|
503
|
+
data: { step: 'env', config },
|
|
786
504
|
};
|
|
787
505
|
case 'env':
|
|
788
|
-
config.env =
|
|
789
|
-
if (input.trim()) {
|
|
790
|
-
const envLines = input.trim().split('\n');
|
|
791
|
-
for (const line of envLines) {
|
|
792
|
-
const [key, ...valueParts] = line.split('=');
|
|
793
|
-
if (key && valueParts.length > 0) {
|
|
794
|
-
config.env[key.trim()] = valueParts.join('=').trim();
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
506
|
+
config.env = this.mcpService.parseEnvironmentVariables(input);
|
|
798
507
|
return {
|
|
799
508
|
type: 'prompt_server_config',
|
|
800
|
-
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
|
|
801
|
-
|
|
509
|
+
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
|
|
510
|
+
? Object.entries(config.env)
|
|
511
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
512
|
+
.join(', ')
|
|
513
|
+
: 'none'}\n\nConfirm to add this server? (y/n):`,
|
|
514
|
+
data: { step: 'confirm', config },
|
|
802
515
|
};
|
|
803
516
|
case 'confirm':
|
|
804
|
-
if (input.trim().toLowerCase() === 'y' ||
|
|
517
|
+
if (input.trim().toLowerCase() === 'y' ||
|
|
518
|
+
input.trim().toLowerCase() === 'yes') {
|
|
805
519
|
const serverConfig = {
|
|
806
520
|
command: config.command,
|
|
807
521
|
args: config.args,
|
|
808
|
-
env: config.env
|
|
522
|
+
env: config.env,
|
|
809
523
|
};
|
|
810
|
-
|
|
811
|
-
if (!
|
|
812
|
-
|
|
524
|
+
const result = this.mcpService.addServer(config.name, serverConfig);
|
|
525
|
+
if (!result.success) {
|
|
526
|
+
return {
|
|
527
|
+
type: 'error',
|
|
528
|
+
message: result.message,
|
|
529
|
+
};
|
|
813
530
|
}
|
|
814
|
-
this.persistentConfig.mcpServers[config.name] = serverConfig;
|
|
815
|
-
// Save configuration
|
|
816
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
817
|
-
// Auto-connect the newly configured server
|
|
818
|
-
this.sessionServers[config.name] = serverConfig;
|
|
819
531
|
return {
|
|
820
532
|
type: 'success',
|
|
821
|
-
message:
|
|
822
|
-
data:
|
|
533
|
+
message: `${result.message}.`,
|
|
534
|
+
data: result.data,
|
|
823
535
|
};
|
|
824
536
|
}
|
|
825
|
-
else if (input.trim().toLowerCase() === 'n' ||
|
|
537
|
+
else if (input.trim().toLowerCase() === 'n' ||
|
|
538
|
+
input.trim().toLowerCase() === 'no') {
|
|
826
539
|
return {
|
|
827
540
|
type: 'info',
|
|
828
|
-
message: 'Server configuration cancelled.'
|
|
541
|
+
message: 'Server configuration cancelled.',
|
|
829
542
|
};
|
|
830
543
|
}
|
|
831
544
|
else {
|
|
832
545
|
return {
|
|
833
546
|
type: 'error',
|
|
834
|
-
message: 'Please enter "y" for yes or "n" for no.'
|
|
547
|
+
message: 'Please enter "y" for yes or "n" for no.',
|
|
835
548
|
};
|
|
836
549
|
}
|
|
837
550
|
default:
|
|
838
551
|
return {
|
|
839
552
|
type: 'error',
|
|
840
|
-
message: 'Invalid server configuration step.'
|
|
553
|
+
message: 'Invalid server configuration step.',
|
|
841
554
|
};
|
|
842
555
|
}
|
|
843
556
|
}
|
|
@@ -845,180 +558,207 @@ Available slash commands:
|
|
|
845
558
|
if (args.length === 0) {
|
|
846
559
|
return {
|
|
847
560
|
type: 'info',
|
|
848
|
-
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.'
|
|
561
|
+
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.',
|
|
849
562
|
};
|
|
850
563
|
}
|
|
851
564
|
if (args[0] === 'add') {
|
|
852
565
|
return {
|
|
853
566
|
type: 'prompt_server_config',
|
|
854
567
|
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:',
|
|
855
|
-
data: { step: 'name_or_json' }
|
|
568
|
+
data: { step: 'name_or_json' },
|
|
856
569
|
};
|
|
857
570
|
}
|
|
858
571
|
if (args[0] === 'connect') {
|
|
859
572
|
if (args.length < 2) {
|
|
860
|
-
const configuredServers = Object.keys(
|
|
573
|
+
const configuredServers = Object.keys(this.mcpService.getConfiguredServers());
|
|
861
574
|
if (configuredServers.length === 0) {
|
|
862
575
|
return {
|
|
863
576
|
type: 'error',
|
|
864
|
-
message: 'No servers configured. Use /server add to configure servers first.\n\nUsage: /server connect <server_name>'
|
|
577
|
+
message: 'No servers configured. Use /server add to configure servers first.\n\nUsage: /server connect <server_name>',
|
|
865
578
|
};
|
|
866
579
|
}
|
|
867
580
|
return {
|
|
868
581
|
type: 'error',
|
|
869
|
-
message: `Usage: /server connect <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}
|
|
582
|
+
message: `Usage: /server connect <server_name>\n\nConfigured servers: ${configuredServers.join(', ')}`,
|
|
870
583
|
};
|
|
871
584
|
}
|
|
872
585
|
const serverName = args[1];
|
|
873
586
|
if (!serverName) {
|
|
874
587
|
return {
|
|
875
588
|
type: 'error',
|
|
876
|
-
message: 'Server name is required.\n\nUsage: /server connect <server_name>'
|
|
589
|
+
message: 'Server name is required.\n\nUsage: /server connect <server_name>',
|
|
877
590
|
};
|
|
878
591
|
}
|
|
879
592
|
return this.handleConnectServer(serverName);
|
|
880
593
|
}
|
|
881
594
|
if (args[0] === 'disconnect') {
|
|
882
595
|
if (args.length < 2) {
|
|
883
|
-
const connectedServers = Object.keys(this.
|
|
596
|
+
const connectedServers = Object.keys(this.mcpService.getSessionServers());
|
|
884
597
|
if (connectedServers.length === 0) {
|
|
885
598
|
return {
|
|
886
599
|
type: 'info',
|
|
887
|
-
message: 'No servers currently connected.\n\nUsage: /server disconnect <server_name>'
|
|
600
|
+
message: 'No servers currently connected.\n\nUsage: /server disconnect <server_name>',
|
|
888
601
|
};
|
|
889
602
|
}
|
|
890
603
|
return {
|
|
891
604
|
type: 'error',
|
|
892
|
-
message: `Usage: /server disconnect <server_name>\n\nConnected servers: ${connectedServers.join(', ')}
|
|
605
|
+
message: `Usage: /server disconnect <server_name>\n\nConnected servers: ${connectedServers.join(', ')}`,
|
|
893
606
|
};
|
|
894
607
|
}
|
|
895
608
|
const serverName = args[1];
|
|
896
609
|
if (!serverName) {
|
|
897
610
|
return {
|
|
898
611
|
type: 'error',
|
|
899
|
-
message: 'Server name is required.\n\nUsage: /server disconnect <server_name>'
|
|
612
|
+
message: 'Server name is required.\n\nUsage: /server disconnect <server_name>',
|
|
900
613
|
};
|
|
901
614
|
}
|
|
902
615
|
return this.handleDisconnectServer(serverName);
|
|
903
616
|
}
|
|
904
617
|
return {
|
|
905
618
|
type: 'error',
|
|
906
|
-
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'
|
|
619
|
+
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',
|
|
907
620
|
};
|
|
908
621
|
}
|
|
909
622
|
handleListServers() {
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
serverList += ' Command: npx\n';
|
|
917
|
-
serverList += ' Args: -y @modelcontextprotocol/server-filesystem /tmp\n\n';
|
|
918
|
-
// Custom configured servers
|
|
919
|
-
const configuredServerNames = Object.keys(persistentServers);
|
|
920
|
-
if (configuredServerNames.length === 0) {
|
|
921
|
-
serverList += 'No custom servers configured.\n\nUse /server add to configure servers, then /server connect <name> to connect.';
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
for (const [name, config] of Object.entries(persistentServers)) {
|
|
925
|
-
const isConnected = connectedServers[name] !== undefined;
|
|
926
|
-
const status = isConnected ? 'š¢ Connected' : 'š“ Disconnected';
|
|
927
|
-
const action = isConnected ? '/server disconnect' : '/server connect';
|
|
928
|
-
serverList += `šø ${name}:\n`;
|
|
929
|
-
serverList += ` Status: ${status}\n`;
|
|
930
|
-
serverList += ` Command: ${config.command}\n`;
|
|
931
|
-
if (config.args && config.args.length > 0) {
|
|
932
|
-
serverList += ` Args: ${config.args.join(' ')}\n`;
|
|
933
|
-
}
|
|
934
|
-
if (config.env && Object.keys(config.env).length > 0) {
|
|
935
|
-
serverList += ` Env: ${Object.entries(config.env).map(([k, v]) => `${k}=${v}`).join(', ')}\n`;
|
|
936
|
-
}
|
|
937
|
-
serverList += ` Action: ${action} ${name}\n\n`;
|
|
938
|
-
}
|
|
623
|
+
const serverStatus = this.mcpService.getServerStatus();
|
|
624
|
+
if (serverStatus.length === 0) {
|
|
625
|
+
return {
|
|
626
|
+
type: 'info',
|
|
627
|
+
message: 'No custom servers configured.\n\nUse /server add to configure servers, then /server connect <name> to connect.',
|
|
628
|
+
};
|
|
939
629
|
}
|
|
940
630
|
return {
|
|
941
|
-
type: '
|
|
942
|
-
message:
|
|
631
|
+
type: 'list_servers',
|
|
632
|
+
message: 'MCP Server Status:',
|
|
633
|
+
data: { servers: serverStatus },
|
|
943
634
|
};
|
|
944
635
|
}
|
|
945
636
|
handleConnectServer(serverName) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
if (!configuredServer) {
|
|
949
|
-
const availableServers = Object.keys(this.persistentConfig.mcpServers || {});
|
|
637
|
+
const result = this.mcpService.connectServer(serverName);
|
|
638
|
+
if (!result.success) {
|
|
950
639
|
return {
|
|
951
640
|
type: 'error',
|
|
952
|
-
message:
|
|
641
|
+
message: result.message,
|
|
953
642
|
};
|
|
954
643
|
}
|
|
955
|
-
// Check if already connected (in session servers)
|
|
956
|
-
if (this.sessionServers[serverName]) {
|
|
957
|
-
return {
|
|
958
|
-
type: 'info',
|
|
959
|
-
message: `ā
Server "${serverName}" is already connected.`
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
// Connect the server (add to session servers)
|
|
963
|
-
this.sessionServers[serverName] = configuredServer;
|
|
964
644
|
return {
|
|
965
645
|
type: 'success',
|
|
966
|
-
message:
|
|
967
|
-
data:
|
|
646
|
+
message: `${result.message}.`,
|
|
647
|
+
data: result.data,
|
|
968
648
|
};
|
|
969
649
|
}
|
|
970
650
|
handleDisconnectServer(serverName) {
|
|
971
|
-
|
|
972
|
-
if (!
|
|
973
|
-
const connectedServers = Object.keys(this.sessionServers);
|
|
651
|
+
const result = this.mcpService.disconnectServer(serverName);
|
|
652
|
+
if (!result.success) {
|
|
974
653
|
return {
|
|
975
654
|
type: 'error',
|
|
976
|
-
message:
|
|
655
|
+
message: result.message,
|
|
977
656
|
};
|
|
978
657
|
}
|
|
979
|
-
// Disconnect the server (remove from session servers)
|
|
980
|
-
delete this.sessionServers[serverName];
|
|
981
658
|
return {
|
|
982
659
|
type: 'success',
|
|
983
|
-
message:
|
|
984
|
-
data:
|
|
660
|
+
message: `${result.message}.`,
|
|
661
|
+
data: result.data,
|
|
985
662
|
};
|
|
986
663
|
}
|
|
987
664
|
// Method to handle API key input and complete model selection
|
|
988
665
|
handleApiKeyInput(apiKey, provider, model) {
|
|
989
|
-
//
|
|
990
|
-
const
|
|
991
|
-
if (!
|
|
666
|
+
// Set the API key
|
|
667
|
+
const keyResult = this.llmService.setApiKey(provider, apiKey, false);
|
|
668
|
+
if (!keyResult.success) {
|
|
992
669
|
return {
|
|
993
670
|
type: 'error',
|
|
994
|
-
message:
|
|
671
|
+
message: keyResult.message,
|
|
995
672
|
};
|
|
996
673
|
}
|
|
997
|
-
//
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
this.persistentConfig.apiKeys[envVar] = apiKey;
|
|
1007
|
-
// Set the model configuration
|
|
1008
|
-
this.currentLLMConfig = {
|
|
1009
|
-
provider: provider,
|
|
1010
|
-
model,
|
|
1011
|
-
temperature: this.currentLLMConfig?.temperature || 0.7,
|
|
1012
|
-
maxTokens: this.currentLLMConfig?.maxTokens
|
|
1013
|
-
};
|
|
1014
|
-
// Save both the API key and the current model to persistent storage
|
|
1015
|
-
this.persistentConfig.lastModel = this.currentLLMConfig;
|
|
1016
|
-
SecureStorage.saveConfig(this.persistentConfig);
|
|
1017
|
-
const maskedKey = this.maskApiKey(apiKey);
|
|
674
|
+
// Now set the model
|
|
675
|
+
const modelResult = this.llmService.setModel(provider, model);
|
|
676
|
+
if (!modelResult.success) {
|
|
677
|
+
return {
|
|
678
|
+
type: 'error',
|
|
679
|
+
message: modelResult.message,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
const maskedKey = this.llmService.maskApiKey(apiKey);
|
|
1018
683
|
return {
|
|
1019
684
|
type: 'success',
|
|
1020
685
|
message: `ā
${provider} API key set (${maskedKey})\nš¤ Switched to ${provider}/${model}`,
|
|
1021
|
-
data: { llmConfig: this.
|
|
686
|
+
data: { llmConfig: this.llmService.getCurrentConfig() },
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
handleLogs(args) {
|
|
690
|
+
const logPath = Logger.getLogPath();
|
|
691
|
+
if (args.length === 0) {
|
|
692
|
+
return {
|
|
693
|
+
type: 'info',
|
|
694
|
+
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}`,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
const subcommand = args[0];
|
|
698
|
+
switch (subcommand) {
|
|
699
|
+
case 'path':
|
|
700
|
+
return {
|
|
701
|
+
type: 'info',
|
|
702
|
+
message: `š Log file location:\n${logPath}`,
|
|
703
|
+
};
|
|
704
|
+
case 'tail':
|
|
705
|
+
try {
|
|
706
|
+
const fs = require('fs');
|
|
707
|
+
if (!fs.existsSync(logPath)) {
|
|
708
|
+
return {
|
|
709
|
+
type: 'info',
|
|
710
|
+
message: 'š No log file found yet. Logs will be created when the app starts logging.',
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
const logContent = fs.readFileSync(logPath, 'utf8');
|
|
714
|
+
const lines = logContent
|
|
715
|
+
.split('\n')
|
|
716
|
+
.filter((line) => line.trim());
|
|
717
|
+
const recentLines = lines.slice(-20); // Show last 20 lines
|
|
718
|
+
if (recentLines.length === 0) {
|
|
719
|
+
return {
|
|
720
|
+
type: 'info',
|
|
721
|
+
message: 'š Log file is empty.',
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
type: 'info',
|
|
726
|
+
message: `š Recent log entries (last ${recentLines.length} lines):\n\n${recentLines.join('\n')}`,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
return {
|
|
731
|
+
type: 'error',
|
|
732
|
+
message: `ā Failed to read logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
default:
|
|
736
|
+
return {
|
|
737
|
+
type: 'error',
|
|
738
|
+
message: `Unknown logs subcommand: ${subcommand}. Use /logs for help.`,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
handleClearLogs() {
|
|
743
|
+
try {
|
|
744
|
+
Logger.clearLogs();
|
|
745
|
+
Logger.info('Logs cleared by user command');
|
|
746
|
+
return {
|
|
747
|
+
type: 'success',
|
|
748
|
+
message: 'ā
Debug logs cleared successfully.',
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
return {
|
|
753
|
+
type: 'error',
|
|
754
|
+
message: `ā Failed to clear logs: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
handleHistory() {
|
|
759
|
+
return {
|
|
760
|
+
type: 'info',
|
|
761
|
+
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!`,
|
|
1022
762
|
};
|
|
1023
763
|
}
|
|
1024
764
|
}
|