claudeup 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/package.json +23 -12
  2. package/dist/data/cli-tools.d.ts +0 -13
  3. package/dist/data/cli-tools.d.ts.map +0 -1
  4. package/dist/data/cli-tools.js +0 -113
  5. package/dist/data/cli-tools.js.map +0 -1
  6. package/dist/data/marketplaces.d.ts +0 -4
  7. package/dist/data/marketplaces.d.ts.map +0 -1
  8. package/dist/data/marketplaces.js +0 -26
  9. package/dist/data/marketplaces.js.map +0 -1
  10. package/dist/data/mcp-servers.d.ts +0 -8
  11. package/dist/data/mcp-servers.d.ts.map +0 -1
  12. package/dist/data/mcp-servers.js +0 -421
  13. package/dist/data/mcp-servers.js.map +0 -1
  14. package/dist/data/statuslines.d.ts +0 -10
  15. package/dist/data/statuslines.d.ts.map +0 -1
  16. package/dist/data/statuslines.js +0 -160
  17. package/dist/data/statuslines.js.map +0 -1
  18. package/dist/index.d.ts +0 -3
  19. package/dist/index.d.ts.map +0 -1
  20. package/dist/index.js.map +0 -1
  21. package/dist/services/claude-settings.d.ts +0 -46
  22. package/dist/services/claude-settings.d.ts.map +0 -1
  23. package/dist/services/claude-settings.js +0 -248
  24. package/dist/services/claude-settings.js.map +0 -1
  25. package/dist/services/mcp-registry.d.ts +0 -10
  26. package/dist/services/mcp-registry.d.ts.map +0 -1
  27. package/dist/services/mcp-registry.js +0 -88
  28. package/dist/services/mcp-registry.js.map +0 -1
  29. package/dist/services/plugin-manager.d.ts +0 -23
  30. package/dist/services/plugin-manager.d.ts.map +0 -1
  31. package/dist/services/plugin-manager.js +0 -151
  32. package/dist/services/plugin-manager.js.map +0 -1
  33. package/dist/types/index.d.ts +0 -83
  34. package/dist/types/index.d.ts.map +0 -1
  35. package/dist/types/index.js +0 -2
  36. package/dist/types/index.js.map +0 -1
  37. package/dist/ui/app.d.ts +0 -38
  38. package/dist/ui/app.d.ts.map +0 -1
  39. package/dist/ui/app.js +0 -553
  40. package/dist/ui/app.js.map +0 -1
  41. package/dist/ui/screens/cli-tools.d.ts +0 -4
  42. package/dist/ui/screens/cli-tools.d.ts.map +0 -1
  43. package/dist/ui/screens/cli-tools.js +0 -331
  44. package/dist/ui/screens/cli-tools.js.map +0 -1
  45. package/dist/ui/screens/main-menu.d.ts +0 -3
  46. package/dist/ui/screens/main-menu.d.ts.map +0 -1
  47. package/dist/ui/screens/main-menu.js +0 -110
  48. package/dist/ui/screens/main-menu.js.map +0 -1
  49. package/dist/ui/screens/marketplace.d.ts +0 -3
  50. package/dist/ui/screens/marketplace.d.ts.map +0 -1
  51. package/dist/ui/screens/marketplace.js +0 -132
  52. package/dist/ui/screens/marketplace.js.map +0 -1
  53. package/dist/ui/screens/mcp-registry.d.ts +0 -10
  54. package/dist/ui/screens/mcp-registry.d.ts.map +0 -1
  55. package/dist/ui/screens/mcp-registry.js +0 -310
  56. package/dist/ui/screens/mcp-registry.js.map +0 -1
  57. package/dist/ui/screens/mcp-setup.d.ts +0 -4
  58. package/dist/ui/screens/mcp-setup.d.ts.map +0 -1
  59. package/dist/ui/screens/mcp-setup.js +0 -673
  60. package/dist/ui/screens/mcp-setup.js.map +0 -1
  61. package/dist/ui/screens/plugins.d.ts +0 -3
  62. package/dist/ui/screens/plugins.d.ts.map +0 -1
  63. package/dist/ui/screens/plugins.js +0 -449
  64. package/dist/ui/screens/plugins.js.map +0 -1
  65. package/dist/ui/screens/statusline.d.ts +0 -5
  66. package/dist/ui/screens/statusline.d.ts.map +0 -1
  67. package/dist/ui/screens/statusline.js +0 -235
  68. package/dist/ui/screens/statusline.js.map +0 -1
@@ -1,673 +0,0 @@
1
- import blessed from 'neo-blessed';
2
- import { createHeader, createFooter, showMessage, showConfirm, showInput, showSelect, } from '../app.js';
3
- import { getMcpServersByCategory, getCategoryDisplayName, categoryOrder, getAllMcpServers, } from '../../data/mcp-servers.js';
4
- import { searchMcpServers } from '../../services/mcp-registry.js';
5
- import { addMcpServer, removeMcpServer, getInstalledMcpServers, getEnabledMcpServers, } from '../../services/claude-settings.js';
6
- // Search results screen
7
- export async function createMcpSearchScreen(state, query) {
8
- createHeader(state, 'MCP Servers');
9
- const installedServers = await getInstalledMcpServers(state.projectPath);
10
- const enabledServers = await getEnabledMcpServers(state.projectPath);
11
- const allServers = getAllMcpServers();
12
- // Search both local and remote sources
13
- const q = query.toLowerCase();
14
- // Search local curated servers
15
- const localFilteredServers = allServers.filter(s => s.name.toLowerCase().includes(q) ||
16
- s.description.toLowerCase().includes(q) ||
17
- (s.category && s.category.toLowerCase().includes(q)));
18
- // Search remote MCP registry
19
- let remoteServers = [];
20
- let remoteError = null;
21
- try {
22
- const response = await searchMcpServers({ query, limit: 50 });
23
- remoteServers = response.servers || [];
24
- }
25
- catch (err) {
26
- // Remote search failed, will show local results only
27
- remoteError = err;
28
- remoteServers = [];
29
- }
30
- // Combine and deduplicate results (prioritize local/curated)
31
- const allResults = new Map();
32
- // Add local results first (they have more metadata)
33
- for (const server of localFilteredServers) {
34
- allResults.set(server.name, { server, source: 'local' });
35
- }
36
- // Add remote results, converting McpRegistryServer to McpServer format
37
- for (const remote of remoteServers) {
38
- if (!allResults.has(remote.name)) {
39
- // Convert remote server to our format
40
- const convertedServer = {
41
- name: remote.name,
42
- description: remote.short_description || 'No description',
43
- type: 'http',
44
- url: remote.url,
45
- category: 'productivity', // Default category for remote servers
46
- requiresConfig: false,
47
- };
48
- allResults.set(remote.name, { server: convertedServer, source: 'remote' });
49
- }
50
- }
51
- const combinedResults = Array.from(allResults.values());
52
- const localCount = localFilteredServers.length;
53
- let searchInfo = `${combinedResults.length} found`;
54
- if (localCount > 0) {
55
- searchInfo += ` ({cyan-fg}${localCount} local{/cyan-fg})`;
56
- }
57
- if (remoteError) {
58
- searchInfo += ` | {red-fg}Remote search failed{/red-fg}`;
59
- }
60
- // Search header
61
- blessed.box({
62
- parent: state.screen,
63
- top: 3,
64
- left: 2,
65
- width: '50%-3',
66
- height: 3,
67
- tags: true,
68
- border: { type: 'line' },
69
- style: { border: { fg: 'green' } },
70
- label: ' Search Results ',
71
- content: `{white-fg}${query}{/white-fg} {gray-fg}(${searchInfo}){/gray-fg}`,
72
- });
73
- // Build list items
74
- const listItems = combinedResults.map(({ server, source }) => {
75
- const isInstalled = installedServers[server.name] !== undefined;
76
- const isEnabled = enabledServers[server.name] === true;
77
- let status = '{gray-fg}○{/gray-fg}';
78
- if (isInstalled && isEnabled) {
79
- status = '{green-fg}●{/green-fg}';
80
- }
81
- else if (isInstalled) {
82
- status = '{yellow-fg}●{/yellow-fg}';
83
- }
84
- return {
85
- label: `${status} {bold}${server.name}{/bold}`,
86
- server,
87
- source,
88
- };
89
- });
90
- if (listItems.length === 0) {
91
- listItems.push({
92
- label: '{gray-fg}No servers match your search{/gray-fg}',
93
- server: undefined,
94
- source: 'local',
95
- });
96
- }
97
- const listLabels = listItems.map((item) => item.label);
98
- const list = blessed.list({
99
- parent: state.screen,
100
- top: 6,
101
- left: 2,
102
- width: '50%-3',
103
- height: '100%-9',
104
- items: listLabels,
105
- keys: true,
106
- mouse: true,
107
- tags: true,
108
- scrollable: true,
109
- border: { type: 'line' },
110
- style: {
111
- selected: { bg: 'green', fg: 'white' },
112
- border: { fg: 'gray' },
113
- },
114
- });
115
- // Detail panel
116
- const detailBox = blessed.box({
117
- parent: state.screen,
118
- top: 3,
119
- left: '50%',
120
- width: '50%-2',
121
- height: '100%-5',
122
- tags: true,
123
- border: { type: 'line' },
124
- style: { border: { fg: 'gray' } },
125
- label: ' Details ',
126
- });
127
- const updateDetail = () => {
128
- const selected = list.selected;
129
- const item = listItems[selected];
130
- if (!item?.server) {
131
- detailBox.setContent('{gray-fg}No server selected{/gray-fg}');
132
- state.screen.render();
133
- return;
134
- }
135
- const server = item.server;
136
- const isInstalled = installedServers[server.name] !== undefined;
137
- const sourceTag = item.source === 'remote' ? '\n{bold}Source:{/bold} {cyan-fg}MCP Registry{/cyan-fg}' : '';
138
- detailBox.setContent(`
139
- {bold}{cyan-fg}${server.name}{/cyan-fg}{/bold}
140
-
141
- ${server.description}
142
- ${sourceTag}
143
-
144
- ${isInstalled
145
- ? '{red-fg}Press Enter to remove{/red-fg}'
146
- : '{green-fg}Press Enter to install{/green-fg}'}
147
- `.trim());
148
- state.screen.render();
149
- };
150
- list.on('select item', updateDetail);
151
- setTimeout(updateDetail, 0);
152
- // Handle selection
153
- list.on('select', async (_item, index) => {
154
- const item = listItems[index];
155
- if (!item?.server)
156
- return;
157
- const server = item.server;
158
- const isInstalled = installedServers[server.name] !== undefined;
159
- if (isInstalled) {
160
- const remove = await showConfirm(state, `Remove ${server.name}?`, 'Remove MCP server?');
161
- if (remove) {
162
- await removeMcpServer(server.name, state.projectPath);
163
- await showMessage(state, 'Removed', `${server.name} removed.`, 'success');
164
- createMcpSearchScreen(state, query);
165
- }
166
- }
167
- else {
168
- await installMcpServer(state, server);
169
- createMcpSearchScreen(state, query);
170
- }
171
- });
172
- // Back to main MCP screen
173
- list.key(['escape', 'q'], () => {
174
- createMcpScreen(state);
175
- });
176
- // New search
177
- list.key(['/'], async () => {
178
- const newQuery = await showInput(state, 'Search', 'Search MCP servers:', query);
179
- if (newQuery !== null && newQuery.trim()) {
180
- createMcpSearchScreen(state, newQuery);
181
- }
182
- });
183
- createFooter(state, '↑↓ Navigate │ Enter Install/Remove │ / New Search │ Esc Back');
184
- list.focus();
185
- state.screen.render();
186
- }
187
- // Main MCP screen (no search filtering)
188
- export async function createMcpScreen(state) {
189
- createHeader(state, 'MCP Servers');
190
- const installedServers = await getInstalledMcpServers(state.projectPath);
191
- const enabledServers = await getEnabledMcpServers(state.projectPath);
192
- const serversByCategory = getMcpServersByCategory();
193
- // Search box - just shows hint
194
- blessed.box({
195
- parent: state.screen,
196
- top: 3,
197
- left: 2,
198
- width: '50%-3',
199
- height: 3,
200
- tags: true,
201
- border: { type: 'line' },
202
- style: { border: { fg: 'cyan' } },
203
- label: ' Search ',
204
- content: '{gray-fg}Press / to search...{/gray-fg}',
205
- });
206
- const listItems = [];
207
- for (const category of categoryOrder) {
208
- const servers = serversByCategory[category];
209
- if (!servers || servers.length === 0)
210
- continue;
211
- listItems.push({
212
- label: `{bold}{cyan-fg}${getCategoryDisplayName(category)}{/cyan-fg}{/bold}`,
213
- isCategory: true,
214
- });
215
- for (const server of servers) {
216
- const isInstalled = installedServers[server.name] !== undefined;
217
- const isEnabled = enabledServers[server.name] === true;
218
- let status = '{gray-fg}○{/gray-fg}';
219
- if (isInstalled && isEnabled) {
220
- status = '{green-fg}●{/green-fg}';
221
- }
222
- else if (isInstalled) {
223
- status = '{yellow-fg}●{/yellow-fg}';
224
- }
225
- const configTag = server.requiresConfig ? ' {yellow-fg}*{/yellow-fg}' : '';
226
- listItems.push({
227
- label: ` ${status} {bold}${server.name}{/bold}${configTag}`,
228
- server,
229
- });
230
- }
231
- }
232
- const list = blessed.list({
233
- parent: state.screen,
234
- top: 6,
235
- left: 2,
236
- width: '50%-3',
237
- height: '100%-9',
238
- items: listItems.map((item) => item.label),
239
- keys: true,
240
- mouse: true,
241
- tags: true,
242
- scrollable: true,
243
- border: { type: 'line' },
244
- style: {
245
- selected: { bg: 'blue', fg: 'white' },
246
- border: { fg: 'gray' },
247
- },
248
- scrollbar: { ch: '|', style: { bg: 'gray' } },
249
- });
250
- // Detail panel on the right
251
- const detailBox = blessed.box({
252
- parent: state.screen,
253
- top: 3,
254
- left: '50%',
255
- width: '50%-2',
256
- height: '100%-5',
257
- content: '',
258
- tags: true,
259
- border: {
260
- type: 'line',
261
- },
262
- style: {
263
- border: {
264
- fg: 'gray',
265
- },
266
- },
267
- label: ' Details ',
268
- });
269
- // Update detail panel on selection change
270
- const updateDetail = () => {
271
- const selected = list.selected;
272
- const item = listItems[selected];
273
- if (!item || item.isCategory || !item.server) {
274
- detailBox.setContent('{gray-fg}Select a server to see details{/gray-fg}');
275
- state.screen.render();
276
- return;
277
- }
278
- const server = item.server;
279
- const isInstalled = installedServers[server.name] !== undefined;
280
- const isEnabled = enabledServers[server.name] === true;
281
- let statusText = '{gray-fg}Not installed{/gray-fg}';
282
- if (isInstalled && isEnabled) {
283
- statusText = '{green-fg}● Installed & Enabled{/green-fg}';
284
- }
285
- else if (isInstalled) {
286
- statusText = '{yellow-fg}● Installed (disabled){/yellow-fg}';
287
- }
288
- let typeInfo = '';
289
- if (server.type === 'http') {
290
- typeInfo = `{bold}URL:{/bold} {cyan-fg}${server.url}{/cyan-fg}`;
291
- }
292
- else {
293
- typeInfo = `{bold}Command:{/bold} {cyan-fg}${server.command} ${(server.args || []).join(' ')}{/cyan-fg}`;
294
- }
295
- let configInfo = '';
296
- if (isInstalled) {
297
- // Show current configuration for installed servers
298
- const currentConfig = installedServers[server.name];
299
- if (currentConfig?.env && Object.keys(currentConfig.env).length > 0) {
300
- configInfo = `\n\n{bold}Current Configuration:{/bold}\n`;
301
- for (const [varName, varValue] of Object.entries(currentConfig.env)) {
302
- const isReference = varValue.startsWith('${') && varValue.endsWith('}');
303
- if (isReference) {
304
- // Extract var name from ${VAR} and check if set in environment
305
- const refVarName = varValue.slice(2, -1);
306
- const envValue = process.env[refVarName];
307
- const envStatus = envValue ? '{green-fg}[SET]{/green-fg}' : '{yellow-fg}[NOT SET]{/yellow-fg}';
308
- configInfo += ` {cyan-fg}${varName}{/cyan-fg}: {gray-fg}${varValue}{/gray-fg} ${envStatus}\n`;
309
- }
310
- else {
311
- // Hardcoded value - show masked
312
- const masked = varValue.length > 8 ? varValue.slice(0, 8) + '...' : varValue;
313
- configInfo += ` {cyan-fg}${varName}{/cyan-fg}: {gray-fg}${masked}{/gray-fg} {blue-fg}[HARDCODED]{/blue-fg}\n`;
314
- }
315
- }
316
- configInfo += `\n{gray-fg}Press 'e' to edit configuration{/gray-fg}`;
317
- }
318
- }
319
- else if (server.requiresConfig && server.configFields) {
320
- // Show required env vars for servers not yet installed
321
- configInfo = `\n\n{bold}Required Environment Variables:{/bold}\n`;
322
- for (const field of server.configFields) {
323
- const envVarName = field.envVar || field.name;
324
- const envValue = process.env[envVarName];
325
- const isSet = envValue !== undefined && envValue !== '';
326
- const req = field.required ? '{red-fg}*{/red-fg}' : '';
327
- const status = isSet
328
- ? '{green-fg}[SET]{/green-fg}'
329
- : '{yellow-fg}[NOT SET]{/yellow-fg}';
330
- configInfo += ` ${req} ${envVarName} ${status}\n`;
331
- configInfo += ` {gray-fg}${field.label}{/gray-fg}\n`;
332
- }
333
- }
334
- const actionHint = isInstalled
335
- ? '{gray-fg}Enter: Remove │ e: Edit Config{/gray-fg}'
336
- : '{green-fg}Press Enter to install{/green-fg}';
337
- const content = `
338
- {bold}{cyan-fg}${server.name}{/cyan-fg}{/bold}
339
-
340
- ${server.description}
341
-
342
- {bold}Status:{/bold} ${statusText}
343
-
344
- ${typeInfo}${configInfo}
345
-
346
- ${actionHint}
347
- `.trim();
348
- detailBox.setContent(content);
349
- state.screen.render();
350
- };
351
- list.on('select item', updateDetail);
352
- // Initial update
353
- setTimeout(updateDetail, 0);
354
- // Handle selection
355
- list.on('select', async (_item, index) => {
356
- const selected = listItems[index];
357
- if (!selected || selected.isCategory || !selected.server) {
358
- return;
359
- }
360
- const server = selected.server;
361
- const isInstalled = installedServers[server.name] !== undefined;
362
- if (isInstalled) {
363
- const remove = await showConfirm(state, `Remove ${server.name}?`, 'This will remove the MCP server configuration.');
364
- if (remove) {
365
- await removeMcpServer(server.name, state.projectPath);
366
- await showMessage(state, 'Removed', `${server.name} has been removed.`, 'success');
367
- createMcpScreen(state);
368
- }
369
- }
370
- else {
371
- await installMcpServer(state, server);
372
- }
373
- });
374
- // Manual j/k navigation (since vi mode is disabled)
375
- list.key(['j'], () => {
376
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
377
- list.down();
378
- state.screen.render();
379
- });
380
- list.key(['k'], () => {
381
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
382
- list.up();
383
- state.screen.render();
384
- });
385
- // Edit configuration with 'e' key
386
- list.key(['e'], async () => {
387
- const selected = list.selected;
388
- const item = listItems[selected];
389
- if (!item || item.isCategory || !item.server)
390
- return;
391
- const server = item.server;
392
- const isInstalled = installedServers[server.name] !== undefined;
393
- if (isInstalled) {
394
- await editMcpServerConfig(state, server, installedServers[server.name]);
395
- }
396
- else {
397
- await showMessage(state, 'Not Installed', 'Install the server first to configure it.', 'info');
398
- }
399
- });
400
- // Search with / key - opens search screen
401
- list.key(['/'], async () => {
402
- const query = await showInput(state, 'Search', 'Search MCP servers:');
403
- if (query !== null && query.trim()) {
404
- createMcpSearchScreen(state, query);
405
- }
406
- });
407
- // Switch to registry search with r
408
- list.key(['r'], async () => {
409
- const { navigateTo } = await import('../app.js');
410
- navigateTo(state, 'mcp-registry');
411
- });
412
- createFooter(state, '↑↓ Navigate │ Enter Install/Remove │ e Edit │ / Search │ r Registry │ q Back');
413
- list.focus();
414
- state.screen.render();
415
- }
416
- async function installMcpServer(state, server) {
417
- let config;
418
- if (server.type === 'http') {
419
- // HTTP-based MCP server
420
- config = {
421
- type: 'http',
422
- url: server.url,
423
- };
424
- }
425
- else {
426
- // Command-based MCP server
427
- config = {
428
- command: server.command,
429
- args: server.args ? [...server.args] : undefined,
430
- env: server.env ? { ...server.env } : undefined,
431
- };
432
- }
433
- // Collect configuration if required
434
- if (server.requiresConfig && server.configFields) {
435
- // Check which env vars are already set
436
- const envStatus = [];
437
- for (const field of server.configFields) {
438
- const envVarName = field.envVar || field.name;
439
- const existingValue = process.env[envVarName];
440
- envStatus.push({ field, existingValue });
441
- }
442
- // Check if all required env vars are set
443
- const missingRequired = envStatus.filter((e) => e.field.required && (!e.existingValue || e.existingValue === ''));
444
- const hasExistingVars = envStatus.some((e) => e.existingValue !== undefined && e.existingValue !== '');
445
- // If some env vars exist, ask user if they want to use them
446
- if (hasExistingVars && missingRequired.length === 0) {
447
- const useExisting = await showConfirm(state, 'Use Environment Variables?', 'All required environment variables are already set.\nUse values from your environment?');
448
- if (useExisting) {
449
- // Use existing env vars - reference them with ${VAR} syntax
450
- config.env = config.env || {};
451
- for (const { field } of envStatus) {
452
- const envVarName = field.envVar || field.name;
453
- config.env[envVarName] = `\${${envVarName}}`;
454
- }
455
- }
456
- else {
457
- // User wants to enter new values
458
- for (const { field, existingValue } of envStatus) {
459
- const envVarName = field.envVar || field.name;
460
- const hint = existingValue ? ` (current: ${existingValue.slice(0, 8)}...)` : '';
461
- const value = await showInput(state, `Configure ${server.name}`, `${field.label}${field.required ? ' (required)' : ''}${hint}:`, field.default);
462
- if (value === null) {
463
- return; // User cancelled
464
- }
465
- if (field.required && !value) {
466
- await showMessage(state, 'Required Field', `${field.label} is required.`, 'error');
467
- return;
468
- }
469
- if (value) {
470
- config.env = config.env || {};
471
- config.env[envVarName] = value;
472
- }
473
- }
474
- }
475
- }
476
- else {
477
- // Some required vars are missing - prompt for each
478
- if (missingRequired.length > 0) {
479
- const missingNames = missingRequired.map((e) => e.field.envVar || e.field.name).join(', ');
480
- await showMessage(state, 'Missing Environment Variables', `The following required variables are not set:\n${missingNames}\n\nYou can set them in your shell or enter values now.`, 'info');
481
- }
482
- for (const { field, existingValue } of envStatus) {
483
- const envVarName = field.envVar || field.name;
484
- const isSet = existingValue !== undefined && existingValue !== '';
485
- let defaultValue = field.default;
486
- let prompt = `${field.label}${field.required ? ' (required)' : ''}:`;
487
- if (isSet) {
488
- // Env var is set, offer to use it
489
- const useIt = await showConfirm(state, `Use ${envVarName}?`, `${envVarName} is set in your environment.\nUse the existing value?`);
490
- if (useIt) {
491
- config.env = config.env || {};
492
- config.env[envVarName] = `\${${envVarName}}`;
493
- continue;
494
- }
495
- prompt = `${field.label} (override existing):`;
496
- }
497
- const value = await showInput(state, `Configure ${server.name}`, prompt, defaultValue);
498
- if (value === null) {
499
- return; // User cancelled
500
- }
501
- if (field.required && !value) {
502
- await showMessage(state, 'Required Field', `${field.label} is required.`, 'error');
503
- return;
504
- }
505
- if (value) {
506
- // Replace placeholder in args
507
- if (config.args) {
508
- config.args = config.args.map((arg) => arg.replace(`\${${field.name}}`, value));
509
- }
510
- config.env = config.env || {};
511
- config.env[envVarName] = value;
512
- }
513
- }
514
- }
515
- }
516
- // Add server to .mcp.json
517
- await addMcpServer(server.name, config, state.projectPath);
518
- await showMessage(state, 'Installed', `${server.name} has been configured.\n\nRestart Claude Code to activate.`, 'success');
519
- createMcpScreen(state);
520
- }
521
- async function editMcpServerConfig(state, server, currentConfig) {
522
- // Get current env vars from config
523
- const currentEnv = currentConfig.env || {};
524
- const envVarNames = Object.keys(currentEnv);
525
- // Get configFields from curated server definition if available
526
- const allServers = getAllMcpServers();
527
- const curatedServer = allServers.find((s) => s.name === server.name);
528
- const configFields = curatedServer?.configFields || [];
529
- // Build list of all env vars (from current config + configFields)
530
- const allEnvVars = new Set(envVarNames);
531
- for (const field of configFields) {
532
- const envVarName = field.envVar || field.name;
533
- allEnvVars.add(envVarName);
534
- }
535
- if (allEnvVars.size === 0) {
536
- // No env vars to edit, offer to add new ones
537
- const addNew = await showConfirm(state, 'No Environment Variables', 'This server has no environment variables configured.\nWould you like to add one?');
538
- if (addNew) {
539
- await addNewEnvVar(state, server.name, currentConfig);
540
- }
541
- return;
542
- }
543
- // Let user choose what to do
544
- const action = await showSelect(state, `Configure ${server.name}`, 'What would you like to do?', [
545
- { label: 'Edit existing variables', value: 'edit' },
546
- { label: 'Add new variable', value: 'add' },
547
- { label: 'Cancel', value: 'cancel' },
548
- ]);
549
- if (action === null || action === 'cancel') {
550
- return;
551
- }
552
- if (action === 'add') {
553
- await addNewEnvVar(state, server.name, currentConfig);
554
- return;
555
- }
556
- // Edit existing variables
557
- const updatedEnv = { ...currentEnv };
558
- let modified = false;
559
- for (const envVarName of allEnvVars) {
560
- const currentValue = currentEnv[envVarName];
561
- const field = configFields.find((f) => (f.envVar || f.name) === envVarName);
562
- const fieldLabel = field?.label || envVarName;
563
- // Determine current value type
564
- const isReference = currentValue?.startsWith('${') && currentValue?.endsWith('}');
565
- const envValueFromShell = process.env[envVarName];
566
- const hasShellValue = envValueFromShell !== undefined && envValueFromShell !== '';
567
- // Build description of current state
568
- let currentDesc = 'Not configured';
569
- if (currentValue) {
570
- if (isReference) {
571
- const refStatus = hasShellValue ? 'set in environment' : 'NOT set in environment';
572
- currentDesc = `${currentValue} (${refStatus})`;
573
- }
574
- else {
575
- const masked = currentValue.length > 8 ? currentValue.slice(0, 8) + '...' : currentValue;
576
- currentDesc = `Hardcoded: ${masked}`;
577
- }
578
- }
579
- // Build options
580
- const options = [
581
- { label: `Keep current: ${currentDesc}`, value: 'keep' },
582
- ];
583
- if (hasShellValue) {
584
- options.push({
585
- label: `Use environment variable \${${envVarName}}`,
586
- value: 'env',
587
- });
588
- }
589
- options.push({ label: 'Enter new value', value: 'new' });
590
- if (currentValue) {
591
- options.push({ label: 'Remove this variable', value: 'remove' });
592
- }
593
- const choice = await showSelect(state, `Edit ${envVarName}`, `${fieldLabel}\n\nCurrent: ${currentDesc}`, options);
594
- if (choice === null) {
595
- // User cancelled - ask if they want to save partial changes
596
- if (modified) {
597
- const savePartial = await showConfirm(state, 'Save Changes?', 'You have unsaved changes. Save them?');
598
- if (savePartial) {
599
- break;
600
- }
601
- }
602
- return;
603
- }
604
- if (choice === 'keep') {
605
- // Keep current value
606
- continue;
607
- }
608
- else if (choice === 'env') {
609
- // Use environment variable reference
610
- updatedEnv[envVarName] = `\${${envVarName}}`;
611
- modified = true;
612
- }
613
- else if (choice === 'new') {
614
- // Enter new value
615
- const newValue = await showInput(state, `Set ${envVarName}`, `${fieldLabel}:`, '');
616
- if (newValue === null) {
617
- continue; // Skip this var
618
- }
619
- if (newValue) {
620
- updatedEnv[envVarName] = newValue;
621
- modified = true;
622
- }
623
- }
624
- else if (choice === 'remove') {
625
- delete updatedEnv[envVarName];
626
- modified = true;
627
- }
628
- }
629
- if (modified) {
630
- // Save updated config
631
- const newConfig = {
632
- ...currentConfig,
633
- env: Object.keys(updatedEnv).length > 0 ? updatedEnv : undefined,
634
- };
635
- await addMcpServer(server.name, newConfig, state.projectPath);
636
- await showMessage(state, 'Configuration Updated', `${server.name} configuration has been updated.\n\nRestart Claude Code to apply changes.`, 'success');
637
- }
638
- createMcpScreen(state);
639
- }
640
- async function addNewEnvVar(state, serverName, currentConfig) {
641
- // Get variable name
642
- const varName = await showInput(state, 'Add Environment Variable', 'Variable name (e.g., API_KEY):');
643
- if (varName === null || !varName.trim()) {
644
- return;
645
- }
646
- const cleanVarName = varName.trim().toUpperCase().replace(/[^A-Z0-9_]/g, '_');
647
- // Check if var exists in environment
648
- const envValue = process.env[cleanVarName];
649
- const hasEnvValue = envValue !== undefined && envValue !== '';
650
- let value = null;
651
- if (hasEnvValue) {
652
- const useEnv = await showConfirm(state, `${cleanVarName} Found`, `${cleanVarName} is set in your environment.\nUse the environment variable reference?`);
653
- if (useEnv) {
654
- value = `\${${cleanVarName}}`;
655
- }
656
- }
657
- if (value === null) {
658
- value = await showInput(state, `Set ${cleanVarName}`, 'Enter value:');
659
- if (value === null || !value) {
660
- return;
661
- }
662
- }
663
- // Update config
664
- const updatedEnv = { ...currentConfig.env, [cleanVarName]: value };
665
- const newConfig = {
666
- ...currentConfig,
667
- env: updatedEnv,
668
- };
669
- await addMcpServer(serverName, newConfig, state.projectPath);
670
- await showMessage(state, 'Variable Added', `${cleanVarName} has been added to ${serverName}.\n\nRestart Claude Code to apply changes.`, 'success');
671
- createMcpScreen(state);
672
- }
673
- //# sourceMappingURL=mcp-setup.js.map