upfynai-code 3.0.4 → 3.2.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.
- package/README.md +69 -92
- package/bin/cli.js +191 -0
- package/dist/client/assets/AppContent-M14Au3SB.js +542 -0
- package/{client/dist/assets/BrowserPanel-0TLEl-IC.js → dist/client/assets/BrowserPanel-TFKm2NDJ.js} +2 -2
- package/dist/client/assets/DashboardPanel-C88HjsCh.js +1 -0
- package/dist/client/assets/FileTree-DvO1xnDE.js +1 -0
- package/{client/dist/assets/GitPanel-C_xFM-N2.js → dist/client/assets/GitPanel-D-slVlyy.js} +2 -2
- package/dist/client/assets/LoginModal-Chi4SYcr.js +21 -0
- package/{client/dist/assets/MarkdownPreview-CESjI261.js → dist/client/assets/MarkdownPreview-CuIix2u9.js} +1 -1
- package/dist/client/assets/MermaidBlock-Dq9uFv82.js +2 -0
- package/dist/client/assets/Onboarding-QYXx24dX.js +1 -0
- package/{client/dist/assets/PreviewPanel-CqCa92Tf.js → dist/client/assets/PreviewPanel-Dd8q-jo0.js} +1 -1
- package/dist/client/assets/SetupForm-CrspaUva.js +1 -0
- package/dist/client/assets/WorkflowsPanel-DIlYAdhB.js +1 -0
- package/dist/client/assets/index-CnNNzw9A.css +1 -0
- package/{client/dist/assets/index-HaY-3pK1.js → dist/client/assets/index-rUkK9FDP.js} +26 -26
- package/{client/dist/assets/vendor-codemirror-D2ALgpaX.js → dist/client/assets/vendor-codemirror-jc6nyJQg.js} +1 -1
- package/{client/dist/assets/vendor-diff-DNQpbhrT.js → dist/client/assets/vendor-diff-THJmAcEI.js} +1 -1
- package/{client/dist/assets/vendor-icons-GyYE35HP.js → dist/client/assets/vendor-icons-CfjIpdrD.js} +145 -155
- package/{client/dist/assets/vendor-markdown-CimbIo6Y.js → dist/client/assets/vendor-markdown-Cdm6NEGf.js} +1 -1
- package/dist/client/assets/vendor-mermaid-DTPaBx-U.js +2559 -0
- package/{client/dist/assets/vendor-react-96lCPsRK.js → dist/client/assets/vendor-react-wFkb6mSf.js} +1 -1
- package/{client/dist/assets/vendor-syntax-LS_Nt30I.js → dist/client/assets/vendor-syntax-C_UZR7tc.js} +1 -1
- package/dist/client/favicon.png +0 -0
- package/dist/client/icons/icon-128x128.png +0 -0
- package/dist/client/icons/icon-144x144.png +0 -0
- package/dist/client/icons/icon-152x152.png +0 -0
- package/dist/client/icons/icon-192x192.png +0 -0
- package/dist/client/icons/icon-384x384.png +0 -0
- package/dist/client/icons/icon-512x512.png +0 -0
- package/dist/client/icons/icon-72x72.png +0 -0
- package/dist/client/icons/icon-96x96.png +0 -0
- package/{client/dist → dist/client}/index.html +37 -36
- package/dist/client/logo-128.png +0 -0
- package/dist/client/logo-256.png +0 -0
- package/dist/client/logo-32.png +0 -0
- package/dist/client/logo-512.png +0 -0
- package/dist/client/logo-64.png +0 -0
- package/dist/client/logo.png +0 -0
- package/{client/dist → dist/client}/manifest.json +12 -12
- package/{client/dist → dist/client}/mcp-docs.html +1 -1
- package/{client/dist → dist/client}/sw.js +2 -2
- package/package.json +56 -105
- package/scripts/postinstall.js +9 -0
- package/scripts/prepublish.js +77 -0
- package/src/animation.js +228 -0
- package/src/auth.js +142 -0
- package/src/config.js +40 -0
- package/src/connect.js +416 -0
- package/src/launch.js +81 -0
- package/src/mcp.js +57 -0
- package/src/permissions.js +140 -0
- package/src/persistent-shell.js +261 -0
- package/src/server.js +54 -0
- package/client/dist/assets/AppContent-CwrTP6TW.js +0 -545
- package/client/dist/assets/CanvasFullScreen-D1GWQsGL.js +0 -1
- package/client/dist/assets/CanvasWorkspace-D7ORj358.js +0 -163
- package/client/dist/assets/DashboardPanel-BV7ybUDe.js +0 -1
- package/client/dist/assets/FileTree-5qfhBqdE.js +0 -1
- package/client/dist/assets/LoginModal-CImJHRjX.js +0 -13
- package/client/dist/assets/MermaidBlock-BFM21cwe.js +0 -2
- package/client/dist/assets/Onboarding-B3cteLu2.js +0 -1
- package/client/dist/assets/SetupForm-P6dsYgHO.js +0 -1
- package/client/dist/assets/WorkflowsPanel-CBoN80kc.js +0 -1
- package/client/dist/assets/index-46kkVu2i.css +0 -1
- package/client/dist/assets/pdf-CE_K4jFx.js +0 -12
- package/client/dist/assets/vendor-canvas-BZV40eAE.css +0 -1
- package/client/dist/assets/vendor-canvas-DvHJ_Pn2.js +0 -49
- package/client/dist/assets/vendor-mermaid-DucWyDEe.js +0 -2556
- package/client/dist/favicon.png +0 -0
- package/client/dist/icons/icon-128x128.png +0 -0
- package/client/dist/icons/icon-144x144.png +0 -0
- package/client/dist/icons/icon-152x152.png +0 -0
- package/client/dist/icons/icon-192x192.png +0 -0
- package/client/dist/icons/icon-384x384.png +0 -0
- package/client/dist/icons/icon-512x512.png +0 -0
- package/client/dist/icons/icon-72x72.png +0 -0
- package/client/dist/icons/icon-96x96.png +0 -0
- package/client/dist/logo-128.png +0 -0
- package/client/dist/logo-256.png +0 -0
- package/client/dist/logo-32.png +0 -0
- package/client/dist/logo-512.png +0 -0
- package/client/dist/logo-64.png +0 -0
- package/commands/upfynai-connect.md +0 -59
- package/commands/upfynai-disconnect.md +0 -31
- package/commands/upfynai-doctor.md +0 -99
- package/commands/upfynai-export.md +0 -49
- package/commands/upfynai-local.md +0 -82
- package/commands/upfynai-status.md +0 -75
- package/commands/upfynai-stop.md +0 -49
- package/commands/upfynai-uninstall.md +0 -58
- package/commands/upfynai.md +0 -69
- package/scripts/build-client.js +0 -17
- package/scripts/fix-node-pty.js +0 -67
- package/scripts/install-commands.js +0 -78
- package/server/agent-loop.js +0 -242
- package/server/auto-compact.js +0 -99
- package/server/browser.js +0 -131
- package/server/claude-sdk.js +0 -797
- package/server/cli-ui.js +0 -798
- package/server/cli.js +0 -751
- package/server/constants/config.js +0 -31
- package/server/cursor-cli.js +0 -270
- package/server/database/auth.db +0 -0
- package/server/database/db.js +0 -1547
- package/server/database/init.sql +0 -70
- package/server/index.js +0 -3813
- package/server/load-env.js +0 -26
- package/server/mcp-server.js +0 -621
- package/server/middleware/auth.js +0 -184
- package/server/middleware/relayHelpers.js +0 -44
- package/server/middleware/sandboxRouter.js +0 -174
- package/server/openai-codex.js +0 -403
- package/server/openrouter.js +0 -137
- package/server/projects.js +0 -1807
- package/server/provider-factory.js +0 -174
- package/server/relay-client.js +0 -390
- package/server/routes/agent.js +0 -1234
- package/server/routes/auth.js +0 -559
- package/server/routes/browser.js +0 -419
- package/server/routes/canvas.js +0 -53
- package/server/routes/cli-auth.js +0 -263
- package/server/routes/codex.js +0 -396
- package/server/routes/commands.js +0 -707
- package/server/routes/composio.js +0 -176
- package/server/routes/cursor.js +0 -770
- package/server/routes/dashboard.js +0 -295
- package/server/routes/git.js +0 -1208
- package/server/routes/keys.js +0 -34
- package/server/routes/mcp-utils.js +0 -48
- package/server/routes/mcp.js +0 -661
- package/server/routes/payments.js +0 -227
- package/server/routes/projects.js +0 -754
- package/server/routes/sessions.js +0 -146
- package/server/routes/settings.js +0 -261
- package/server/routes/taskmaster.js +0 -1928
- package/server/routes/user.js +0 -106
- package/server/routes/vapi-chat.js +0 -624
- package/server/routes/voice.js +0 -235
- package/server/routes/webhooks.js +0 -166
- package/server/routes/workflows.js +0 -312
- package/server/sandbox.js +0 -120
- package/server/services/browser-ai.js +0 -154
- package/server/services/composio.js +0 -204
- package/server/services/sessionRegistry.js +0 -139
- package/server/services/whisperService.js +0 -84
- package/server/services/workflowScheduler.js +0 -211
- package/server/tests/relay-flow.test.js +0 -570
- package/server/tests/sessions.test.js +0 -259
- package/server/utils/commandParser.js +0 -303
- package/server/utils/email.js +0 -66
- package/server/utils/gitConfig.js +0 -24
- package/server/utils/mcp-detector.js +0 -198
- package/server/utils/taskmaster-websocket.js +0 -129
- package/shared/integrationCatalog.d.ts +0 -12
- package/shared/integrationCatalog.js +0 -172
- package/shared/modelConstants.js +0 -96
- /package/{shared → dist}/agents/claude.js +0 -0
- /package/{shared → dist}/agents/codex.js +0 -0
- /package/{shared → dist}/agents/cursor.js +0 -0
- /package/{shared → dist}/agents/detect.js +0 -0
- /package/{shared → dist}/agents/exec.js +0 -0
- /package/{shared → dist}/agents/files.js +0 -0
- /package/{shared → dist}/agents/git.js +0 -0
- /package/{shared → dist}/agents/gitagent.js +0 -0
- /package/{shared → dist}/agents/index.js +0 -0
- /package/{shared → dist}/agents/shell.js +0 -0
- /package/{shared → dist}/agents/utils.js +0 -0
- /package/{client/dist → dist/client}/api-docs.html +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- /package/{client/dist → dist/client}/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- /package/{client/dist → dist/client}/assets/vendor-i18n-DCFGyhQR.js +0 -0
- /package/{client/dist → dist/client}/assets/vendor-xterm-CZq1hqo1.js +0 -0
- /package/{client/dist → dist/client}/assets/vendor-xterm-qxJ8_QYu.css +0 -0
- /package/{client/dist → dist/client}/clear-cache.html +0 -0
- /package/{client/dist → dist/client}/convert-icons.md +0 -0
- /package/{client/dist → dist/client}/favicon.svg +0 -0
- /package/{client/dist → dist/client}/generate-icons.js +0 -0
- /package/{client/dist → dist/client}/icons/claude-ai-icon.svg +0 -0
- /package/{client/dist → dist/client}/icons/codex-white.svg +0 -0
- /package/{client/dist → dist/client}/icons/codex.svg +0 -0
- /package/{client/dist → dist/client}/icons/cursor-white.svg +0 -0
- /package/{client/dist → dist/client}/icons/cursor.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-128x128.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-144x144.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-152x152.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-192x192.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-384x384.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-512x512.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-72x72.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-96x96.svg +0 -0
- /package/{client/dist → dist/client}/icons/icon-template.svg +0 -0
- /package/{client/dist → dist/client}/logo.svg +0 -0
- /package/{client/dist → dist/client}/offline.html +0 -0
- /package/{client/dist → dist/client}/screenshots/cli-selection.png +0 -0
- /package/{client/dist → dist/client}/screenshots/desktop-main.png +0 -0
- /package/{client/dist → dist/client}/screenshots/mobile-chat.png +0 -0
- /package/{client/dist → dist/client}/screenshots/tools-modal.png +0 -0
- /package/{shared → dist}/gitagent/index.js +0 -0
- /package/{shared → dist}/gitagent/parser.js +0 -0
- /package/{shared → dist}/gitagent/prompt-builder.js +0 -0
package/server/routes/mcp.js
DELETED
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { promises as fs } from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
import { dirname } from 'path';
|
|
7
|
-
import { spawn } from 'child_process';
|
|
8
|
-
|
|
9
|
-
const router = express.Router();
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = dirname(__filename);
|
|
12
|
-
|
|
13
|
-
// Claude CLI command routes
|
|
14
|
-
|
|
15
|
-
// GET /api/mcp/cli/list - List MCP servers using Claude CLI
|
|
16
|
-
router.get('/cli/list', async (req, res) => {
|
|
17
|
-
try {
|
|
18
|
-
// Cloud mode: run claude CLI on user's machine via relay
|
|
19
|
-
if (req.isCloud) {
|
|
20
|
-
if (!req.requireRelay()) return;
|
|
21
|
-
try {
|
|
22
|
-
const result = await req.sendRelay('shell-command', { command: 'claude mcp list' }, 30000);
|
|
23
|
-
const output = result.stdout || result.output || '';
|
|
24
|
-
return res.json({ success: true, output, servers: parseClaudeListOutput(output) });
|
|
25
|
-
} catch (err) {
|
|
26
|
-
return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Local mode
|
|
31
|
-
const { spawn } = await import('child_process');
|
|
32
|
-
|
|
33
|
-
const process = spawn('claude', ['mcp', 'list'], {
|
|
34
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
let stdout = '';
|
|
38
|
-
let stderr = '';
|
|
39
|
-
|
|
40
|
-
process.stdout.on('data', (data) => {
|
|
41
|
-
stdout += data.toString();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
process.stderr.on('data', (data) => {
|
|
45
|
-
stderr += data.toString();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
process.on('close', (code) => {
|
|
49
|
-
if (code === 0) {
|
|
50
|
-
res.json({ success: true, output: stdout, servers: parseClaudeListOutput(stdout) });
|
|
51
|
-
} else {
|
|
52
|
-
// CLI error
|
|
53
|
-
res.status(500).json({ error: 'Claude CLI command failed', details: stderr });
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
process.on('error', (error) => {
|
|
58
|
-
// CLI run error
|
|
59
|
-
res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
|
|
60
|
-
});
|
|
61
|
-
} catch (error) {
|
|
62
|
-
// MCP list error
|
|
63
|
-
res.status(500).json({ error: 'Failed to list MCP servers', details: 'An error occurred' });
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// POST /api/mcp/cli/add - Add MCP server using Claude CLI
|
|
68
|
-
router.post('/cli/add', async (req, res) => {
|
|
69
|
-
try {
|
|
70
|
-
const { name, type = 'stdio', command, args = [], url, headers = {}, env = {}, scope = 'user', projectPath } = req.body;
|
|
71
|
-
|
|
72
|
-
// Cloud mode: run claude CLI on user's machine via relay
|
|
73
|
-
if (req.isCloud) {
|
|
74
|
-
if (!req.requireRelay()) return;
|
|
75
|
-
try {
|
|
76
|
-
let cliParts = ['claude', 'mcp', 'add', '--scope', scope];
|
|
77
|
-
if (type === 'http') {
|
|
78
|
-
cliParts.push('--transport', 'http', name, url);
|
|
79
|
-
Object.entries(headers).forEach(([key, value]) => { cliParts.push('--header', `${key}: ${value}`); });
|
|
80
|
-
} else if (type === 'sse') {
|
|
81
|
-
cliParts.push('--transport', 'sse', name, url);
|
|
82
|
-
Object.entries(headers).forEach(([key, value]) => { cliParts.push('--header', `${key}: ${value}`); });
|
|
83
|
-
} else {
|
|
84
|
-
cliParts.push(name);
|
|
85
|
-
Object.entries(env).forEach(([key, value]) => { cliParts.push('-e', `${key}=${value}`); });
|
|
86
|
-
cliParts.push(command);
|
|
87
|
-
if (args && args.length > 0) cliParts.push(...args);
|
|
88
|
-
}
|
|
89
|
-
const fullCmd = cliParts.map(a => (typeof a === 'string' && a.includes(' ')) ? `"${a}"` : a).join(' ');
|
|
90
|
-
const payload = { command: fullCmd };
|
|
91
|
-
if (scope === 'local' && projectPath) payload.cwd = projectPath;
|
|
92
|
-
const result = await req.sendRelay('shell-command', payload, 30000);
|
|
93
|
-
return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" added successfully` });
|
|
94
|
-
} catch (err) {
|
|
95
|
-
return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const { spawn } = await import('child_process');
|
|
100
|
-
|
|
101
|
-
let cliArgs = ['mcp', 'add'];
|
|
102
|
-
|
|
103
|
-
// Add scope flag
|
|
104
|
-
cliArgs.push('--scope', scope);
|
|
105
|
-
|
|
106
|
-
if (type === 'http') {
|
|
107
|
-
cliArgs.push('--transport', 'http', name, url);
|
|
108
|
-
// Add headers if provided
|
|
109
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
110
|
-
cliArgs.push('--header', `${key}: ${value}`);
|
|
111
|
-
});
|
|
112
|
-
} else if (type === 'sse') {
|
|
113
|
-
cliArgs.push('--transport', 'sse', name, url);
|
|
114
|
-
// Add headers if provided
|
|
115
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
116
|
-
cliArgs.push('--header', `${key}: ${value}`);
|
|
117
|
-
});
|
|
118
|
-
} else {
|
|
119
|
-
// stdio (default): claude mcp add --scope user <name> <command> [args...]
|
|
120
|
-
cliArgs.push(name);
|
|
121
|
-
// Add environment variables
|
|
122
|
-
Object.entries(env).forEach(([key, value]) => {
|
|
123
|
-
cliArgs.push('-e', `${key}=${value}`);
|
|
124
|
-
});
|
|
125
|
-
cliArgs.push(command);
|
|
126
|
-
if (args && args.length > 0) {
|
|
127
|
-
cliArgs.push(...args);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// For local scope, we need to run the command in the project directory
|
|
133
|
-
const spawnOptions = {
|
|
134
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
if (scope === 'local' && projectPath) {
|
|
138
|
-
spawnOptions.cwd = projectPath;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const process = spawn('claude', cliArgs, spawnOptions);
|
|
142
|
-
|
|
143
|
-
let stdout = '';
|
|
144
|
-
let stderr = '';
|
|
145
|
-
|
|
146
|
-
process.stdout.on('data', (data) => {
|
|
147
|
-
stdout += data.toString();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
process.stderr.on('data', (data) => {
|
|
151
|
-
stderr += data.toString();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
process.on('close', (code) => {
|
|
155
|
-
if (code === 0) {
|
|
156
|
-
res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully` });
|
|
157
|
-
} else {
|
|
158
|
-
// CLI error
|
|
159
|
-
res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
process.on('error', (error) => {
|
|
164
|
-
// CLI run error
|
|
165
|
-
res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
|
|
166
|
-
});
|
|
167
|
-
} catch (error) {
|
|
168
|
-
// MCP add error
|
|
169
|
-
res.status(500).json({ error: 'Failed to add MCP server', details: 'An error occurred' });
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// POST /api/mcp/cli/add-json - Add MCP server using JSON format
|
|
174
|
-
router.post('/cli/add-json', async (req, res) => {
|
|
175
|
-
try {
|
|
176
|
-
const { name, jsonConfig, scope = 'user', projectPath } = req.body;
|
|
177
|
-
|
|
178
|
-
// Validate and parse JSON config
|
|
179
|
-
let parsedConfig;
|
|
180
|
-
try {
|
|
181
|
-
parsedConfig = typeof jsonConfig === 'string' ? JSON.parse(jsonConfig) : jsonConfig;
|
|
182
|
-
} catch (parseError) {
|
|
183
|
-
return res.status(400).json({
|
|
184
|
-
error: 'Invalid JSON configuration',
|
|
185
|
-
details: parseError.message
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Validate required fields
|
|
190
|
-
if (!parsedConfig.type) {
|
|
191
|
-
return res.status(400).json({
|
|
192
|
-
error: 'Invalid configuration',
|
|
193
|
-
details: 'Missing required field: type'
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (parsedConfig.type === 'stdio' && !parsedConfig.command) {
|
|
198
|
-
return res.status(400).json({
|
|
199
|
-
error: 'Invalid configuration',
|
|
200
|
-
details: 'stdio type requires a command field'
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if ((parsedConfig.type === 'http' || parsedConfig.type === 'sse') && !parsedConfig.url) {
|
|
205
|
-
return res.status(400).json({
|
|
206
|
-
error: 'Invalid configuration',
|
|
207
|
-
details: `${parsedConfig.type} type requires a url field`
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Cloud mode: run claude CLI on user's machine via relay
|
|
212
|
-
if (req.isCloud) {
|
|
213
|
-
if (!req.requireRelay()) return;
|
|
214
|
-
try {
|
|
215
|
-
const jsonString = JSON.stringify(parsedConfig);
|
|
216
|
-
const fullCmd = `claude mcp add-json --scope ${scope} ${name} '${jsonString}'`;
|
|
217
|
-
const payload = { command: fullCmd };
|
|
218
|
-
if (scope === 'local' && projectPath) payload.cwd = projectPath;
|
|
219
|
-
const result = await req.sendRelay('shell-command', payload, 30000);
|
|
220
|
-
return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" added successfully via JSON` });
|
|
221
|
-
} catch (err) {
|
|
222
|
-
return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const { spawn } = await import('child_process');
|
|
227
|
-
|
|
228
|
-
// Build the command: claude mcp add-json --scope <scope> <name> '<json>'
|
|
229
|
-
const cliArgs = ['mcp', 'add-json', '--scope', scope, name];
|
|
230
|
-
|
|
231
|
-
// Add the JSON config as a properly formatted string
|
|
232
|
-
const jsonString = JSON.stringify(parsedConfig);
|
|
233
|
-
cliArgs.push(jsonString);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// For local scope, we need to run the command in the project directory
|
|
237
|
-
const spawnOptions = {
|
|
238
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
if (scope === 'local' && projectPath) {
|
|
242
|
-
spawnOptions.cwd = projectPath;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const process = spawn('claude', cliArgs, spawnOptions);
|
|
246
|
-
|
|
247
|
-
let stdout = '';
|
|
248
|
-
let stderr = '';
|
|
249
|
-
|
|
250
|
-
process.stdout.on('data', (data) => {
|
|
251
|
-
stdout += data.toString();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
process.stderr.on('data', (data) => {
|
|
255
|
-
stderr += data.toString();
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
process.on('close', (code) => {
|
|
259
|
-
if (code === 0) {
|
|
260
|
-
res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully via JSON` });
|
|
261
|
-
} else {
|
|
262
|
-
// CLI error
|
|
263
|
-
res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
process.on('error', (error) => {
|
|
268
|
-
// CLI run error
|
|
269
|
-
res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
|
|
270
|
-
});
|
|
271
|
-
} catch (error) {
|
|
272
|
-
// MCP JSON add error
|
|
273
|
-
res.status(500).json({ error: 'Failed to add MCP server', details: 'An error occurred' });
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// DELETE /api/mcp/cli/remove/:name - Remove MCP server using Claude CLI
|
|
278
|
-
router.delete('/cli/remove/:name', async (req, res) => {
|
|
279
|
-
try {
|
|
280
|
-
const { name } = req.params;
|
|
281
|
-
const { scope } = req.query;
|
|
282
|
-
|
|
283
|
-
// Handle the ID format (remove scope prefix if present)
|
|
284
|
-
let actualName = name;
|
|
285
|
-
let actualScope = scope;
|
|
286
|
-
|
|
287
|
-
if (name.includes(':')) {
|
|
288
|
-
const [prefix, serverName] = name.split(':');
|
|
289
|
-
actualName = serverName;
|
|
290
|
-
actualScope = actualScope || prefix;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Cloud mode: run claude CLI on user's machine via relay
|
|
294
|
-
if (req.isCloud) {
|
|
295
|
-
if (!req.requireRelay()) return;
|
|
296
|
-
try {
|
|
297
|
-
const scopeFlag = actualScope === 'local' ? '--scope local' : '--scope user';
|
|
298
|
-
const result = await req.sendRelay('shell-command', { command: `claude mcp remove ${scopeFlag} ${actualName}` }, 30000);
|
|
299
|
-
return res.json({ success: true, output: result.stdout || result.output || '', message: `MCP server "${name}" removed successfully` });
|
|
300
|
-
} catch (err) {
|
|
301
|
-
return res.status(500).json({ error: 'Claude CLI command failed via relay', details: err.message });
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const { spawn } = await import('child_process');
|
|
306
|
-
|
|
307
|
-
// Build command args based on scope
|
|
308
|
-
let cliArgs = ['mcp', 'remove'];
|
|
309
|
-
|
|
310
|
-
// Add scope flag if it's local scope
|
|
311
|
-
if (actualScope === 'local') {
|
|
312
|
-
cliArgs.push('--scope', 'local');
|
|
313
|
-
} else if (actualScope === 'user' || !actualScope) {
|
|
314
|
-
// User scope is default, but we can be explicit
|
|
315
|
-
cliArgs.push('--scope', 'user');
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
cliArgs.push(actualName);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const process = spawn('claude', cliArgs, {
|
|
322
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
let stdout = '';
|
|
326
|
-
let stderr = '';
|
|
327
|
-
|
|
328
|
-
process.stdout.on('data', (data) => {
|
|
329
|
-
stdout += data.toString();
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
process.stderr.on('data', (data) => {
|
|
333
|
-
stderr += data.toString();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
process.on('close', (code) => {
|
|
337
|
-
if (code === 0) {
|
|
338
|
-
res.json({ success: true, output: stdout, message: `MCP server "${name}" removed successfully` });
|
|
339
|
-
} else {
|
|
340
|
-
// CLI error
|
|
341
|
-
res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
process.on('error', (error) => {
|
|
346
|
-
// CLI run error
|
|
347
|
-
res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
|
|
348
|
-
});
|
|
349
|
-
} catch (error) {
|
|
350
|
-
// MCP remove error
|
|
351
|
-
res.status(500).json({ error: 'Failed to remove MCP server', details: 'An error occurred' });
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
// GET /api/mcp/cli/get/:name - Get MCP server details using Claude CLI
|
|
356
|
-
router.get('/cli/get/:name', async (req, res) => {
|
|
357
|
-
try {
|
|
358
|
-
const { name } = req.params;
|
|
359
|
-
|
|
360
|
-
// Cloud mode: run claude CLI on user's machine via relay
|
|
361
|
-
if (req.isCloud) {
|
|
362
|
-
if (!req.requireRelay()) return;
|
|
363
|
-
try {
|
|
364
|
-
const result = await req.sendRelay('shell-command', { command: `claude mcp get ${name}` }, 30000);
|
|
365
|
-
const output = result.stdout || result.output || '';
|
|
366
|
-
return res.json({ success: true, output, server: parseClaudeGetOutput(output) });
|
|
367
|
-
} catch (err) {
|
|
368
|
-
return res.status(404).json({ error: 'Claude CLI command failed via relay', details: err.message });
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const { spawn } = await import('child_process');
|
|
373
|
-
|
|
374
|
-
const process = spawn('claude', ['mcp', 'get', name], {
|
|
375
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
let stdout = '';
|
|
379
|
-
let stderr = '';
|
|
380
|
-
|
|
381
|
-
process.stdout.on('data', (data) => {
|
|
382
|
-
stdout += data.toString();
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
process.stderr.on('data', (data) => {
|
|
386
|
-
stderr += data.toString();
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
process.on('close', (code) => {
|
|
390
|
-
if (code === 0) {
|
|
391
|
-
res.json({ success: true, output: stdout, server: parseClaudeGetOutput(stdout) });
|
|
392
|
-
} else {
|
|
393
|
-
// CLI error
|
|
394
|
-
res.status(404).json({ error: 'Claude CLI command failed', details: stderr });
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
process.on('error', (error) => {
|
|
399
|
-
// CLI run error
|
|
400
|
-
res.status(500).json({ error: 'Failed to run Claude CLI', details: 'An error occurred' });
|
|
401
|
-
});
|
|
402
|
-
} catch (error) {
|
|
403
|
-
// MCP CLI details error
|
|
404
|
-
res.status(500).json({ error: 'Failed to get MCP server details', details: 'An error occurred' });
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// GET /api/mcp/config/read - Read MCP servers directly from Claude config files
|
|
409
|
-
router.get('/config/read', async (req, res) => {
|
|
410
|
-
try {
|
|
411
|
-
// Cloud mode: read config from user's machine via relay
|
|
412
|
-
if (req.isCloud) {
|
|
413
|
-
if (!req.requireRelay()) return;
|
|
414
|
-
const configFiles = ['~/.claude.json', '~/.claude/settings.json'];
|
|
415
|
-
let configData = null;
|
|
416
|
-
let configPath = null;
|
|
417
|
-
|
|
418
|
-
for (const filePath of configFiles) {
|
|
419
|
-
try {
|
|
420
|
-
const result = await req.sendRelay('file-read', { filePath }, 15000);
|
|
421
|
-
configData = JSON.parse(result.content);
|
|
422
|
-
configPath = filePath;
|
|
423
|
-
break;
|
|
424
|
-
} catch { /* try next */ }
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (!configData) {
|
|
428
|
-
return res.json({ success: true, configPath: null, servers: [] });
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const servers = [];
|
|
432
|
-
if (configData.mcpServers && typeof configData.mcpServers === 'object') {
|
|
433
|
-
for (const [name, config] of Object.entries(configData.mcpServers)) {
|
|
434
|
-
const server = { id: name, name, type: 'stdio', scope: 'user', config: {}, raw: config };
|
|
435
|
-
if (config.command) {
|
|
436
|
-
server.type = 'stdio';
|
|
437
|
-
server.config = { command: config.command, args: config.args || [], env: config.env || {} };
|
|
438
|
-
} else if (config.url) {
|
|
439
|
-
server.type = config.transport || 'http';
|
|
440
|
-
server.config = { url: config.url, headers: config.headers || {} };
|
|
441
|
-
}
|
|
442
|
-
servers.push(server);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
// Check for project-scoped MCP servers if projectPath is provided
|
|
446
|
-
const cloudProjectPath = req.query.projectPath;
|
|
447
|
-
if (cloudProjectPath && configData.projects && typeof configData.projects === 'object') {
|
|
448
|
-
const projectConfig = configData.projects[cloudProjectPath];
|
|
449
|
-
if (projectConfig?.mcpServers && typeof projectConfig.mcpServers === 'object') {
|
|
450
|
-
for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
|
|
451
|
-
const server = { id: `project:${name}`, name, type: 'stdio', scope: 'project', projectPath: cloudProjectPath, config: {}, raw: config };
|
|
452
|
-
if (config.command) {
|
|
453
|
-
server.type = 'stdio';
|
|
454
|
-
server.config = { command: config.command, args: config.args || [], env: config.env || {} };
|
|
455
|
-
} else if (config.url) {
|
|
456
|
-
server.type = config.transport || 'http';
|
|
457
|
-
server.config = { url: config.url, headers: config.headers || {} };
|
|
458
|
-
}
|
|
459
|
-
servers.push(server);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return res.json({ success: true, configPath, servers });
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const homeDir = os.homedir();
|
|
468
|
-
const configPaths = [
|
|
469
|
-
path.join(homeDir, '.claude.json'),
|
|
470
|
-
path.join(homeDir, '.claude', 'settings.json')
|
|
471
|
-
];
|
|
472
|
-
|
|
473
|
-
let configData = null;
|
|
474
|
-
let configPath = null;
|
|
475
|
-
|
|
476
|
-
// Try to read from either config file
|
|
477
|
-
for (const filepath of configPaths) {
|
|
478
|
-
try {
|
|
479
|
-
const fileContent = await fs.readFile(filepath, 'utf8');
|
|
480
|
-
configData = JSON.parse(fileContent);
|
|
481
|
-
configPath = filepath;
|
|
482
|
-
break;
|
|
483
|
-
} catch (error) {
|
|
484
|
-
// File doesn't exist or is not valid JSON, try next
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (!configData) {
|
|
489
|
-
return res.json({
|
|
490
|
-
success: false,
|
|
491
|
-
message: 'No Claude configuration file found',
|
|
492
|
-
servers: []
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Extract MCP servers from the config
|
|
497
|
-
const servers = [];
|
|
498
|
-
|
|
499
|
-
// Check for user-scoped MCP servers (at root level)
|
|
500
|
-
if (configData.mcpServers && typeof configData.mcpServers === 'object' && Object.keys(configData.mcpServers).length > 0) {
|
|
501
|
-
for (const [name, config] of Object.entries(configData.mcpServers)) {
|
|
502
|
-
const server = {
|
|
503
|
-
id: name,
|
|
504
|
-
name: name,
|
|
505
|
-
type: 'stdio', // Default type
|
|
506
|
-
scope: 'user', // User scope - available across all projects
|
|
507
|
-
config: {},
|
|
508
|
-
raw: config // Include raw config for full details
|
|
509
|
-
};
|
|
510
|
-
|
|
511
|
-
// Determine transport type and extract config
|
|
512
|
-
if (config.command) {
|
|
513
|
-
server.type = 'stdio';
|
|
514
|
-
server.config.command = config.command;
|
|
515
|
-
server.config.args = config.args || [];
|
|
516
|
-
server.config.env = config.env || {};
|
|
517
|
-
} else if (config.url) {
|
|
518
|
-
server.type = config.transport || 'http';
|
|
519
|
-
server.config.url = config.url;
|
|
520
|
-
server.config.headers = config.headers || {};
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
servers.push(server);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Check for local-scoped MCP servers (project-specific)
|
|
528
|
-
// Use provided projectPath query param, or fall back to cwd
|
|
529
|
-
const currentProjectPath = req.query.projectPath || process.cwd();
|
|
530
|
-
|
|
531
|
-
// Check under 'projects' key
|
|
532
|
-
if (configData.projects && configData.projects[currentProjectPath]) {
|
|
533
|
-
const projectConfig = configData.projects[currentProjectPath];
|
|
534
|
-
if (projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object' && Object.keys(projectConfig.mcpServers).length > 0) {
|
|
535
|
-
for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
|
|
536
|
-
const server = {
|
|
537
|
-
id: `local:${name}`, // Prefix with scope for uniqueness
|
|
538
|
-
name: name, // Keep original name
|
|
539
|
-
type: 'stdio', // Default type
|
|
540
|
-
scope: 'local', // Local scope - only for this project
|
|
541
|
-
projectPath: currentProjectPath,
|
|
542
|
-
config: {},
|
|
543
|
-
raw: config // Include raw config for full details
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
// Determine transport type and extract config
|
|
547
|
-
if (config.command) {
|
|
548
|
-
server.type = 'stdio';
|
|
549
|
-
server.config.command = config.command;
|
|
550
|
-
server.config.args = config.args || [];
|
|
551
|
-
server.config.env = config.env || {};
|
|
552
|
-
} else if (config.url) {
|
|
553
|
-
server.type = config.transport || 'http';
|
|
554
|
-
server.config.url = config.url;
|
|
555
|
-
server.config.headers = config.headers || {};
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
servers.push(server);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
res.json({
|
|
565
|
-
success: true,
|
|
566
|
-
configPath: configPath,
|
|
567
|
-
servers: servers
|
|
568
|
-
});
|
|
569
|
-
} catch (error) {
|
|
570
|
-
res.status(500).json({
|
|
571
|
-
error: 'Failed to read Claude configuration',
|
|
572
|
-
details: 'An error occurred'
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
// Helper functions to parse Claude CLI output
|
|
578
|
-
function parseClaudeListOutput(output) {
|
|
579
|
-
const servers = [];
|
|
580
|
-
const lines = output.split('\n').filter(line => line.trim());
|
|
581
|
-
|
|
582
|
-
for (const line of lines) {
|
|
583
|
-
// Skip the header line
|
|
584
|
-
if (line.includes('Checking MCP server health')) continue;
|
|
585
|
-
|
|
586
|
-
// Parse lines like "test: test test - ✗ Failed to connect"
|
|
587
|
-
// or "server-name: command or description - ✓ Connected"
|
|
588
|
-
if (line.includes(':')) {
|
|
589
|
-
const colonIndex = line.indexOf(':');
|
|
590
|
-
const name = line.substring(0, colonIndex).trim();
|
|
591
|
-
|
|
592
|
-
// Skip empty names
|
|
593
|
-
if (!name) continue;
|
|
594
|
-
|
|
595
|
-
// Extract the rest after the name
|
|
596
|
-
const rest = line.substring(colonIndex + 1).trim();
|
|
597
|
-
|
|
598
|
-
// Try to extract description and status
|
|
599
|
-
let description = rest;
|
|
600
|
-
let status = 'unknown';
|
|
601
|
-
let type = 'stdio'; // default type
|
|
602
|
-
|
|
603
|
-
// Check for status indicators
|
|
604
|
-
if (rest.includes('✓') || rest.includes('✗')) {
|
|
605
|
-
const statusMatch = rest.match(/(.*?)\s*-\s*([✓✗].*)$/);
|
|
606
|
-
if (statusMatch) {
|
|
607
|
-
description = statusMatch[1].trim();
|
|
608
|
-
status = statusMatch[2].includes('✓') ? 'connected' : 'failed';
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// Try to determine type from description
|
|
613
|
-
if (description.startsWith('http://') || description.startsWith('https://')) {
|
|
614
|
-
type = 'http';
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
servers.push({
|
|
618
|
-
name,
|
|
619
|
-
type,
|
|
620
|
-
status: status || 'active',
|
|
621
|
-
description
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
return servers;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function parseClaudeGetOutput(output) {
|
|
630
|
-
// Parse the output from 'claude mcp get <name>' command
|
|
631
|
-
// This is a simple parser - might need adjustment based on actual output format
|
|
632
|
-
try {
|
|
633
|
-
// Try to extract JSON if present
|
|
634
|
-
const jsonMatch = output.match(/\{[\s\S]*\}/);
|
|
635
|
-
if (jsonMatch) {
|
|
636
|
-
return JSON.parse(jsonMatch[0]);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Otherwise, parse as text
|
|
640
|
-
const server = { raw_output: output };
|
|
641
|
-
const lines = output.split('\n');
|
|
642
|
-
|
|
643
|
-
for (const line of lines) {
|
|
644
|
-
if (line.includes('Name:')) {
|
|
645
|
-
server.name = line.split(':')[1]?.trim();
|
|
646
|
-
} else if (line.includes('Type:')) {
|
|
647
|
-
server.type = line.split(':')[1]?.trim();
|
|
648
|
-
} else if (line.includes('Command:')) {
|
|
649
|
-
server.command = line.split(':')[1]?.trim();
|
|
650
|
-
} else if (line.includes('URL:')) {
|
|
651
|
-
server.url = line.split(':')[1]?.trim();
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
return server;
|
|
656
|
-
} catch (error) {
|
|
657
|
-
return { raw_output: output, parse_error: error.message };
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
export default router;
|