@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.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 (258) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +11 -11
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lsp/server.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path, { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { getLanguageId } from './language.js';\n\n/**\n * LSP Server definition and spawning logic\n */\nexport interface LSPServerInfo {\n id: string;\n name: string;\n languageIds: string[];\n root: (cwd: string) => string | null;\n spawn: (root: string) => ChildProcess | Promise<{ process: ChildProcess; initialization?: any } | undefined>;\n}\n\n/**\n * Find the nearest project root directory\n */\nexport function findNearestRoot(cwd: string, markers: string[]): string | null {\n let current = cwd;\n\n while (current !== '/') {\n for (const marker of markers) {\n if (existsSync(join(current, marker))) {\n return current;\n }\n }\n\n const parent = join(current, '..');\n if (parent === current) break;\n current = parent;\n }\n\n return null;\n}\n\n/**\n * Built-in LSP server definitions\n */\nexport const BUILTIN_SERVERS: Record<string, LSPServerInfo> = {\n typescript: {\n id: 'typescript',\n name: 'TypeScript Language Server',\n languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n root: (cwd: string) => findNearestRoot(cwd, ['tsconfig.json', 'package.json']),\n spawn: async (root: string) => {\n // Try to resolve TypeScript from the project directory\n const requireFromRoot = createRequire(pathToFileURL(path.join(root, 'package.json')));\n let tsserver: string | undefined;\n try {\n tsserver = requireFromRoot.resolve('typescript/lib/tsserver.js');\n } catch {\n tsserver = undefined;\n }\n if (!tsserver) {\n return undefined;\n }\n\n // Resolve typescript-language-server binary directly to avoid npx hangs\n // (npx can hang indefinitely in projects with pnpm links)\n const localBin = join(root, 'node_modules', '.bin', 'typescript-language-server');\n const cwdBin = join(process.cwd(), 'node_modules', '.bin', 'typescript-language-server');\n let tslsBinary: string;\n if (existsSync(localBin)) {\n tslsBinary = localBin;\n } else if (existsSync(cwdBin)) {\n tslsBinary = cwdBin;\n } else {\n // Fall back to npx as last resort, but this may hang with pnpm links\n tslsBinary = 'npx';\n }\n\n const args = tslsBinary === 'npx' ? ['typescript-language-server', '--stdio'] : ['--stdio'];\n\n const proc = spawn(tslsBinary, args, {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n return {\n process: proc,\n initialization: {\n tsserver: {\n path: tsserver,\n logVerbosity: 'off',\n },\n },\n };\n },\n },\n\n eslint: {\n id: 'eslint',\n name: 'ESLint Language Server',\n languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n root: (cwd: string) =>\n findNearestRoot(cwd, ['package.json', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml', '.eslintrc.yaml']),\n spawn: (root: string) => {\n const binaryPath = join(process.cwd(), 'node_modules', '.bin', 'eslint-lsp');\n return spawn(binaryPath, ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n python: {\n id: 'python',\n name: 'Python Language Server (Pyright)',\n languageIds: ['python'],\n root: (cwd: string) => findNearestRoot(cwd, ['pyproject.toml', 'setup.py', 'requirements.txt', '.git']),\n spawn: (root: string) => {\n // Try node_modules first, then fall back to system PATH\n const localPath = join(process.cwd(), 'node_modules', '.bin', 'pyright-langserver');\n const binaryPath = existsSync(localPath) ? localPath : 'pyright-langserver';\n return spawn(binaryPath, ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n go: {\n id: 'go',\n name: 'Go Language Server (gopls)',\n languageIds: ['go'],\n root: (cwd: string) => findNearestRoot(cwd, ['go.mod', '.git']),\n spawn: (root: string) => {\n return spawn('gopls', ['serve'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n rust: {\n id: 'rust',\n name: 'Rust Language Server (rust-analyzer)',\n languageIds: ['rust'],\n root: (cwd: string) => findNearestRoot(cwd, ['Cargo.toml', '.git']),\n spawn: (root: string) => {\n return spawn('rust-analyzer', ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n};\n\n/**\n * Get all servers that can handle a file\n */\nexport function getServersForFile(filePath: string, cwd: string): LSPServerInfo[] {\n const languageId = getLanguageId(filePath);\n if (!languageId) return [];\n\n return Object.values(BUILTIN_SERVERS).filter(\n server => server.languageIds.includes(languageId) && server.root(cwd) !== null,\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../../src/lsp/server.ts"],"sourcesContent":["import type { ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path, { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { getLanguageId } from './language.js';\n\n/**\n * LSP Server definition and spawning logic\n */\nexport interface LSPServerInfo {\n id: string;\n name: string;\n languageIds: string[];\n root: (cwd: string) => string | null;\n spawn: (root: string) => ChildProcess | Promise<{ process: ChildProcess; initialization?: any } | undefined>;\n}\n\n/**\n * Find the nearest project root directory\n */\nexport function findNearestRoot(cwd: string, markers: string[]): string | null {\n let current = cwd;\n\n while (current !== '/') {\n for (const marker of markers) {\n if (existsSync(join(current, marker))) {\n return current;\n }\n }\n\n const parent = join(current, '..');\n if (parent === current) break;\n current = parent;\n }\n\n return null;\n}\n\n/**\n * Built-in LSP server definitions\n */\nexport const BUILTIN_SERVERS: Record<string, LSPServerInfo> = {\n typescript: {\n id: 'typescript',\n name: 'TypeScript Language Server',\n languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n root: (cwd: string) => findNearestRoot(cwd, ['tsconfig.json', 'package.json']),\n spawn: async (root: string) => {\n // Try to resolve TypeScript from the project directory\n const requireFromRoot = createRequire(pathToFileURL(path.join(root, 'package.json')));\n let tsserver: string | undefined;\n try {\n tsserver = requireFromRoot.resolve('typescript/lib/tsserver.js');\n } catch {\n tsserver = undefined;\n }\n if (!tsserver) {\n return undefined;\n }\n\n // Resolve typescript-language-server binary directly to avoid npx hangs\n // (npx can hang indefinitely in projects with pnpm links)\n const localBin = join(root, 'node_modules', '.bin', 'typescript-language-server');\n const cwdBin = join(process.cwd(), 'node_modules', '.bin', 'typescript-language-server');\n let tslsBinary: string;\n if (existsSync(localBin)) {\n tslsBinary = localBin;\n } else if (existsSync(cwdBin)) {\n tslsBinary = cwdBin;\n } else {\n // Fall back to npx as last resort, but this may hang with pnpm links\n tslsBinary = 'npx';\n }\n\n const args = tslsBinary === 'npx' ? ['typescript-language-server', '--stdio'] : ['--stdio'];\n\n const proc = spawn(tslsBinary, args, {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n return {\n process: proc,\n initialization: {\n tsserver: {\n path: tsserver,\n logVerbosity: 'off',\n },\n },\n };\n },\n },\n\n eslint: {\n id: 'eslint',\n name: 'ESLint Language Server',\n languageIds: ['typescript', 'typescriptreact', 'javascript', 'javascriptreact'],\n root: (cwd: string) =>\n findNearestRoot(cwd, ['package.json', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml', '.eslintrc.yaml']),\n spawn: (root: string) => {\n const binaryPath = join(process.cwd(), 'node_modules', '.bin', 'eslint-lsp');\n return spawn(binaryPath, ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n python: {\n id: 'python',\n name: 'Python Language Server (Pyright)',\n languageIds: ['python'],\n root: (cwd: string) => findNearestRoot(cwd, ['pyproject.toml', 'setup.py', 'requirements.txt', '.git']),\n spawn: (root: string) => {\n // Try node_modules first, then fall back to system PATH\n const localPath = join(process.cwd(), 'node_modules', '.bin', 'pyright-langserver');\n const binaryPath = existsSync(localPath) ? localPath : 'pyright-langserver';\n return spawn(binaryPath, ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n go: {\n id: 'go',\n name: 'Go Language Server (gopls)',\n languageIds: ['go'],\n root: (cwd: string) => findNearestRoot(cwd, ['go.mod', '.git']),\n spawn: (root: string) => {\n return spawn('gopls', ['serve'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n\n rust: {\n id: 'rust',\n name: 'Rust Language Server (rust-analyzer)',\n languageIds: ['rust'],\n root: (cwd: string) => findNearestRoot(cwd, ['Cargo.toml', '.git']),\n spawn: (root: string) => {\n return spawn('rust-analyzer', ['--stdio'], {\n cwd: root,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n },\n },\n};\n\n/**\n * Get all servers that can handle a file\n */\nexport function getServersForFile(filePath: string, cwd: string): LSPServerInfo[] {\n const languageId = getLanguageId(filePath);\n if (!languageId) return [];\n\n return Object.values(BUILTIN_SERVERS).filter(\n server => server.languageIds.includes(languageId) && server.root(cwd) !== null,\n );\n}\n"],"mappings":"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"}
@@ -1,52 +1,55 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
1
+ import * as fs$1 from "fs";
2
+ import * as path$1 from "path";
3
+ //#region src/lsp/workspace.ts
4
+ /**
5
+ * Workspace markers that indicate the root of a project
6
+ */
3
7
  const WORKSPACE_MARKERS = [
4
- "package.json",
5
- "tsconfig.json",
6
- "pyproject.toml",
7
- "go.mod",
8
- ".git",
9
- "Cargo.toml",
10
- "composer.json"
8
+ "package.json",
9
+ "tsconfig.json",
10
+ "pyproject.toml",
11
+ "go.mod",
12
+ ".git",
13
+ "Cargo.toml",
14
+ "composer.json"
11
15
  ];
16
+ /**
17
+ * Find the workspace root by walking up from the file path
18
+ * looking for common workspace markers.
19
+ *
20
+ * For monorepos, this prioritizes the closest package/project root
21
+ * (e.g., packages/core) over the monorepo root.
22
+ */
12
23
  function findWorkspaceRoot(filePath) {
13
- let currentDir = path.dirname(path.resolve(filePath));
14
- const root = path.parse(currentDir).root;
15
- let closestProjectRoot = null;
16
- let searchDir = currentDir;
17
- while (searchDir !== root) {
18
- const hasTsConfig = fs.existsSync(path.join(searchDir, "tsconfig.json"));
19
- const hasPackageJson = fs.existsSync(path.join(searchDir, "package.json"));
20
- if (hasTsConfig || hasPackageJson) {
21
- closestProjectRoot = searchDir;
22
- break;
23
- }
24
- const parentDir = path.dirname(searchDir);
25
- if (parentDir === searchDir) {
26
- break;
27
- }
28
- searchDir = parentDir;
29
- }
30
- if (closestProjectRoot) {
31
- return closestProjectRoot;
32
- }
33
- currentDir = path.dirname(path.resolve(filePath));
34
- while (currentDir !== root) {
35
- for (const marker of WORKSPACE_MARKERS) {
36
- const markerPath = path.join(currentDir, marker);
37
- if (fs.existsSync(markerPath)) {
38
- return currentDir;
39
- }
40
- }
41
- const parentDir = path.dirname(currentDir);
42
- if (parentDir === currentDir) {
43
- break;
44
- }
45
- currentDir = parentDir;
46
- }
47
- return path.dirname(path.resolve(filePath));
24
+ let currentDir = path$1.dirname(path$1.resolve(filePath));
25
+ const root = path$1.parse(currentDir).root;
26
+ let closestProjectRoot = null;
27
+ let searchDir = currentDir;
28
+ while (searchDir !== root) {
29
+ const hasTsConfig = fs$1.existsSync(path$1.join(searchDir, "tsconfig.json"));
30
+ const hasPackageJson = fs$1.existsSync(path$1.join(searchDir, "package.json"));
31
+ if (hasTsConfig || hasPackageJson) {
32
+ closestProjectRoot = searchDir;
33
+ break;
34
+ }
35
+ const parentDir = path$1.dirname(searchDir);
36
+ if (parentDir === searchDir) break;
37
+ searchDir = parentDir;
38
+ }
39
+ if (closestProjectRoot) return closestProjectRoot;
40
+ currentDir = path$1.dirname(path$1.resolve(filePath));
41
+ while (currentDir !== root) {
42
+ for (const marker of WORKSPACE_MARKERS) {
43
+ const markerPath = path$1.join(currentDir, marker);
44
+ if (fs$1.existsSync(markerPath)) return currentDir;
45
+ }
46
+ const parentDir = path$1.dirname(currentDir);
47
+ if (parentDir === currentDir) break;
48
+ currentDir = parentDir;
49
+ }
50
+ return path$1.dirname(path$1.resolve(filePath));
48
51
  }
49
- export {
50
- findWorkspaceRoot
51
- };
52
+ //#endregion
53
+ export { findWorkspaceRoot };
54
+
52
55
  //# sourceMappingURL=workspace.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lsp/workspace.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Workspace markers that indicate the root of a project\n */\nconst WORKSPACE_MARKERS = [\n 'package.json',\n 'tsconfig.json',\n 'pyproject.toml',\n 'go.mod',\n '.git',\n 'Cargo.toml',\n 'composer.json',\n];\n\n/**\n * Find the workspace root by walking up from the file path\n * looking for common workspace markers.\n *\n * For monorepos, this prioritizes the closest package/project root\n * (e.g., packages/core) over the monorepo root.\n */\nexport function findWorkspaceRoot(filePath: string): string {\n let currentDir = path.dirname(path.resolve(filePath));\n const root = path.parse(currentDir).root;\n\n // First, find the closest tsconfig.json or package.json\n // This handles monorepos where the actual project is in a subdirectory\n let closestProjectRoot: string | null = null;\n let searchDir = currentDir;\n\n while (searchDir !== root) {\n const hasTsConfig = fs.existsSync(path.join(searchDir, 'tsconfig.json'));\n const hasPackageJson = fs.existsSync(path.join(searchDir, 'package.json'));\n\n if (hasTsConfig || hasPackageJson) {\n closestProjectRoot = searchDir;\n break;\n }\n\n const parentDir = path.dirname(searchDir);\n if (parentDir === searchDir) {\n break;\n }\n searchDir = parentDir;\n }\n\n // If we found a project root, return it\n if (closestProjectRoot) {\n return closestProjectRoot;\n }\n\n // Otherwise, fall back to looking for any workspace marker\n currentDir = path.dirname(path.resolve(filePath));\n while (currentDir !== root) {\n for (const marker of WORKSPACE_MARKERS) {\n const markerPath = path.join(currentDir, marker);\n if (fs.existsSync(markerPath)) {\n return currentDir;\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n break;\n }\n currentDir = parentDir;\n }\n\n // Fallback to the directory containing the file\n return path.dirname(path.resolve(filePath));\n}\n"],"mappings":"AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAKtB,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,kBAAkB,UAA0B;AAC1D,MAAI,aAAa,KAAK,QAAQ,KAAK,QAAQ,QAAQ,CAAC;AACpD,QAAM,OAAO,KAAK,MAAM,UAAU,EAAE;AAIpC,MAAI,qBAAoC;AACxC,MAAI,YAAY;AAEhB,SAAO,cAAc,MAAM;AACzB,UAAM,cAAc,GAAG,WAAW,KAAK,KAAK,WAAW,eAAe,CAAC;AACvE,UAAM,iBAAiB,GAAG,WAAW,KAAK,KAAK,WAAW,cAAc,CAAC;AAEzE,QAAI,eAAe,gBAAgB;AACjC,2BAAqB;AACrB;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,QAAQ,SAAS;AACxC,QAAI,cAAc,WAAW;AAC3B;AAAA,IACF;AACA,gBAAY;AAAA,EACd;AAGA,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AAGA,eAAa,KAAK,QAAQ,KAAK,QAAQ,QAAQ,CAAC;AAChD,SAAO,eAAe,MAAM;AAC1B,eAAW,UAAU,mBAAmB;AACtC,YAAM,aAAa,KAAK,KAAK,YAAY,MAAM;AAC/C,UAAI,GAAG,WAAW,UAAU,GAAG;AAC7B,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,QAAQ,UAAU;AACzC,QAAI,cAAc,YAAY;AAC5B;AAAA,IACF;AACA,iBAAa;AAAA,EACf;AAGA,SAAO,KAAK,QAAQ,KAAK,QAAQ,QAAQ,CAAC;AAC5C;","names":[]}
1
+ {"version":3,"file":"workspace.js","names":["path","fs"],"sources":["../../src/lsp/workspace.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * Workspace markers that indicate the root of a project\n */\nconst WORKSPACE_MARKERS = [\n 'package.json',\n 'tsconfig.json',\n 'pyproject.toml',\n 'go.mod',\n '.git',\n 'Cargo.toml',\n 'composer.json',\n];\n\n/**\n * Find the workspace root by walking up from the file path\n * looking for common workspace markers.\n *\n * For monorepos, this prioritizes the closest package/project root\n * (e.g., packages/core) over the monorepo root.\n */\nexport function findWorkspaceRoot(filePath: string): string {\n let currentDir = path.dirname(path.resolve(filePath));\n const root = path.parse(currentDir).root;\n\n // First, find the closest tsconfig.json or package.json\n // This handles monorepos where the actual project is in a subdirectory\n let closestProjectRoot: string | null = null;\n let searchDir = currentDir;\n\n while (searchDir !== root) {\n const hasTsConfig = fs.existsSync(path.join(searchDir, 'tsconfig.json'));\n const hasPackageJson = fs.existsSync(path.join(searchDir, 'package.json'));\n\n if (hasTsConfig || hasPackageJson) {\n closestProjectRoot = searchDir;\n break;\n }\n\n const parentDir = path.dirname(searchDir);\n if (parentDir === searchDir) {\n break;\n }\n searchDir = parentDir;\n }\n\n // If we found a project root, return it\n if (closestProjectRoot) {\n return closestProjectRoot;\n }\n\n // Otherwise, fall back to looking for any workspace marker\n currentDir = path.dirname(path.resolve(filePath));\n while (currentDir !== root) {\n for (const marker of WORKSPACE_MARKERS) {\n const markerPath = path.join(currentDir, marker);\n if (fs.existsSync(markerPath)) {\n return currentDir;\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n break;\n }\n currentDir = parentDir;\n }\n\n // Fallback to the directory containing the file\n return path.dirname(path.resolve(filePath));\n}\n"],"mappings":";;;;;;AAMA,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;AASA,SAAgB,kBAAkB,UAA0B;CAC1D,IAAI,aAAaA,OAAK,QAAQA,OAAK,QAAQ,QAAQ,CAAC;CACpD,MAAM,OAAOA,OAAK,MAAM,UAAU,CAAC,CAAC;CAIpC,IAAI,qBAAoC;CACxC,IAAI,YAAY;CAEhB,OAAO,cAAc,MAAM;EACzB,MAAM,cAAcC,KAAG,WAAWD,OAAK,KAAK,WAAW,eAAe,CAAC;EACvE,MAAM,iBAAiBC,KAAG,WAAWD,OAAK,KAAK,WAAW,cAAc,CAAC;EAEzE,IAAI,eAAe,gBAAgB;GACjC,qBAAqB;GACrB;EACF;EAEA,MAAM,YAAYA,OAAK,QAAQ,SAAS;EACxC,IAAI,cAAc,WAChB;EAEF,YAAY;CACd;CAGA,IAAI,oBACF,OAAO;CAIT,aAAaA,OAAK,QAAQA,OAAK,QAAQ,QAAQ,CAAC;CAChD,OAAO,eAAe,MAAM;EAC1B,KAAK,MAAM,UAAU,mBAAmB;GACtC,MAAM,aAAaA,OAAK,KAAK,YAAY,MAAM;GAC/C,IAAIC,KAAG,WAAW,UAAU,GAC1B,OAAO;EAEX;EAEA,MAAM,YAAYD,OAAK,QAAQ,UAAU;EACzC,IAAI,cAAc,YAChB;EAEF,aAAa;CACf;CAGA,OAAOA,OAAK,QAAQA,OAAK,QAAQ,QAAQ,CAAC;AAC5C"}
@@ -1,229 +1,240 @@
1
- import * as fs from "node:fs";
2
- import * as os from "node:os";
3
- import * as path from "node:path";
4
1
  import { DEFAULT_CONFIG_DIR } from "../constants.js";
2
+ import * as fs$1 from "fs";
3
+ import * as os$1 from "os";
4
+ import * as path$1 from "path";
5
+ //#region src/mcp/config.ts
6
+ /**
7
+ * MCP server configuration loading from filesystem.
8
+ * Loads from:
9
+ * 1. .claude/settings.local.json (Claude Code compat — lowest priority)
10
+ * 2. ~/.mastracode/mcp.json (global)
11
+ * 3. .mcp.json (project root — Claude Code compatible)
12
+ * 4. .mastracode/mcp.json (project — highest priority)
13
+ *
14
+ * Higher-priority configs override lower ones by server name. The project root
15
+ * `.mcp.json` is read so a project that already keeps MCP servers there for
16
+ * Claude Code does not need to duplicate them under `.mastracode/`.
17
+ */
18
+ /**
19
+ * Default OAuth redirect URL for servers that do not configure one.
20
+ * Port 1458 is stable across sessions so persisted tokens keep the same
21
+ * storage fingerprint; it sits clear of the ports the Codex login flow
22
+ * reserves (1455/1457). When 1458 is busy the callback server falls back
23
+ * to the next sequential port, which stays covered by the client
24
+ * registration (see `@mastra/mcp`'s `getCallbackUrlCandidates`).
25
+ */
5
26
  const DEFAULT_OAUTH_REDIRECT_URL = "http://127.0.0.1:1458/oauth/callback";
6
27
  const LOOPBACK_IPV4 = /^127\.(?:\d{1,3})\.(?:\d{1,3})\.(?:\d{1,3})$/;
7
28
  function isLoopbackHostname(hostname) {
8
- return hostname === "localhost" || hostname === "[::1]" || hostname === "::1" || LOOPBACK_IPV4.test(hostname);
9
- }
29
+ return hostname === "localhost" || hostname === "[::1]" || hostname === "::1" || LOOPBACK_IPV4.test(hostname);
30
+ }
31
+ /**
32
+ * Resolve the effective OAuth redirect URL for a server config.
33
+ *
34
+ * `callbackPort` (the Claude Code / Codex convention) takes precedence and
35
+ * synthesizes `http://localhost:<port>/callback`. Config-file entries are
36
+ * normalized by `parseOAuthConfig` (which also enforces that `callbackPort`
37
+ * and `redirectUrl` are mutually exclusive), but programmatically registered
38
+ * servers bypass the parser, so every consumer of the redirect URL must
39
+ * resolve it through this helper rather than reading `redirectUrl` directly.
40
+ */
10
41
  function resolveOAuthRedirectUrl(oauth) {
11
- if (oauth?.callbackPort !== void 0) {
12
- return `http://localhost:${oauth.callbackPort}/callback`;
13
- }
14
- return oauth?.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;
42
+ if (oauth?.callbackPort !== void 0) return `http://localhost:${oauth.callbackPort}/callback`;
43
+ return oauth?.redirectUrl ?? "http://127.0.0.1:1458/oauth/callback";
15
44
  }
16
45
  function loadMcpConfig(projectDir, configDirName = DEFAULT_CONFIG_DIR) {
17
- const claudeConfig = loadClaudeSettings(projectDir);
18
- const globalConfig = loadSingleConfig(getGlobalMcpPath(configDirName));
19
- const rootConfig = loadSingleConfig(getRootMcpPath(projectDir));
20
- const projectConfig = loadSingleConfig(getProjectMcpPath(projectDir, configDirName));
21
- return mergeConfigs(claudeConfig, globalConfig, rootConfig, projectConfig);
46
+ return mergeConfigs(loadClaudeSettings(projectDir), loadSingleConfig(getGlobalMcpPath(configDirName)), loadSingleConfig(getRootMcpPath(projectDir)), loadSingleConfig(getProjectMcpPath(projectDir, configDirName)));
22
47
  }
23
48
  function getProjectMcpPath(projectDir, configDirName = DEFAULT_CONFIG_DIR) {
24
- return path.join(projectDir, configDirName, "mcp.json");
49
+ return path$1.join(projectDir, configDirName, "mcp.json");
25
50
  }
26
51
  function getRootMcpPath(projectDir) {
27
- return path.join(projectDir, ".mcp.json");
52
+ return path$1.join(projectDir, ".mcp.json");
28
53
  }
29
54
  function getGlobalMcpPath(configDirName = DEFAULT_CONFIG_DIR) {
30
- return path.join(os.homedir(), configDirName, "mcp.json");
55
+ return path$1.join(os$1.homedir(), configDirName, "mcp.json");
31
56
  }
32
57
  function getClaudeSettingsPath(projectDir) {
33
- return path.join(projectDir, ".claude", "settings.local.json");
58
+ return path$1.join(projectDir, ".claude", "settings.local.json");
34
59
  }
35
60
  function loadSingleConfig(filePath) {
36
- try {
37
- if (!fs.existsSync(filePath)) return {};
38
- const raw = fs.readFileSync(filePath, "utf-8");
39
- return validateConfig(JSON.parse(raw));
40
- } catch {
41
- return {};
42
- }
61
+ try {
62
+ if (!fs$1.existsSync(filePath)) return {};
63
+ const raw = fs$1.readFileSync(filePath, "utf-8");
64
+ return validateConfig(JSON.parse(raw));
65
+ } catch {
66
+ return {};
67
+ }
43
68
  }
44
69
  function loadClaudeSettings(projectDir) {
45
- try {
46
- const filePath = getClaudeSettingsPath(projectDir);
47
- if (!fs.existsSync(filePath)) return {};
48
- const raw = fs.readFileSync(filePath, "utf-8");
49
- const parsed = JSON.parse(raw);
50
- if (parsed?.mcpServers && typeof parsed.mcpServers === "object") {
51
- return validateConfig({ mcpServers: parsed.mcpServers });
52
- }
53
- return {};
54
- } catch {
55
- return {};
56
- }
57
- }
70
+ try {
71
+ const filePath = getClaudeSettingsPath(projectDir);
72
+ if (!fs$1.existsSync(filePath)) return {};
73
+ const raw = fs$1.readFileSync(filePath, "utf-8");
74
+ const parsed = JSON.parse(raw);
75
+ if (parsed?.mcpServers && typeof parsed.mcpServers === "object") return validateConfig({ mcpServers: parsed.mcpServers });
76
+ return {};
77
+ } catch {
78
+ return {};
79
+ }
80
+ }
81
+ /**
82
+ * Expand `${VAR}` and `${VAR:-default}` references in a string from the
83
+ * environment, matching how Claude Code resolves values in `.mcp.json`.
84
+ * A referenced variable that is unset or empty falls back to its default,
85
+ * or to an empty string when no default is given.
86
+ */
58
87
  function expandEnvVars(value, env = process.env) {
59
- return value.replace(
60
- /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
61
- (_match, bracedName, fallback, bareName) => {
62
- const name = bracedName ?? bareName;
63
- const resolved = env[name];
64
- if (resolved !== void 0 && resolved !== "") return resolved;
65
- return fallback ?? "";
66
- }
67
- );
68
- }
88
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, bracedName, fallback, bareName) => {
89
+ const resolved = env[bracedName ?? bareName];
90
+ if (resolved !== void 0 && resolved !== "") return resolved;
91
+ return fallback ?? "";
92
+ });
93
+ }
94
+ /**
95
+ * Expand `${VAR}` and `$VAR` references in every string-valued HTTP header so that
96
+ * secrets like API keys can be referenced from the environment instead of
97
+ * being hardcoded in `mcp.json`.
98
+ */
69
99
  function expandHeaderEnvVars(headers) {
70
- const expanded = {};
71
- for (const [key, value] of Object.entries(headers)) {
72
- if (typeof value === "string") expanded[key] = expandEnvVars(value);
73
- }
74
- return expanded;
75
- }
100
+ const expanded = {};
101
+ for (const [key, value] of Object.entries(headers)) if (typeof value === "string") expanded[key] = expandEnvVars(value);
102
+ return expanded;
103
+ }
104
+ /**
105
+ * Expand `${VAR}` and `$VAR` references in every string-valued environment
106
+ * variable passed to a stdio server, so that secrets can be referenced from the
107
+ * host environment instead of being hardcoded in `mcp.json`.
108
+ */
76
109
  function expandEnvValues(env) {
77
- const expanded = {};
78
- for (const [key, value] of Object.entries(env)) {
79
- if (typeof value === "string") expanded[key] = expandEnvVars(value);
80
- }
81
- return expanded;
110
+ const expanded = {};
111
+ for (const [key, value] of Object.entries(env)) if (typeof value === "string") expanded[key] = expandEnvVars(value);
112
+ return expanded;
82
113
  }
114
+ /**
115
+ * Classify a raw server entry as stdio, http, or skip (with reason).
116
+ */
83
117
  function classifyServerEntry(raw) {
84
- if (!raw || typeof raw !== "object") {
85
- return { kind: "skip", reason: "Invalid entry: expected an object" };
86
- }
87
- const obj = raw;
88
- const hasCommand = typeof obj.command === "string";
89
- const hasUrl = typeof obj.url === "string";
90
- if (hasCommand && hasUrl) {
91
- return { kind: "skip", reason: 'Cannot specify both "command" and "url"' };
92
- }
93
- if (hasCommand) {
94
- return { kind: "stdio" };
95
- }
96
- if (hasUrl) {
97
- try {
98
- new URL(expandEnvVars(obj.url));
99
- } catch {
100
- return { kind: "skip", reason: `Invalid URL: "${obj.url}"` };
101
- }
102
- return { kind: "http" };
103
- }
104
- return { kind: "skip", reason: 'Missing required field: "command" (stdio) or "url" (http)' };
118
+ if (!raw || typeof raw !== "object") return {
119
+ kind: "skip",
120
+ reason: "Invalid entry: expected an object"
121
+ };
122
+ const obj = raw;
123
+ const hasCommand = typeof obj.command === "string";
124
+ const hasUrl = typeof obj.url === "string";
125
+ if (hasCommand && hasUrl) return {
126
+ kind: "skip",
127
+ reason: "Cannot specify both \"command\" and \"url\""
128
+ };
129
+ if (hasCommand) return { kind: "stdio" };
130
+ if (hasUrl) {
131
+ try {
132
+ new URL(expandEnvVars(obj.url));
133
+ } catch {
134
+ return {
135
+ kind: "skip",
136
+ reason: `Invalid URL: "${obj.url}"`
137
+ };
138
+ }
139
+ return { kind: "http" };
140
+ }
141
+ return {
142
+ kind: "skip",
143
+ reason: "Missing required field: \"command\" (stdio) or \"url\" (http)"
144
+ };
105
145
  }
106
146
  function validateConfig(raw) {
107
- if (!raw || typeof raw !== "object") return {};
108
- const obj = raw;
109
- if (!obj.mcpServers || typeof obj.mcpServers !== "object") return {};
110
- const servers = {};
111
- const skippedServers = [];
112
- const rawServers = obj.mcpServers;
113
- for (const [name, entry] of Object.entries(rawServers)) {
114
- const classification = classifyServerEntry(entry);
115
- if (classification.kind === "stdio") {
116
- const e = entry;
117
- servers[name] = {
118
- command: e.command,
119
- args: Array.isArray(e.args) ? e.args : void 0,
120
- env: typeof e.env === "object" && e.env !== null ? expandEnvValues(e.env) : void 0
121
- };
122
- } else if (classification.kind === "http") {
123
- const e = entry;
124
- const oauthResult = parseOAuthConfig(e.oauth);
125
- if (oauthResult.reason) {
126
- skippedServers.push({ name, reason: oauthResult.reason });
127
- continue;
128
- }
129
- servers[name] = {
130
- url: expandEnvVars(e.url),
131
- headers: typeof e.headers === "object" && e.headers !== null ? expandHeaderEnvVars(e.headers) : void 0,
132
- oauth: oauthResult.config
133
- };
134
- } else {
135
- skippedServers.push({ name, reason: classification.reason });
136
- }
137
- }
138
- const result = {};
139
- if (Object.keys(servers).length > 0) {
140
- result.mcpServers = servers;
141
- }
142
- if (skippedServers.length > 0) {
143
- result.skippedServers = skippedServers;
144
- }
145
- return result;
147
+ if (!raw || typeof raw !== "object") return {};
148
+ const obj = raw;
149
+ if (!obj.mcpServers || typeof obj.mcpServers !== "object") return {};
150
+ const servers = {};
151
+ const skippedServers = [];
152
+ const rawServers = obj.mcpServers;
153
+ for (const [name, entry] of Object.entries(rawServers)) {
154
+ const classification = classifyServerEntry(entry);
155
+ if (classification.kind === "stdio") {
156
+ const e = entry;
157
+ servers[name] = {
158
+ command: e.command,
159
+ args: Array.isArray(e.args) ? e.args : void 0,
160
+ env: typeof e.env === "object" && e.env !== null ? expandEnvValues(e.env) : void 0
161
+ };
162
+ } else if (classification.kind === "http") {
163
+ const e = entry;
164
+ const oauthResult = parseOAuthConfig(e.oauth);
165
+ if (oauthResult.reason) {
166
+ skippedServers.push({
167
+ name,
168
+ reason: oauthResult.reason
169
+ });
170
+ continue;
171
+ }
172
+ servers[name] = {
173
+ url: expandEnvVars(e.url),
174
+ headers: typeof e.headers === "object" && e.headers !== null ? expandHeaderEnvVars(e.headers) : void 0,
175
+ oauth: oauthResult.config
176
+ };
177
+ } else skippedServers.push({
178
+ name,
179
+ reason: classification.reason
180
+ });
181
+ }
182
+ const result = {};
183
+ if (Object.keys(servers).length > 0) result.mcpServers = servers;
184
+ if (skippedServers.length > 0) result.skippedServers = skippedServers;
185
+ return result;
146
186
  }
147
187
  function parseOAuthConfig(raw) {
148
- if (raw === void 0) return {};
149
- if (!raw || typeof raw !== "object") {
150
- return { reason: "Invalid OAuth config: expected an object" };
151
- }
152
- const obj = raw;
153
- if (obj.redirectUrl !== void 0 && typeof obj.redirectUrl !== "string") {
154
- return { reason: 'Invalid OAuth config: "redirectUrl" must be a string' };
155
- }
156
- let callbackPortRedirectUrl;
157
- if (obj.callbackPort !== void 0) {
158
- if (obj.redirectUrl !== void 0) {
159
- return { reason: 'Invalid OAuth config: set either "redirectUrl" or "callbackPort", not both' };
160
- }
161
- if (typeof obj.callbackPort !== "number" || !Number.isInteger(obj.callbackPort) || obj.callbackPort < 1 || obj.callbackPort > 65535) {
162
- return { reason: 'Invalid OAuth config: "callbackPort" must be an integer between 1 and 65535' };
163
- }
164
- callbackPortRedirectUrl = resolveOAuthRedirectUrl({ callbackPort: obj.callbackPort });
165
- }
166
- const rawRedirectUrl = callbackPortRedirectUrl ?? obj.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;
167
- try {
168
- const redirectUrl = new URL(rawRedirectUrl);
169
- const isLoopback = isLoopbackHostname(redirectUrl.hostname);
170
- if (redirectUrl.protocol !== "https:" && !(redirectUrl.protocol === "http:" && isLoopback)) {
171
- return { reason: "Invalid OAuth redirectUrl: must use HTTPS unless it is a loopback HTTP URL" };
172
- }
173
- } catch {
174
- return { reason: `Invalid OAuth redirectUrl: "${rawRedirectUrl}"` };
175
- }
176
- if (obj.scopes !== void 0 && (!Array.isArray(obj.scopes) || obj.scopes.some((scope) => typeof scope !== "string"))) {
177
- return { reason: 'Invalid OAuth config: "scopes" must be an array of strings' };
178
- }
179
- return {
180
- config: {
181
- redirectUrl: rawRedirectUrl,
182
- clientName: typeof obj.clientName === "string" ? obj.clientName : void 0,
183
- scopes: obj.scopes,
184
- clientId: typeof obj.clientId === "string" ? obj.clientId : void 0,
185
- clientSecret: typeof obj.clientSecret === "string" ? obj.clientSecret : void 0
186
- }
187
- };
188
- }
188
+ if (raw === void 0) return {};
189
+ if (!raw || typeof raw !== "object") return { reason: "Invalid OAuth config: expected an object" };
190
+ const obj = raw;
191
+ if (obj.redirectUrl !== void 0 && typeof obj.redirectUrl !== "string") return { reason: "Invalid OAuth config: \"redirectUrl\" must be a string" };
192
+ let callbackPortRedirectUrl;
193
+ if (obj.callbackPort !== void 0) {
194
+ if (obj.redirectUrl !== void 0) return { reason: "Invalid OAuth config: set either \"redirectUrl\" or \"callbackPort\", not both" };
195
+ if (typeof obj.callbackPort !== "number" || !Number.isInteger(obj.callbackPort) || obj.callbackPort < 1 || obj.callbackPort > 65535) return { reason: "Invalid OAuth config: \"callbackPort\" must be an integer between 1 and 65535" };
196
+ callbackPortRedirectUrl = resolveOAuthRedirectUrl({ callbackPort: obj.callbackPort });
197
+ }
198
+ const rawRedirectUrl = callbackPortRedirectUrl ?? obj.redirectUrl ?? "http://127.0.0.1:1458/oauth/callback";
199
+ try {
200
+ const redirectUrl = new URL(rawRedirectUrl);
201
+ const isLoopback = isLoopbackHostname(redirectUrl.hostname);
202
+ if (redirectUrl.protocol !== "https:" && !(redirectUrl.protocol === "http:" && isLoopback)) return { reason: "Invalid OAuth redirectUrl: must use HTTPS unless it is a loopback HTTP URL" };
203
+ } catch {
204
+ return { reason: `Invalid OAuth redirectUrl: "${rawRedirectUrl}"` };
205
+ }
206
+ if (obj.scopes !== void 0 && (!Array.isArray(obj.scopes) || obj.scopes.some((scope) => typeof scope !== "string"))) return { reason: "Invalid OAuth config: \"scopes\" must be an array of strings" };
207
+ return { config: {
208
+ redirectUrl: rawRedirectUrl,
209
+ clientName: typeof obj.clientName === "string" ? obj.clientName : void 0,
210
+ scopes: obj.scopes,
211
+ clientId: typeof obj.clientId === "string" ? obj.clientId : void 0,
212
+ clientSecret: typeof obj.clientSecret === "string" ? obj.clientSecret : void 0
213
+ } };
214
+ }
215
+ /**
216
+ * Merge configs: claude (lowest priority) < global < project (highest).
217
+ * Later configs override earlier by server name.
218
+ * Skipped entries are accumulated, but if a higher-priority config provides
219
+ * a valid entry for a skipped name, the skip is removed.
220
+ */
189
221
  function mergeConfigs(...configs) {
190
- const merged = {};
191
- const allSkipped = [];
192
- for (const config of configs) {
193
- if (config.mcpServers) {
194
- for (const [name, server] of Object.entries(config.mcpServers)) {
195
- merged[name] = server;
196
- }
197
- }
198
- if (config.skippedServers) {
199
- allSkipped.push(...config.skippedServers);
200
- }
201
- }
202
- const validNames = new Set(Object.keys(merged));
203
- const filteredSkipped = allSkipped.filter((s) => !validNames.has(s.name));
204
- const skippedMap = /* @__PURE__ */ new Map();
205
- for (const s of filteredSkipped) {
206
- skippedMap.set(s.name, s);
207
- }
208
- const result = {};
209
- if (Object.keys(merged).length > 0) {
210
- result.mcpServers = merged;
211
- }
212
- if (skippedMap.size > 0) {
213
- result.skippedServers = Array.from(skippedMap.values());
214
- }
215
- return result;
216
- }
217
- export {
218
- DEFAULT_OAUTH_REDIRECT_URL,
219
- classifyServerEntry,
220
- expandEnvVars,
221
- getClaudeSettingsPath,
222
- getGlobalMcpPath,
223
- getProjectMcpPath,
224
- getRootMcpPath,
225
- loadMcpConfig,
226
- resolveOAuthRedirectUrl,
227
- validateConfig
228
- };
222
+ const merged = {};
223
+ const allSkipped = [];
224
+ for (const config of configs) {
225
+ if (config.mcpServers) for (const [name, server] of Object.entries(config.mcpServers)) merged[name] = server;
226
+ if (config.skippedServers) allSkipped.push(...config.skippedServers);
227
+ }
228
+ const validNames = new Set(Object.keys(merged));
229
+ const filteredSkipped = allSkipped.filter((s) => !validNames.has(s.name));
230
+ const skippedMap = /* @__PURE__ */ new Map();
231
+ for (const s of filteredSkipped) skippedMap.set(s.name, s);
232
+ const result = {};
233
+ if (Object.keys(merged).length > 0) result.mcpServers = merged;
234
+ if (skippedMap.size > 0) result.skippedServers = Array.from(skippedMap.values());
235
+ return result;
236
+ }
237
+ //#endregion
238
+ export { DEFAULT_OAUTH_REDIRECT_URL, classifyServerEntry, expandEnvVars, getClaudeSettingsPath, getGlobalMcpPath, getProjectMcpPath, getRootMcpPath, loadMcpConfig, resolveOAuthRedirectUrl, validateConfig };
239
+
229
240
  //# sourceMappingURL=config.js.map