dr-claw 1.1.2

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 (287) hide show
  1. package/LICENSE +1380 -0
  2. package/NOTICE +17 -0
  3. package/README.md +875 -0
  4. package/README.zh-CN.md +857 -0
  5. package/dist/api-docs.html +878 -0
  6. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  7. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  8. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  9. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  10. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  11. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  12. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  13. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  14. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  15. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  16. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  17. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  18. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  19. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  20. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  21. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  22. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  23. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  24. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  25. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  26. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  27. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  28. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  29. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  30. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  31. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  32. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  33. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  34. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  35. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  36. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  37. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  38. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  39. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  40. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  41. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  42. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  43. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  44. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  45. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  48. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  49. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  50. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  51. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  52. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  53. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  54. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  55. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  56. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  57. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  58. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  59. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  60. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  61. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  62. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  63. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  64. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  65. package/dist/assets/_basePickBy-C0n1uo4u.js +1 -0
  66. package/dist/assets/_baseUniq-BRNmhFkc.js +1 -0
  67. package/dist/assets/arc-DOMBmC_A.js +1 -0
  68. package/dist/assets/architectureDiagram-VXUJARFQ-CtMWglbD.js +36 -0
  69. package/dist/assets/blockDiagram-VD42YOAC-DK_gTkJy.js +122 -0
  70. package/dist/assets/c4Diagram-YG6GDRKO-DrDAvsn7.js +10 -0
  71. package/dist/assets/channel-BFOE7OyC.js +1 -0
  72. package/dist/assets/chunk-4BX2VUAB-CGbhdZcN.js +1 -0
  73. package/dist/assets/chunk-55IACEB6-sGeFSxen.js +1 -0
  74. package/dist/assets/chunk-B4BG7PRW-RxgHAU_P.js +165 -0
  75. package/dist/assets/chunk-DI55MBZ5-CvHUXZrx.js +220 -0
  76. package/dist/assets/chunk-FMBD7UC4-Sn7cXVNU.js +15 -0
  77. package/dist/assets/chunk-QN33PNHL-bDKOHppN.js +1 -0
  78. package/dist/assets/chunk-QZHKN3VN-B9kN5LGm.js +1 -0
  79. package/dist/assets/chunk-TZMSLE5B-CCBe6len.js +1 -0
  80. package/dist/assets/classDiagram-2ON5EDUG-DQoMNYlY.js +1 -0
  81. package/dist/assets/classDiagram-v2-WZHVMYZB-DQoMNYlY.js +1 -0
  82. package/dist/assets/clone-BulQSblH.js +1 -0
  83. package/dist/assets/cose-bilkent-S5V4N54A-r6sLiHBW.js +1 -0
  84. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  85. package/dist/assets/dagre-6UL2VRFP-AbsaR1Oa.js +4 -0
  86. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  87. package/dist/assets/diagram-PSM6KHXK-DjCvdUyy.js +24 -0
  88. package/dist/assets/diagram-QEK2KX5R-CqiocXkH.js +43 -0
  89. package/dist/assets/diagram-S2PKOQOG-D5ALeoII.js +24 -0
  90. package/dist/assets/erDiagram-Q2GNP2WA-C1AeOsiz.js +60 -0
  91. package/dist/assets/flowDiagram-NV44I4VS-C98ve_DO.js +162 -0
  92. package/dist/assets/ganttDiagram-JELNMOA3-3zdLvcxD.js +267 -0
  93. package/dist/assets/gitGraphDiagram-V2S2FVAM-nDuDMsHh.js +65 -0
  94. package/dist/assets/graph-Blm-emkR.js +1 -0
  95. package/dist/assets/index-B8Yutsnm.css +1 -0
  96. package/dist/assets/index-Cf6gyhc3.js +654 -0
  97. package/dist/assets/infoDiagram-HS3SLOUP-CmzE3q0R.js +2 -0
  98. package/dist/assets/init-Gi6I4Gst.js +1 -0
  99. package/dist/assets/journeyDiagram-XKPGCS4Q-CYaW7LrP.js +139 -0
  100. package/dist/assets/kanban-definition-3W4ZIXB7-mkmW7p0j.js +89 -0
  101. package/dist/assets/layout-Ct2kFIId.js +1 -0
  102. package/dist/assets/linear-CKG9EwIJ.js +1 -0
  103. package/dist/assets/mermaid.core-CaKcUnnd.js +250 -0
  104. package/dist/assets/mindmap-definition-VGOIOE7T-Bct-pYin.js +68 -0
  105. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  106. package/dist/assets/pieDiagram-ADFJNKIX-C8wFD4Jr.js +30 -0
  107. package/dist/assets/quadrantDiagram-AYHSOK5B-CuiE_f46.js +7 -0
  108. package/dist/assets/requirementDiagram-UZGBJVZJ-C4v1CqU8.js +64 -0
  109. package/dist/assets/sankeyDiagram-TZEHDZUN-keAqqs-Y.js +10 -0
  110. package/dist/assets/sequenceDiagram-WL72ISMW-ClNbvKTB.js +145 -0
  111. package/dist/assets/stateDiagram-FKZM4ZOC-D8We-2vq.js +1 -0
  112. package/dist/assets/stateDiagram-v2-4FDKWEC3-BUURfjrE.js +1 -0
  113. package/dist/assets/timeline-definition-IT6M3QCI-DsvEMT5T.js +61 -0
  114. package/dist/assets/treemap-GDKQZRPO-TJrzN7Bj.js +162 -0
  115. package/dist/assets/vendor-codemirror-ansM-eig.js +33 -0
  116. package/dist/assets/vendor-react-BWF6YQQ3.js +8 -0
  117. package/dist/assets/vendor-xterm-DfcmCpbH.js +66 -0
  118. package/dist/assets/xychartDiagram-PRI3JC2R-BdATmY5F.js +7 -0
  119. package/dist/clear-cache.html +85 -0
  120. package/dist/convert-icons.md +53 -0
  121. package/dist/dr-claw.png +0 -0
  122. package/dist/favicon.png +0 -0
  123. package/dist/generate-icons.js +49 -0
  124. package/dist/icons/claude-ai-icon.svg +1 -0
  125. package/dist/icons/codex-white.svg +3 -0
  126. package/dist/icons/codex.svg +3 -0
  127. package/dist/icons/cursor-white.svg +12 -0
  128. package/dist/icons/cursor.svg +1 -0
  129. package/dist/icons/file.svg +16997 -0
  130. package/dist/icons/generate-icons.md +19 -0
  131. package/dist/icons/icon-128x128.png +0 -0
  132. package/dist/icons/icon-144x144.png +0 -0
  133. package/dist/icons/icon-152x152.png +0 -0
  134. package/dist/icons/icon-192x192.png +0 -0
  135. package/dist/icons/icon-384x384.png +0 -0
  136. package/dist/icons/icon-512x512.png +0 -0
  137. package/dist/icons/icon-72x72.png +0 -0
  138. package/dist/icons/icon-96x96.png +0 -0
  139. package/dist/icons/news/arxiv.svg +1 -0
  140. package/dist/icons/news/huggingface.svg +8 -0
  141. package/dist/icons/news/x-black.png +0 -0
  142. package/dist/icons/news/x-white.png +0 -0
  143. package/dist/icons/news/xiaohongshu.png +0 -0
  144. package/dist/index.html +51 -0
  145. package/dist/leveraged-cognition.png +0 -0
  146. package/dist/manifest.json +61 -0
  147. package/dist/screenshots/chat.png +0 -0
  148. package/dist/screenshots/new_claw.png +0 -0
  149. package/dist/screenshots/news_dashboard.png +0 -0
  150. package/dist/screenshots/project_dashboard.png +0 -0
  151. package/dist/screenshots/research_lab.png +0 -0
  152. package/dist/screenshots/skill_library.png +0 -0
  153. package/dist/screenshots/task_list.png +0 -0
  154. package/dist/screenshots/terminal_example.png +0 -0
  155. package/dist/screenshots/terminal_example1.png +0 -0
  156. package/dist/sw.js +58 -0
  157. package/dist/wechat-group-qr.jpg +0 -0
  158. package/package.json +133 -0
  159. package/public/api-docs.html +878 -0
  160. package/public/clear-cache.html +85 -0
  161. package/public/convert-icons.md +53 -0
  162. package/public/dr-claw.png +0 -0
  163. package/public/favicon.png +0 -0
  164. package/public/generate-icons.js +49 -0
  165. package/public/icons/claude-ai-icon.svg +1 -0
  166. package/public/icons/codex-white.svg +3 -0
  167. package/public/icons/codex.svg +3 -0
  168. package/public/icons/cursor-white.svg +12 -0
  169. package/public/icons/cursor.svg +1 -0
  170. package/public/icons/file.svg +16997 -0
  171. package/public/icons/generate-icons.md +19 -0
  172. package/public/icons/icon-128x128.png +0 -0
  173. package/public/icons/icon-144x144.png +0 -0
  174. package/public/icons/icon-152x152.png +0 -0
  175. package/public/icons/icon-192x192.png +0 -0
  176. package/public/icons/icon-384x384.png +0 -0
  177. package/public/icons/icon-512x512.png +0 -0
  178. package/public/icons/icon-72x72.png +0 -0
  179. package/public/icons/icon-96x96.png +0 -0
  180. package/public/icons/news/arxiv.svg +1 -0
  181. package/public/icons/news/huggingface.svg +8 -0
  182. package/public/icons/news/x-black.png +0 -0
  183. package/public/icons/news/x-white.png +0 -0
  184. package/public/icons/news/xiaohongshu.png +0 -0
  185. package/public/leveraged-cognition.png +0 -0
  186. package/public/manifest.json +61 -0
  187. package/public/screenshots/chat.png +0 -0
  188. package/public/screenshots/new_claw.png +0 -0
  189. package/public/screenshots/news_dashboard.png +0 -0
  190. package/public/screenshots/project_dashboard.png +0 -0
  191. package/public/screenshots/research_lab.png +0 -0
  192. package/public/screenshots/skill_library.png +0 -0
  193. package/public/screenshots/task_list.png +0 -0
  194. package/public/screenshots/terminal_example.png +0 -0
  195. package/public/screenshots/terminal_example1.png +0 -0
  196. package/public/sw.js +58 -0
  197. package/public/wechat-group-qr.jpg +0 -0
  198. package/scripts/export-skills-catalog-v2.mjs +746 -0
  199. package/scripts/fix-node-pty.js +45 -0
  200. package/scripts/gemini_image_core.py +470 -0
  201. package/scripts/install-python-tools.js +64 -0
  202. package/server/AGENTS.md +73 -0
  203. package/server/__tests__/gemini-legacy-thought.test.mjs +88 -0
  204. package/server/__tests__/runtimePorts.test.mjs +115 -0
  205. package/server/__tests__/session-delete.test.mjs +166 -0
  206. package/server/bin/compute-node +70 -0
  207. package/server/claude-sdk.js +799 -0
  208. package/server/cli-chat.js +488 -0
  209. package/server/cli.js +354 -0
  210. package/server/compute-node.js +439 -0
  211. package/server/constants/config.js +5 -0
  212. package/server/cursor-cli.js +340 -0
  213. package/server/database/auth.db +0 -0
  214. package/server/database/db.js +1852 -0
  215. package/server/database/init.sql +194 -0
  216. package/server/gemini-cli.js +1352 -0
  217. package/server/index.js +3154 -0
  218. package/server/load-env.js +71 -0
  219. package/server/local-gpu.js +860 -0
  220. package/server/middleware/auth.js +100 -0
  221. package/server/openai-codex.js +724 -0
  222. package/server/openrouter.js +890 -0
  223. package/server/project-token-usage.js +407 -0
  224. package/server/projects.js +3973 -0
  225. package/server/routes/agent.js +1272 -0
  226. package/server/routes/auth.js +128 -0
  227. package/server/routes/auto-research.js +558 -0
  228. package/server/routes/cli-auth.js +958 -0
  229. package/server/routes/codex.js +344 -0
  230. package/server/routes/commands.js +601 -0
  231. package/server/routes/compute.js +358 -0
  232. package/server/routes/cursor.js +795 -0
  233. package/server/routes/git.js +1348 -0
  234. package/server/routes/mcp-utils.js +48 -0
  235. package/server/routes/mcp.js +618 -0
  236. package/server/routes/news.js +711 -0
  237. package/server/routes/projects.js +633 -0
  238. package/server/routes/references.js +338 -0
  239. package/server/routes/settings.js +285 -0
  240. package/server/routes/skills.js +914 -0
  241. package/server/routes/taskmaster.js +2566 -0
  242. package/server/routes/telemetry.js +89 -0
  243. package/server/routes/user.js +155 -0
  244. package/server/scripts/fix_session_names.js +57 -0
  245. package/server/scripts/research-news/common_words.py +19 -0
  246. package/server/scripts/research-news/link_keywords.py +298 -0
  247. package/server/scripts/research-news/scan_existing_notes.py +266 -0
  248. package/server/scripts/research-news/scoring_utils.py +227 -0
  249. package/server/scripts/research-news/search_arxiv.py +842 -0
  250. package/server/scripts/research-news/search_huggingface.py +438 -0
  251. package/server/scripts/research-news/search_x.py +490 -0
  252. package/server/scripts/research-news/search_xiaohongshu.py +610 -0
  253. package/server/taskmaster-templates/README.md +150 -0
  254. package/server/taskmaster-templates/README.zh-CN.md +150 -0
  255. package/server/taskmaster-templates/ai-research-dataset.json +230 -0
  256. package/server/taskmaster-templates/ai-research-method-model.json +230 -0
  257. package/server/taskmaster-templates/ai-research-position-paper.json +230 -0
  258. package/server/telemetry.js +121 -0
  259. package/server/templates/AGENTS.md +103 -0
  260. package/server/templates/CLAUDE.md +104 -0
  261. package/server/templates/GEMINI.md +103 -0
  262. package/server/templates/cursor-project.md +108 -0
  263. package/server/templates/index.js +37 -0
  264. package/server/utils/__tests__/sessionTokenUsage.test.js +82 -0
  265. package/server/utils/auto-research-mailer.js +57 -0
  266. package/server/utils/cliResolution.js +170 -0
  267. package/server/utils/commandParser.js +303 -0
  268. package/server/utils/cursorCommand.js +61 -0
  269. package/server/utils/geminiApiKey.js +28 -0
  270. package/server/utils/gitConfig.js +24 -0
  271. package/server/utils/imageAttachmentFiles.js +35 -0
  272. package/server/utils/mcp-detector.js +198 -0
  273. package/server/utils/parsers/bibtex-parser.js +207 -0
  274. package/server/utils/permissions.js +100 -0
  275. package/server/utils/runtimePorts.js +212 -0
  276. package/server/utils/sessionFormatting.js +68 -0
  277. package/server/utils/sessionIndex.js +100 -0
  278. package/server/utils/sessionMode.js +95 -0
  279. package/server/utils/sessionTokenUsage.js +105 -0
  280. package/server/utils/taskmaster-websocket.js +129 -0
  281. package/server/utils/zotero-client.js +136 -0
  282. package/shared/errorClassifier.js +102 -0
  283. package/shared/geminiThinkingSupport.d.ts +22 -0
  284. package/shared/geminiThinkingSupport.js +96 -0
  285. package/shared/geminiThoughtParser.d.ts +8 -0
  286. package/shared/geminiThoughtParser.js +30 -0
  287. package/shared/modelConstants.js +139 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * MCP UTILITIES API ROUTES
3
+ * ========================
4
+ *
5
+ * API endpoints for MCP server detection and configuration utilities.
6
+ * These endpoints expose centralized MCP detection functionality.
7
+ */
8
+
9
+ import express from 'express';
10
+ import { detectTaskMasterMCPServer, getAllMCPServers } from '../utils/mcp-detector.js';
11
+
12
+ const router = express.Router();
13
+
14
+ /**
15
+ * GET /api/mcp-utils/taskmaster-server
16
+ * Check if TaskMaster MCP server is configured
17
+ */
18
+ router.get('/taskmaster-server', async (req, res) => {
19
+ try {
20
+ const result = await detectTaskMasterMCPServer();
21
+ res.json(result);
22
+ } catch (error) {
23
+ console.error('TaskMaster MCP detection error:', error);
24
+ res.status(500).json({
25
+ error: 'Failed to detect TaskMaster MCP server',
26
+ message: error.message
27
+ });
28
+ }
29
+ });
30
+
31
+ /**
32
+ * GET /api/mcp-utils/all-servers
33
+ * Get all configured MCP servers
34
+ */
35
+ router.get('/all-servers', async (req, res) => {
36
+ try {
37
+ const result = await getAllMCPServers();
38
+ res.json(result);
39
+ } catch (error) {
40
+ console.error('MCP servers detection error:', error);
41
+ res.status(500).json({
42
+ error: 'Failed to get MCP servers',
43
+ message: error.message
44
+ });
45
+ }
46
+ });
47
+
48
+ export default router;
@@ -0,0 +1,618 @@
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
+ import { resolveAvailableCliCommand } from '../utils/cliResolution.js';
9
+
10
+ const router = express.Router();
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+
14
+ let cachedClaudeCmd = null;
15
+ let cacheExpiry = 0;
16
+ const CACHE_TTL_MS = 30_000;
17
+
18
+ async function resolveClaudeCommand() {
19
+ if (cachedClaudeCmd && Date.now() < cacheExpiry) {
20
+ return cachedClaudeCmd;
21
+ }
22
+ const resolved = await resolveAvailableCliCommand({
23
+ envVarName: 'CLAUDE_CLI_PATH',
24
+ defaultCommands: ['claude'],
25
+ appendWindowsSuffixes: true
26
+ });
27
+ if (resolved) {
28
+ cachedClaudeCmd = resolved;
29
+ cacheExpiry = Date.now() + CACHE_TTL_MS;
30
+ }
31
+ return resolved;
32
+ }
33
+
34
+ function getSpawnOptions(options = {}) {
35
+ return {
36
+ ...options,
37
+ shell: process.platform === 'win32'
38
+ };
39
+ }
40
+
41
+ // Claude CLI command routes
42
+
43
+ // GET /api/mcp/cli/list - List MCP servers using Claude CLI
44
+ router.get('/cli/list', async (req, res) => {
45
+ try {
46
+ console.log('📋 Listing MCP servers using Claude CLI');
47
+
48
+ const claudeCmd = await resolveClaudeCommand();
49
+ if (!claudeCmd) {
50
+ return res.status(503).json({ error: 'Claude CLI not installed', details: 'Could not find claude command in PATH' });
51
+ }
52
+
53
+ const process = spawn(claudeCmd, ['mcp', 'list'], getSpawnOptions({
54
+ stdio: ['pipe', 'pipe', 'pipe']
55
+ }));
56
+
57
+ let stdout = '';
58
+ let stderr = '';
59
+ let sent = false;
60
+
61
+ process.stdout.on('data', (data) => {
62
+ stdout += data.toString();
63
+ });
64
+
65
+ process.stderr.on('data', (data) => {
66
+ stderr += data.toString();
67
+ });
68
+
69
+ process.on('close', (code) => {
70
+ if (sent) return;
71
+ sent = true;
72
+ if (code === 0) {
73
+ res.json({ success: true, output: stdout, servers: parseClaudeListOutput(stdout) });
74
+ } else {
75
+ console.error('Claude CLI error:', stderr);
76
+ res.status(500).json({ error: 'Claude CLI command failed', details: stderr });
77
+ }
78
+ });
79
+
80
+ process.on('error', (error) => {
81
+ if (sent) return;
82
+ sent = true;
83
+ console.error('Error running Claude CLI:', error);
84
+ res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
85
+ });
86
+ } catch (error) {
87
+ console.error('Error listing MCP servers via CLI:', error);
88
+ res.status(500).json({ error: 'Failed to list MCP servers', details: error.message });
89
+ }
90
+ });
91
+
92
+ // POST /api/mcp/cli/add - Add MCP server using Claude CLI
93
+ router.post('/cli/add', async (req, res) => {
94
+ try {
95
+ const { name, type = 'stdio', command, args = [], url, headers = {}, env = {}, scope = 'user', projectPath } = req.body;
96
+
97
+ console.log(`➕ Adding MCP server using Claude CLI (${scope} scope):`, name);
98
+
99
+ const claudeCmd = await resolveClaudeCommand();
100
+ if (!claudeCmd) {
101
+ return res.status(503).json({ error: 'Claude CLI not installed', details: 'Could not find claude command in PATH' });
102
+ }
103
+
104
+ let cliArgs = ['mcp', 'add'];
105
+
106
+ // Add scope flag
107
+ cliArgs.push('--scope', scope);
108
+
109
+ if (type === 'http') {
110
+ cliArgs.push('--transport', 'http', name, url);
111
+ // Add headers if provided
112
+ Object.entries(headers).forEach(([key, value]) => {
113
+ cliArgs.push('--header', `${key}: ${value}`);
114
+ });
115
+ } else if (type === 'sse') {
116
+ cliArgs.push('--transport', 'sse', name, url);
117
+ // Add headers if provided
118
+ Object.entries(headers).forEach(([key, value]) => {
119
+ cliArgs.push('--header', `${key}: ${value}`);
120
+ });
121
+ } else {
122
+ // stdio (default): claude mcp add --scope user <name> <command> [args...]
123
+ cliArgs.push(name);
124
+ // Add environment variables
125
+ Object.entries(env).forEach(([key, value]) => {
126
+ cliArgs.push('-e', `${key}=${value}`);
127
+ });
128
+ cliArgs.push(command);
129
+ if (args && args.length > 0) {
130
+ cliArgs.push(...args);
131
+ }
132
+ }
133
+
134
+ console.log('🔧 Running Claude CLI command:', 'claude', cliArgs.join(' '));
135
+
136
+ // For local scope, we need to run the command in the project directory
137
+ const spawnOptions = {
138
+ stdio: ['pipe', 'pipe', 'pipe']
139
+ };
140
+
141
+ if (scope === 'local' && projectPath) {
142
+ spawnOptions.cwd = projectPath;
143
+ console.log('📁 Running in project directory:', projectPath);
144
+ }
145
+
146
+ const process = spawn(claudeCmd, cliArgs, getSpawnOptions(spawnOptions));
147
+
148
+ let stdout = '';
149
+ let stderr = '';
150
+ let sent = false;
151
+
152
+ process.stdout.on('data', (data) => {
153
+ stdout += data.toString();
154
+ });
155
+
156
+ process.stderr.on('data', (data) => {
157
+ stderr += data.toString();
158
+ });
159
+
160
+ process.on('close', (code) => {
161
+ if (sent) return;
162
+ sent = true;
163
+ if (code === 0) {
164
+ res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully` });
165
+ } else {
166
+ console.error('Claude CLI error:', stderr);
167
+ res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
168
+ }
169
+ });
170
+
171
+ process.on('error', (error) => {
172
+ if (sent) return;
173
+ sent = true;
174
+ console.error('Error running Claude CLI:', error);
175
+ res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
176
+ });
177
+ } catch (error) {
178
+ console.error('Error adding MCP server via CLI:', error);
179
+ res.status(500).json({ error: 'Failed to add MCP server', details: error.message });
180
+ }
181
+ });
182
+
183
+ // POST /api/mcp/cli/add-json - Add MCP server using JSON format
184
+ router.post('/cli/add-json', async (req, res) => {
185
+ try {
186
+ const { name, jsonConfig, scope = 'user', projectPath } = req.body;
187
+
188
+ console.log('➕ Adding MCP server using JSON format:', name);
189
+
190
+ // Validate and parse JSON config
191
+ let parsedConfig;
192
+ try {
193
+ parsedConfig = typeof jsonConfig === 'string' ? JSON.parse(jsonConfig) : jsonConfig;
194
+ } catch (parseError) {
195
+ return res.status(400).json({
196
+ error: 'Invalid JSON configuration',
197
+ details: parseError.message
198
+ });
199
+ }
200
+
201
+ // Validate required fields
202
+ if (!parsedConfig.type) {
203
+ return res.status(400).json({
204
+ error: 'Invalid configuration',
205
+ details: 'Missing required field: type'
206
+ });
207
+ }
208
+
209
+ if (parsedConfig.type === 'stdio' && !parsedConfig.command) {
210
+ return res.status(400).json({
211
+ error: 'Invalid configuration',
212
+ details: 'stdio type requires a command field'
213
+ });
214
+ }
215
+
216
+ if ((parsedConfig.type === 'http' || parsedConfig.type === 'sse') && !parsedConfig.url) {
217
+ return res.status(400).json({
218
+ error: 'Invalid configuration',
219
+ details: `${parsedConfig.type} type requires a url field`
220
+ });
221
+ }
222
+
223
+ const claudeCmd = await resolveClaudeCommand();
224
+ if (!claudeCmd) {
225
+ return res.status(503).json({ error: 'Claude CLI not installed', details: 'Could not find claude command in PATH' });
226
+ }
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
+ console.log('🔧 Running Claude CLI command:', 'claude', cliArgs[0], cliArgs[1], cliArgs[2], cliArgs[3], cliArgs[4], jsonString);
236
+
237
+ // For local scope, we need to run the command in the project directory
238
+ const spawnOptions = {
239
+ stdio: ['pipe', 'pipe', 'pipe']
240
+ };
241
+
242
+ if (scope === 'local' && projectPath) {
243
+ spawnOptions.cwd = projectPath;
244
+ console.log('📁 Running in project directory:', projectPath);
245
+ }
246
+
247
+ const process = spawn(claudeCmd, cliArgs, getSpawnOptions(spawnOptions));
248
+
249
+ let stdout = '';
250
+ let stderr = '';
251
+ let sent = false;
252
+
253
+ process.stdout.on('data', (data) => {
254
+ stdout += data.toString();
255
+ });
256
+
257
+ process.stderr.on('data', (data) => {
258
+ stderr += data.toString();
259
+ });
260
+
261
+ process.on('close', (code) => {
262
+ if (sent) return;
263
+ sent = true;
264
+ if (code === 0) {
265
+ res.json({ success: true, output: stdout, message: `MCP server "${name}" added successfully via JSON` });
266
+ } else {
267
+ console.error('Claude CLI error:', stderr);
268
+ res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
269
+ }
270
+ });
271
+
272
+ process.on('error', (error) => {
273
+ if (sent) return;
274
+ sent = true;
275
+ console.error('Error running Claude CLI:', error);
276
+ res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
277
+ });
278
+ } catch (error) {
279
+ console.error('Error adding MCP server via JSON:', error);
280
+ res.status(500).json({ error: 'Failed to add MCP server', details: error.message });
281
+ }
282
+ });
283
+
284
+ // DELETE /api/mcp/cli/remove/:name - Remove MCP server using Claude CLI
285
+ router.delete('/cli/remove/:name', async (req, res) => {
286
+ try {
287
+ const { name } = req.params;
288
+ const { scope } = req.query; // Get scope from query params
289
+
290
+ // Handle the ID format (remove scope prefix if present)
291
+ let actualName = name;
292
+ let actualScope = scope;
293
+
294
+ // If the name includes a scope prefix like "local:test", extract it
295
+ if (name.includes(':')) {
296
+ const [prefix, serverName] = name.split(':');
297
+ actualName = serverName;
298
+ actualScope = actualScope || prefix; // Use prefix as scope if not provided in query
299
+ }
300
+
301
+ console.log('🗑️ Removing MCP server using Claude CLI:', actualName, 'scope:', actualScope);
302
+
303
+ const claudeCmd = await resolveClaudeCommand();
304
+ if (!claudeCmd) {
305
+ return res.status(503).json({ error: 'Claude CLI not installed', details: 'Could not find claude command in PATH' });
306
+ }
307
+
308
+ // Build command args based on scope
309
+ let cliArgs = ['mcp', 'remove'];
310
+
311
+ // Add scope flag if it's local scope
312
+ if (actualScope === 'local') {
313
+ cliArgs.push('--scope', 'local');
314
+ } else if (actualScope === 'user' || !actualScope) {
315
+ // User scope is default, but we can be explicit
316
+ cliArgs.push('--scope', 'user');
317
+ }
318
+
319
+ cliArgs.push(actualName);
320
+
321
+ console.log('🔧 Running Claude CLI command:', 'claude', cliArgs.join(' '));
322
+
323
+ const process = spawn(claudeCmd, cliArgs, getSpawnOptions({
324
+ stdio: ['pipe', 'pipe', 'pipe']
325
+ }));
326
+
327
+ let stdout = '';
328
+ let stderr = '';
329
+ let sent = false;
330
+
331
+ process.stdout.on('data', (data) => {
332
+ stdout += data.toString();
333
+ });
334
+
335
+ process.stderr.on('data', (data) => {
336
+ stderr += data.toString();
337
+ });
338
+
339
+ process.on('close', (code) => {
340
+ if (sent) return;
341
+ sent = true;
342
+ if (code === 0) {
343
+ res.json({ success: true, output: stdout, message: `MCP server "${name}" removed successfully` });
344
+ } else {
345
+ console.error('Claude CLI error:', stderr);
346
+ res.status(400).json({ error: 'Claude CLI command failed', details: stderr });
347
+ }
348
+ });
349
+
350
+ process.on('error', (error) => {
351
+ if (sent) return;
352
+ sent = true;
353
+ console.error('Error running Claude CLI:', error);
354
+ res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
355
+ });
356
+ } catch (error) {
357
+ console.error('Error removing MCP server via CLI:', error);
358
+ res.status(500).json({ error: 'Failed to remove MCP server', details: error.message });
359
+ }
360
+ });
361
+
362
+ // GET /api/mcp/cli/get/:name - Get MCP server details using Claude CLI
363
+ router.get('/cli/get/:name', async (req, res) => {
364
+ try {
365
+ const { name } = req.params;
366
+
367
+ console.log('📄 Getting MCP server details using Claude CLI:', name);
368
+
369
+ const claudeCmd = await resolveClaudeCommand();
370
+ if (!claudeCmd) {
371
+ return res.status(503).json({ error: 'Claude CLI not installed', details: 'Could not find claude command in PATH' });
372
+ }
373
+
374
+ const process = spawn(claudeCmd, ['mcp', 'get', name], getSpawnOptions({
375
+ stdio: ['pipe', 'pipe', 'pipe']
376
+ }));
377
+
378
+ let stdout = '';
379
+ let stderr = '';
380
+ let sent = false;
381
+
382
+ process.stdout.on('data', (data) => {
383
+ stdout += data.toString();
384
+ });
385
+
386
+ process.stderr.on('data', (data) => {
387
+ stderr += data.toString();
388
+ });
389
+
390
+ process.on('close', (code) => {
391
+ if (sent) return;
392
+ sent = true;
393
+ if (code === 0) {
394
+ res.json({ success: true, output: stdout, server: parseClaudeGetOutput(stdout) });
395
+ } else {
396
+ console.error('Claude CLI error:', stderr);
397
+ res.status(404).json({ error: 'Claude CLI command failed', details: stderr });
398
+ }
399
+ });
400
+
401
+ process.on('error', (error) => {
402
+ if (sent) return;
403
+ sent = true;
404
+ console.error('Error running Claude CLI:', error);
405
+ res.status(500).json({ error: 'Failed to run Claude CLI', details: error.message });
406
+ });
407
+ } catch (error) {
408
+ console.error('Error getting MCP server details via CLI:', error);
409
+ res.status(500).json({ error: 'Failed to get MCP server details', details: error.message });
410
+ }
411
+ });
412
+
413
+ // GET /api/mcp/config/read - Read MCP servers directly from Claude config files
414
+ router.get('/config/read', async (req, res) => {
415
+ try {
416
+ console.log('📖 Reading MCP servers from Claude config files');
417
+
418
+ const homeDir = os.homedir();
419
+ const configPaths = [
420
+ path.join(homeDir, '.claude.json'),
421
+ path.join(homeDir, '.claude', 'settings.json')
422
+ ];
423
+
424
+ let configData = null;
425
+ let configPath = null;
426
+
427
+ // Try to read from either config file
428
+ for (const filepath of configPaths) {
429
+ try {
430
+ const fileContent = await fs.readFile(filepath, 'utf8');
431
+ configData = JSON.parse(fileContent);
432
+ configPath = filepath;
433
+ console.log(`✅ Found Claude config at: ${filepath}`);
434
+ break;
435
+ } catch (error) {
436
+ // File doesn't exist or is not valid JSON, try next
437
+ console.log(`ℹ️ Config not found or invalid at: ${filepath}`);
438
+ }
439
+ }
440
+
441
+ if (!configData) {
442
+ return res.json({
443
+ success: false,
444
+ message: 'No Claude configuration file found',
445
+ servers: []
446
+ });
447
+ }
448
+
449
+ // Extract MCP servers from the config
450
+ const servers = [];
451
+
452
+ // Check for user-scoped MCP servers (at root level)
453
+ if (configData.mcpServers && typeof configData.mcpServers === 'object' && Object.keys(configData.mcpServers).length > 0) {
454
+ console.log('🔍 Found user-scoped MCP servers:', Object.keys(configData.mcpServers));
455
+ for (const [name, config] of Object.entries(configData.mcpServers)) {
456
+ const server = {
457
+ id: name,
458
+ name: name,
459
+ type: 'stdio', // Default type
460
+ scope: 'user', // User scope - available across all projects
461
+ config: {},
462
+ raw: config // Include raw config for full details
463
+ };
464
+
465
+ // Determine transport type and extract config
466
+ if (config.command) {
467
+ server.type = 'stdio';
468
+ server.config.command = config.command;
469
+ server.config.args = config.args || [];
470
+ server.config.env = config.env || {};
471
+ } else if (config.url) {
472
+ server.type = config.transport || 'http';
473
+ server.config.url = config.url;
474
+ server.config.headers = config.headers || {};
475
+ }
476
+
477
+ servers.push(server);
478
+ }
479
+ }
480
+
481
+ // Check for local-scoped MCP servers (project-specific)
482
+ const currentProjectPath = process.cwd();
483
+
484
+ // Check under 'projects' key
485
+ if (configData.projects && configData.projects[currentProjectPath]) {
486
+ const projectConfig = configData.projects[currentProjectPath];
487
+ if (projectConfig.mcpServers && typeof projectConfig.mcpServers === 'object' && Object.keys(projectConfig.mcpServers).length > 0) {
488
+ console.log(`🔍 Found local-scoped MCP servers for ${currentProjectPath}:`, Object.keys(projectConfig.mcpServers));
489
+ for (const [name, config] of Object.entries(projectConfig.mcpServers)) {
490
+ const server = {
491
+ id: `local:${name}`, // Prefix with scope for uniqueness
492
+ name: name, // Keep original name
493
+ type: 'stdio', // Default type
494
+ scope: 'local', // Local scope - only for this project
495
+ projectPath: currentProjectPath,
496
+ config: {},
497
+ raw: config // Include raw config for full details
498
+ };
499
+
500
+ // Determine transport type and extract config
501
+ if (config.command) {
502
+ server.type = 'stdio';
503
+ server.config.command = config.command;
504
+ server.config.args = config.args || [];
505
+ server.config.env = config.env || {};
506
+ } else if (config.url) {
507
+ server.type = config.transport || 'http';
508
+ server.config.url = config.url;
509
+ server.config.headers = config.headers || {};
510
+ }
511
+
512
+ servers.push(server);
513
+ }
514
+ }
515
+ }
516
+
517
+ console.log(`📋 Found ${servers.length} MCP servers in config`);
518
+
519
+ res.json({
520
+ success: true,
521
+ configPath: configPath,
522
+ servers: servers
523
+ });
524
+ } catch (error) {
525
+ console.error('Error reading Claude config:', error);
526
+ res.status(500).json({
527
+ error: 'Failed to read Claude configuration',
528
+ details: error.message
529
+ });
530
+ }
531
+ });
532
+
533
+ // Helper functions to parse Claude CLI output
534
+ function parseClaudeListOutput(output) {
535
+ const servers = [];
536
+ const lines = output.split('\n').filter(line => line.trim());
537
+
538
+ for (const line of lines) {
539
+ // Skip the header line
540
+ if (line.includes('Checking MCP server health')) continue;
541
+
542
+ // Parse lines like "test: test test - ✗ Failed to connect"
543
+ // or "server-name: command or description - ✓ Connected"
544
+ if (line.includes(':')) {
545
+ const colonIndex = line.indexOf(':');
546
+ const name = line.substring(0, colonIndex).trim();
547
+
548
+ // Skip empty names
549
+ if (!name) continue;
550
+
551
+ // Extract the rest after the name
552
+ const rest = line.substring(colonIndex + 1).trim();
553
+
554
+ // Try to extract description and status
555
+ let description = rest;
556
+ let status = 'unknown';
557
+ let type = 'stdio'; // default type
558
+
559
+ // Check for status indicators
560
+ if (rest.includes('✓') || rest.includes('✗')) {
561
+ const statusMatch = rest.match(/(.*?)\s*-\s*([✓✗].*)$/);
562
+ if (statusMatch) {
563
+ description = statusMatch[1].trim();
564
+ status = statusMatch[2].includes('✓') ? 'connected' : 'failed';
565
+ }
566
+ }
567
+
568
+ // Try to determine type from description
569
+ if (description.startsWith('http://') || description.startsWith('https://')) {
570
+ type = 'http';
571
+ }
572
+
573
+ servers.push({
574
+ name,
575
+ type,
576
+ status: status || 'active',
577
+ description
578
+ });
579
+ }
580
+ }
581
+
582
+ console.log('🔍 Parsed Claude CLI servers:', servers);
583
+ return servers;
584
+ }
585
+
586
+ function parseClaudeGetOutput(output) {
587
+ // Parse the output from 'claude mcp get <name>' command
588
+ // This is a simple parser - might need adjustment based on actual output format
589
+ try {
590
+ // Try to extract JSON if present
591
+ const jsonMatch = output.match(/\{[\s\S]*\}/);
592
+ if (jsonMatch) {
593
+ return JSON.parse(jsonMatch[0]);
594
+ }
595
+
596
+ // Otherwise, parse as text
597
+ const server = { raw_output: output };
598
+ const lines = output.split('\n');
599
+
600
+ for (const line of lines) {
601
+ if (line.includes('Name:')) {
602
+ server.name = line.split(':')[1]?.trim();
603
+ } else if (line.includes('Type:')) {
604
+ server.type = line.split(':')[1]?.trim();
605
+ } else if (line.includes('Command:')) {
606
+ server.command = line.split(':')[1]?.trim();
607
+ } else if (line.includes('URL:')) {
608
+ server.url = line.split(':')[1]?.trim();
609
+ }
610
+ }
611
+
612
+ return server;
613
+ } catch (error) {
614
+ return { raw_output: output, parse_error: error.message };
615
+ }
616
+ }
617
+
618
+ export default router;