@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/mcp/config.ts"],"sourcesContent":["/**\n * MCP server configuration loading from filesystem.\n * Loads from:\n * 1. .claude/settings.local.json (Claude Code compat — lowest priority)\n * 2. ~/.mastracode/mcp.json (global)\n * 3. .mcp.json (project root — Claude Code compatible)\n * 4. .mastracode/mcp.json (project — highest priority)\n *\n * Higher-priority configs override lower ones by server name. The project root\n * `.mcp.json` is read so a project that already keeps MCP servers there for\n * Claude Code does not need to duplicate them under `.mastracode/`.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport type { McpConfig, McpHttpOAuthConfig, McpServerConfig, McpSkippedServer } from './types.js';\n\n/**\n * Default OAuth redirect URL for servers that do not configure one.\n * Port 1458 is stable across sessions so persisted tokens keep the same\n * storage fingerprint; it sits clear of the ports the Codex login flow\n * reserves (1455/1457). When 1458 is busy the callback server falls back\n * to the next sequential port, which stays covered by the client\n * registration (see `@mastra/mcp`'s `getCallbackUrlCandidates`).\n */\nexport const DEFAULT_OAUTH_REDIRECT_URL = 'http://127.0.0.1:1458/oauth/callback';\n\n// Matches the entire 127.0.0.0/8 range in dotted-quad form. `URL` normalizes\n// IPv4 hosts to four octets (so `127.1` becomes `127.0.0.1`), so anchoring the\n// pattern is enough — and it rejects lookalikes like `127.evil.com` that a\n// `startsWith('127.')` check would wrongly accept.\nconst LOOPBACK_IPV4 = /^127\\.(?:\\d{1,3})\\.(?:\\d{1,3})\\.(?:\\d{1,3})$/;\n\nfunction isLoopbackHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '[::1]' || hostname === '::1' || LOOPBACK_IPV4.test(hostname);\n}\n\n/**\n * Resolve the effective OAuth redirect URL for a server config.\n *\n * `callbackPort` (the Claude Code / Codex convention) takes precedence and\n * synthesizes `http://localhost:<port>/callback`. Config-file entries are\n * normalized by `parseOAuthConfig` (which also enforces that `callbackPort`\n * and `redirectUrl` are mutually exclusive), but programmatically registered\n * servers bypass the parser, so every consumer of the redirect URL must\n * resolve it through this helper rather than reading `redirectUrl` directly.\n */\nexport function resolveOAuthRedirectUrl(oauth: McpHttpOAuthConfig | undefined): string {\n if (oauth?.callbackPort !== undefined) {\n return `http://localhost:${oauth.callbackPort}/callback`;\n }\n return oauth?.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;\n}\n\nexport function loadMcpConfig(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): McpConfig {\n const claudeConfig = loadClaudeSettings(projectDir);\n const globalConfig = loadSingleConfig(getGlobalMcpPath(configDirName));\n const rootConfig = loadSingleConfig(getRootMcpPath(projectDir));\n const projectConfig = loadSingleConfig(getProjectMcpPath(projectDir, configDirName));\n\n return mergeConfigs(claudeConfig, globalConfig, rootConfig, projectConfig);\n}\n\nexport function getProjectMcpPath(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(projectDir, configDirName, 'mcp.json');\n}\n\nexport function getRootMcpPath(projectDir: string): string {\n return path.join(projectDir, '.mcp.json');\n}\n\nexport function getGlobalMcpPath(configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(os.homedir(), configDirName, 'mcp.json');\n}\n\nexport function getClaudeSettingsPath(projectDir: string): string {\n return path.join(projectDir, '.claude', 'settings.local.json');\n}\n\nfunction loadSingleConfig(filePath: string): McpConfig {\n try {\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8');\n return validateConfig(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction loadClaudeSettings(projectDir: string): McpConfig {\n try {\n const filePath = getClaudeSettingsPath(projectDir);\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n // Claude Code stores mcpServers at the top level of settings\n if (parsed?.mcpServers && typeof parsed.mcpServers === 'object') {\n return validateConfig({ mcpServers: parsed.mcpServers });\n }\n return {};\n } catch {\n return {};\n }\n}\n\n/**\n * Expand `${VAR}` and `${VAR:-default}` references in a string from the\n * environment, matching how Claude Code resolves values in `.mcp.json`.\n * A referenced variable that is unset or empty falls back to its default,\n * or to an empty string when no default is given.\n */\nexport function expandEnvVars(value: string, env: NodeJS.ProcessEnv = process.env): string {\n return value.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n (_match, bracedName, fallback, bareName) => {\n const name = bracedName ?? bareName;\n const resolved = env[name];\n if (resolved !== undefined && resolved !== '') return resolved;\n return fallback ?? '';\n },\n );\n}\n\n/**\n * Expand `${VAR}` and `$VAR` references in every string-valued HTTP header so that\n * secrets like API keys can be referenced from the environment instead of\n * being hardcoded in `mcp.json`.\n */\nfunction expandHeaderEnvVars(headers: Record<string, unknown>): Record<string, string> {\n const expanded: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (typeof value === 'string') expanded[key] = expandEnvVars(value);\n }\n return expanded;\n}\n\n/**\n * Expand `${VAR}` and `$VAR` references in every string-valued environment\n * variable passed to a stdio server, so that secrets can be referenced from the\n * host environment instead of being hardcoded in `mcp.json`.\n */\nfunction expandEnvValues(env: Record<string, unknown>): Record<string, string> {\n const expanded: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (typeof value === 'string') expanded[key] = expandEnvVars(value);\n }\n return expanded;\n}\n\n/**\n * Classify a raw server entry as stdio, http, or skip (with reason).\n */\nexport function classifyServerEntry(raw: unknown): { kind: 'stdio' | 'http' | 'skip'; reason?: string } {\n if (!raw || typeof raw !== 'object') {\n return { kind: 'skip', reason: 'Invalid entry: expected an object' };\n }\n\n const obj = raw as Record<string, unknown>;\n const hasCommand = typeof obj.command === 'string';\n const hasUrl = typeof obj.url === 'string';\n\n if (hasCommand && hasUrl) {\n return { kind: 'skip', reason: 'Cannot specify both \"command\" and \"url\"' };\n }\n\n if (hasCommand) {\n return { kind: 'stdio' };\n }\n\n if (hasUrl) {\n try {\n new URL(expandEnvVars(obj.url as string));\n } catch {\n return { kind: 'skip', reason: `Invalid URL: \"${obj.url}\"` };\n }\n return { kind: 'http' };\n }\n\n return { kind: 'skip', reason: 'Missing required field: \"command\" (stdio) or \"url\" (http)' };\n}\n\nexport function validateConfig(raw: unknown): McpConfig {\n if (!raw || typeof raw !== 'object') return {};\n const obj = raw as Record<string, unknown>;\n\n if (!obj.mcpServers || typeof obj.mcpServers !== 'object') return {};\n\n const servers: Record<string, McpServerConfig> = {};\n const skippedServers: McpSkippedServer[] = [];\n const rawServers = obj.mcpServers as Record<string, unknown>;\n\n for (const [name, entry] of Object.entries(rawServers)) {\n const classification = classifyServerEntry(entry);\n\n if (classification.kind === 'stdio') {\n const e = entry as Record<string, unknown>;\n servers[name] = {\n command: e.command as string,\n args: Array.isArray(e.args) ? (e.args as string[]) : undefined,\n env:\n typeof e.env === 'object' && e.env !== null ? expandEnvValues(e.env as Record<string, unknown>) : undefined,\n };\n } else if (classification.kind === 'http') {\n const e = entry as Record<string, unknown>;\n const oauthResult = parseOAuthConfig(e.oauth);\n if (oauthResult.reason) {\n skippedServers.push({ name, reason: oauthResult.reason });\n continue;\n }\n servers[name] = {\n url: expandEnvVars(e.url as string),\n headers:\n typeof e.headers === 'object' && e.headers !== null\n ? expandHeaderEnvVars(e.headers as Record<string, unknown>)\n : undefined,\n oauth: oauthResult.config,\n };\n } else {\n skippedServers.push({ name, reason: classification.reason! });\n }\n }\n\n const result: McpConfig = {};\n if (Object.keys(servers).length > 0) {\n result.mcpServers = servers;\n }\n if (skippedServers.length > 0) {\n result.skippedServers = skippedServers;\n }\n return result;\n}\n\nfunction parseOAuthConfig(raw: unknown): { config?: McpHttpOAuthConfig; reason?: string } {\n if (raw === undefined) return {};\n if (!raw || typeof raw !== 'object') {\n return { reason: 'Invalid OAuth config: expected an object' };\n }\n\n const obj = raw as Record<string, unknown>;\n if (obj.redirectUrl !== undefined && typeof obj.redirectUrl !== 'string') {\n return { reason: 'Invalid OAuth config: \"redirectUrl\" must be a string' };\n }\n // `callbackPort` is a shorthand for a loopback redirect URL. It synthesizes\n // `http://localhost:<port>/callback` — the convention Claude Code and Codex\n // emit — so those clients' config (e.g. Slack's official MCP plugin config)\n // can be pasted verbatim. `redirectUrl` stays as the full-control escape\n // hatch (any loopback host/path). The two are mutually exclusive to avoid\n // an ambiguous redirect.\n let callbackPortRedirectUrl: string | undefined;\n if (obj.callbackPort !== undefined) {\n if (obj.redirectUrl !== undefined) {\n return { reason: 'Invalid OAuth config: set either \"redirectUrl\" or \"callbackPort\", not both' };\n }\n if (\n typeof obj.callbackPort !== 'number' ||\n !Number.isInteger(obj.callbackPort) ||\n obj.callbackPort < 1 ||\n obj.callbackPort > 65535\n ) {\n return { reason: 'Invalid OAuth config: \"callbackPort\" must be an integer between 1 and 65535' };\n }\n callbackPortRedirectUrl = resolveOAuthRedirectUrl({ callbackPort: obj.callbackPort });\n }\n\n const rawRedirectUrl = callbackPortRedirectUrl ?? obj.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;\n try {\n const redirectUrl = new URL(rawRedirectUrl);\n const isLoopback = isLoopbackHostname(redirectUrl.hostname);\n if (redirectUrl.protocol !== 'https:' && !(redirectUrl.protocol === 'http:' && isLoopback)) {\n return { reason: 'Invalid OAuth redirectUrl: must use HTTPS unless it is a loopback HTTP URL' };\n }\n } catch {\n return { reason: `Invalid OAuth redirectUrl: \"${rawRedirectUrl}\"` };\n }\n\n if (obj.scopes !== undefined && (!Array.isArray(obj.scopes) || obj.scopes.some(scope => typeof scope !== 'string'))) {\n return { reason: 'Invalid OAuth config: \"scopes\" must be an array of strings' };\n }\n\n return {\n config: {\n redirectUrl: rawRedirectUrl,\n clientName: typeof obj.clientName === 'string' ? obj.clientName : undefined,\n scopes: obj.scopes as string[] | undefined,\n clientId: typeof obj.clientId === 'string' ? obj.clientId : undefined,\n clientSecret: typeof obj.clientSecret === 'string' ? obj.clientSecret : undefined,\n },\n };\n}\n\n/**\n * Merge configs: claude (lowest priority) < global < project (highest).\n * Later configs override earlier by server name.\n * Skipped entries are accumulated, but if a higher-priority config provides\n * a valid entry for a skipped name, the skip is removed.\n */\nfunction mergeConfigs(...configs: McpConfig[]): McpConfig {\n const merged: Record<string, McpServerConfig> = {};\n const allSkipped: McpSkippedServer[] = [];\n\n for (const config of configs) {\n if (config.mcpServers) {\n for (const [name, server] of Object.entries(config.mcpServers)) {\n merged[name] = server;\n }\n }\n if (config.skippedServers) {\n allSkipped.push(...config.skippedServers);\n }\n }\n\n // Remove skipped entries that were resolved by a valid config at any priority\n const validNames = new Set(Object.keys(merged));\n const filteredSkipped = allSkipped.filter(s => !validNames.has(s.name));\n\n // Deduplicate skipped entries by name (keep last occurrence — highest priority reason)\n const skippedMap = new Map<string, McpSkippedServer>();\n for (const s of filteredSkipped) {\n skippedMap.set(s.name, s);\n }\n\n const result: McpConfig = {};\n if (Object.keys(merged).length > 0) {\n result.mcpServers = merged;\n }\n if (skippedMap.size > 0) {\n result.skippedServers = Array.from(skippedMap.values());\n }\n return result;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"config.js","names":["path","os","fs"],"sources":["../../src/mcp/config.ts"],"sourcesContent":["/**\n * MCP server configuration loading from filesystem.\n * Loads from:\n * 1. .claude/settings.local.json (Claude Code compat — lowest priority)\n * 2. ~/.mastracode/mcp.json (global)\n * 3. .mcp.json (project root — Claude Code compatible)\n * 4. .mastracode/mcp.json (project — highest priority)\n *\n * Higher-priority configs override lower ones by server name. The project root\n * `.mcp.json` is read so a project that already keeps MCP servers there for\n * Claude Code does not need to duplicate them under `.mastracode/`.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport type { McpConfig, McpHttpOAuthConfig, McpServerConfig, McpSkippedServer } from './types.js';\n\n/**\n * Default OAuth redirect URL for servers that do not configure one.\n * Port 1458 is stable across sessions so persisted tokens keep the same\n * storage fingerprint; it sits clear of the ports the Codex login flow\n * reserves (1455/1457). When 1458 is busy the callback server falls back\n * to the next sequential port, which stays covered by the client\n * registration (see `@mastra/mcp`'s `getCallbackUrlCandidates`).\n */\nexport const DEFAULT_OAUTH_REDIRECT_URL = 'http://127.0.0.1:1458/oauth/callback';\n\n// Matches the entire 127.0.0.0/8 range in dotted-quad form. `URL` normalizes\n// IPv4 hosts to four octets (so `127.1` becomes `127.0.0.1`), so anchoring the\n// pattern is enough — and it rejects lookalikes like `127.evil.com` that a\n// `startsWith('127.')` check would wrongly accept.\nconst LOOPBACK_IPV4 = /^127\\.(?:\\d{1,3})\\.(?:\\d{1,3})\\.(?:\\d{1,3})$/;\n\nfunction isLoopbackHostname(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '[::1]' || hostname === '::1' || LOOPBACK_IPV4.test(hostname);\n}\n\n/**\n * Resolve the effective OAuth redirect URL for a server config.\n *\n * `callbackPort` (the Claude Code / Codex convention) takes precedence and\n * synthesizes `http://localhost:<port>/callback`. Config-file entries are\n * normalized by `parseOAuthConfig` (which also enforces that `callbackPort`\n * and `redirectUrl` are mutually exclusive), but programmatically registered\n * servers bypass the parser, so every consumer of the redirect URL must\n * resolve it through this helper rather than reading `redirectUrl` directly.\n */\nexport function resolveOAuthRedirectUrl(oauth: McpHttpOAuthConfig | undefined): string {\n if (oauth?.callbackPort !== undefined) {\n return `http://localhost:${oauth.callbackPort}/callback`;\n }\n return oauth?.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;\n}\n\nexport function loadMcpConfig(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): McpConfig {\n const claudeConfig = loadClaudeSettings(projectDir);\n const globalConfig = loadSingleConfig(getGlobalMcpPath(configDirName));\n const rootConfig = loadSingleConfig(getRootMcpPath(projectDir));\n const projectConfig = loadSingleConfig(getProjectMcpPath(projectDir, configDirName));\n\n return mergeConfigs(claudeConfig, globalConfig, rootConfig, projectConfig);\n}\n\nexport function getProjectMcpPath(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(projectDir, configDirName, 'mcp.json');\n}\n\nexport function getRootMcpPath(projectDir: string): string {\n return path.join(projectDir, '.mcp.json');\n}\n\nexport function getGlobalMcpPath(configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(os.homedir(), configDirName, 'mcp.json');\n}\n\nexport function getClaudeSettingsPath(projectDir: string): string {\n return path.join(projectDir, '.claude', 'settings.local.json');\n}\n\nfunction loadSingleConfig(filePath: string): McpConfig {\n try {\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8');\n return validateConfig(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction loadClaudeSettings(projectDir: string): McpConfig {\n try {\n const filePath = getClaudeSettingsPath(projectDir);\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(raw);\n // Claude Code stores mcpServers at the top level of settings\n if (parsed?.mcpServers && typeof parsed.mcpServers === 'object') {\n return validateConfig({ mcpServers: parsed.mcpServers });\n }\n return {};\n } catch {\n return {};\n }\n}\n\n/**\n * Expand `${VAR}` and `${VAR:-default}` references in a string from the\n * environment, matching how Claude Code resolves values in `.mcp.json`.\n * A referenced variable that is unset or empty falls back to its default,\n * or to an empty string when no default is given.\n */\nexport function expandEnvVars(value: string, env: NodeJS.ProcessEnv = process.env): string {\n return value.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n (_match, bracedName, fallback, bareName) => {\n const name = bracedName ?? bareName;\n const resolved = env[name];\n if (resolved !== undefined && resolved !== '') return resolved;\n return fallback ?? '';\n },\n );\n}\n\n/**\n * Expand `${VAR}` and `$VAR` references in every string-valued HTTP header so that\n * secrets like API keys can be referenced from the environment instead of\n * being hardcoded in `mcp.json`.\n */\nfunction expandHeaderEnvVars(headers: Record<string, unknown>): Record<string, string> {\n const expanded: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (typeof value === 'string') expanded[key] = expandEnvVars(value);\n }\n return expanded;\n}\n\n/**\n * Expand `${VAR}` and `$VAR` references in every string-valued environment\n * variable passed to a stdio server, so that secrets can be referenced from the\n * host environment instead of being hardcoded in `mcp.json`.\n */\nfunction expandEnvValues(env: Record<string, unknown>): Record<string, string> {\n const expanded: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (typeof value === 'string') expanded[key] = expandEnvVars(value);\n }\n return expanded;\n}\n\n/**\n * Classify a raw server entry as stdio, http, or skip (with reason).\n */\nexport function classifyServerEntry(raw: unknown): { kind: 'stdio' | 'http' | 'skip'; reason?: string } {\n if (!raw || typeof raw !== 'object') {\n return { kind: 'skip', reason: 'Invalid entry: expected an object' };\n }\n\n const obj = raw as Record<string, unknown>;\n const hasCommand = typeof obj.command === 'string';\n const hasUrl = typeof obj.url === 'string';\n\n if (hasCommand && hasUrl) {\n return { kind: 'skip', reason: 'Cannot specify both \"command\" and \"url\"' };\n }\n\n if (hasCommand) {\n return { kind: 'stdio' };\n }\n\n if (hasUrl) {\n try {\n new URL(expandEnvVars(obj.url as string));\n } catch {\n return { kind: 'skip', reason: `Invalid URL: \"${obj.url}\"` };\n }\n return { kind: 'http' };\n }\n\n return { kind: 'skip', reason: 'Missing required field: \"command\" (stdio) or \"url\" (http)' };\n}\n\nexport function validateConfig(raw: unknown): McpConfig {\n if (!raw || typeof raw !== 'object') return {};\n const obj = raw as Record<string, unknown>;\n\n if (!obj.mcpServers || typeof obj.mcpServers !== 'object') return {};\n\n const servers: Record<string, McpServerConfig> = {};\n const skippedServers: McpSkippedServer[] = [];\n const rawServers = obj.mcpServers as Record<string, unknown>;\n\n for (const [name, entry] of Object.entries(rawServers)) {\n const classification = classifyServerEntry(entry);\n\n if (classification.kind === 'stdio') {\n const e = entry as Record<string, unknown>;\n servers[name] = {\n command: e.command as string,\n args: Array.isArray(e.args) ? (e.args as string[]) : undefined,\n env:\n typeof e.env === 'object' && e.env !== null ? expandEnvValues(e.env as Record<string, unknown>) : undefined,\n };\n } else if (classification.kind === 'http') {\n const e = entry as Record<string, unknown>;\n const oauthResult = parseOAuthConfig(e.oauth);\n if (oauthResult.reason) {\n skippedServers.push({ name, reason: oauthResult.reason });\n continue;\n }\n servers[name] = {\n url: expandEnvVars(e.url as string),\n headers:\n typeof e.headers === 'object' && e.headers !== null\n ? expandHeaderEnvVars(e.headers as Record<string, unknown>)\n : undefined,\n oauth: oauthResult.config,\n };\n } else {\n skippedServers.push({ name, reason: classification.reason! });\n }\n }\n\n const result: McpConfig = {};\n if (Object.keys(servers).length > 0) {\n result.mcpServers = servers;\n }\n if (skippedServers.length > 0) {\n result.skippedServers = skippedServers;\n }\n return result;\n}\n\nfunction parseOAuthConfig(raw: unknown): { config?: McpHttpOAuthConfig; reason?: string } {\n if (raw === undefined) return {};\n if (!raw || typeof raw !== 'object') {\n return { reason: 'Invalid OAuth config: expected an object' };\n }\n\n const obj = raw as Record<string, unknown>;\n if (obj.redirectUrl !== undefined && typeof obj.redirectUrl !== 'string') {\n return { reason: 'Invalid OAuth config: \"redirectUrl\" must be a string' };\n }\n // `callbackPort` is a shorthand for a loopback redirect URL. It synthesizes\n // `http://localhost:<port>/callback` — the convention Claude Code and Codex\n // emit — so those clients' config (e.g. Slack's official MCP plugin config)\n // can be pasted verbatim. `redirectUrl` stays as the full-control escape\n // hatch (any loopback host/path). The two are mutually exclusive to avoid\n // an ambiguous redirect.\n let callbackPortRedirectUrl: string | undefined;\n if (obj.callbackPort !== undefined) {\n if (obj.redirectUrl !== undefined) {\n return { reason: 'Invalid OAuth config: set either \"redirectUrl\" or \"callbackPort\", not both' };\n }\n if (\n typeof obj.callbackPort !== 'number' ||\n !Number.isInteger(obj.callbackPort) ||\n obj.callbackPort < 1 ||\n obj.callbackPort > 65535\n ) {\n return { reason: 'Invalid OAuth config: \"callbackPort\" must be an integer between 1 and 65535' };\n }\n callbackPortRedirectUrl = resolveOAuthRedirectUrl({ callbackPort: obj.callbackPort });\n }\n\n const rawRedirectUrl = callbackPortRedirectUrl ?? obj.redirectUrl ?? DEFAULT_OAUTH_REDIRECT_URL;\n try {\n const redirectUrl = new URL(rawRedirectUrl);\n const isLoopback = isLoopbackHostname(redirectUrl.hostname);\n if (redirectUrl.protocol !== 'https:' && !(redirectUrl.protocol === 'http:' && isLoopback)) {\n return { reason: 'Invalid OAuth redirectUrl: must use HTTPS unless it is a loopback HTTP URL' };\n }\n } catch {\n return { reason: `Invalid OAuth redirectUrl: \"${rawRedirectUrl}\"` };\n }\n\n if (obj.scopes !== undefined && (!Array.isArray(obj.scopes) || obj.scopes.some(scope => typeof scope !== 'string'))) {\n return { reason: 'Invalid OAuth config: \"scopes\" must be an array of strings' };\n }\n\n return {\n config: {\n redirectUrl: rawRedirectUrl,\n clientName: typeof obj.clientName === 'string' ? obj.clientName : undefined,\n scopes: obj.scopes as string[] | undefined,\n clientId: typeof obj.clientId === 'string' ? obj.clientId : undefined,\n clientSecret: typeof obj.clientSecret === 'string' ? obj.clientSecret : undefined,\n },\n };\n}\n\n/**\n * Merge configs: claude (lowest priority) < global < project (highest).\n * Later configs override earlier by server name.\n * Skipped entries are accumulated, but if a higher-priority config provides\n * a valid entry for a skipped name, the skip is removed.\n */\nfunction mergeConfigs(...configs: McpConfig[]): McpConfig {\n const merged: Record<string, McpServerConfig> = {};\n const allSkipped: McpSkippedServer[] = [];\n\n for (const config of configs) {\n if (config.mcpServers) {\n for (const [name, server] of Object.entries(config.mcpServers)) {\n merged[name] = server;\n }\n }\n if (config.skippedServers) {\n allSkipped.push(...config.skippedServers);\n }\n }\n\n // Remove skipped entries that were resolved by a valid config at any priority\n const validNames = new Set(Object.keys(merged));\n const filteredSkipped = allSkipped.filter(s => !validNames.has(s.name));\n\n // Deduplicate skipped entries by name (keep last occurrence — highest priority reason)\n const skippedMap = new Map<string, McpSkippedServer>();\n for (const s of filteredSkipped) {\n skippedMap.set(s.name, s);\n }\n\n const result: McpConfig = {};\n if (Object.keys(merged).length > 0) {\n result.mcpServers = merged;\n }\n if (skippedMap.size > 0) {\n result.skippedServers = Array.from(skippedMap.values());\n }\n return result;\n}\n"],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -1,10 +1,3 @@
1
+ import { getClaudeSettingsPath, getGlobalMcpPath, getProjectMcpPath, loadMcpConfig } from "./config.js";
1
2
  import { createMcpManager } from "./manager.js";
2
- import { loadMcpConfig, getProjectMcpPath, getGlobalMcpPath, getClaudeSettingsPath } from "./config.js";
3
- export {
4
- createMcpManager,
5
- getClaudeSettingsPath,
6
- getGlobalMcpPath,
7
- getProjectMcpPath,
8
- loadMcpConfig
9
- };
10
- //# sourceMappingURL=index.js.map
3
+ export { createMcpManager, getClaudeSettingsPath, getGlobalMcpPath, getProjectMcpPath, loadMcpConfig };