@mastra/code-sdk 1.7.3-alpha.0 → 1.8.0-alpha.10

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 (127) hide show
  1. package/dist/agent-connections/tools.d.ts.map +1 -1
  2. package/dist/agent-connections/tools.js +26 -8
  3. package/dist/agent-connections/tools.js.map +1 -1
  4. package/dist/agents/background-completion-events.d.ts +16 -0
  5. package/dist/agents/background-completion-events.d.ts.map +1 -0
  6. package/dist/agents/background-completion-events.js +19 -0
  7. package/dist/agents/background-completion-events.js.map +1 -0
  8. package/dist/agents/background-completion.d.ts +9 -0
  9. package/dist/agents/background-completion.d.ts.map +1 -0
  10. package/dist/agents/background-completion.js +59 -0
  11. package/dist/agents/background-completion.js.map +1 -0
  12. package/dist/agents/mastracode-gateway.d.ts +2 -0
  13. package/dist/agents/mastracode-gateway.d.ts.map +1 -1
  14. package/dist/agents/mastracode-gateway.js +29 -10
  15. package/dist/agents/mastracode-gateway.js.map +1 -1
  16. package/dist/agents/memory.d.ts +1 -1
  17. package/dist/agents/memory.d.ts.map +1 -1
  18. package/dist/agents/memory.js +32 -25
  19. package/dist/agents/memory.js.map +1 -1
  20. package/dist/agents/model.d.ts +41 -1
  21. package/dist/agents/model.d.ts.map +1 -1
  22. package/dist/agents/model.js +141 -7
  23. package/dist/agents/model.js.map +1 -1
  24. package/dist/agents/tool-availability.d.ts +2 -0
  25. package/dist/agents/tool-availability.d.ts.map +1 -1
  26. package/dist/agents/tool-availability.js +33 -4
  27. package/dist/agents/tool-availability.js.map +1 -1
  28. package/dist/agents/tools.d.ts +1 -1
  29. package/dist/agents/tools.d.ts.map +1 -1
  30. package/dist/agents/tools.js +30 -2
  31. package/dist/agents/tools.js.map +1 -1
  32. package/dist/agents/workspace.d.ts +2 -1
  33. package/dist/agents/workspace.d.ts.map +1 -1
  34. package/dist/agents/workspace.js +3 -3
  35. package/dist/agents/workspace.js.map +1 -1
  36. package/dist/auth/account-rotation-processor.d.ts +207 -0
  37. package/dist/auth/account-rotation-processor.d.ts.map +1 -0
  38. package/dist/auth/account-rotation-processor.js +669 -0
  39. package/dist/auth/account-rotation-processor.js.map +1 -0
  40. package/dist/auth/account-routing-context.d.ts +13 -0
  41. package/dist/auth/account-routing-context.d.ts.map +1 -0
  42. package/dist/auth/account-routing-context.js +41 -0
  43. package/dist/auth/account-routing-context.js.map +1 -0
  44. package/dist/auth/index.d.ts +1 -0
  45. package/dist/auth/index.d.ts.map +1 -1
  46. package/dist/auth/index.js +2 -1
  47. package/dist/auth/providers/anthropic.d.ts.map +1 -1
  48. package/dist/auth/providers/anthropic.js +2 -8
  49. package/dist/auth/providers/anthropic.js.map +1 -1
  50. package/dist/auth/providers/github-copilot.d.ts.map +1 -1
  51. package/dist/auth/providers/github-copilot.js +34 -12
  52. package/dist/auth/providers/github-copilot.js.map +1 -1
  53. package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
  54. package/dist/auth/providers/kimi-coding.js +2 -6
  55. package/dist/auth/providers/kimi-coding.js.map +1 -1
  56. package/dist/auth/providers/openai-codex.d.ts +3 -1
  57. package/dist/auth/providers/openai-codex.d.ts.map +1 -1
  58. package/dist/auth/providers/openai-codex.js +50 -24
  59. package/dist/auth/providers/openai-codex.js.map +1 -1
  60. package/dist/auth/providers/xai.d.ts +1 -1
  61. package/dist/auth/providers/xai.d.ts.map +1 -1
  62. package/dist/auth/providers/xai.js +36 -16
  63. package/dist/auth/providers/xai.js.map +1 -1
  64. package/dist/auth/storage.d.ts +130 -8
  65. package/dist/auth/storage.d.ts.map +1 -1
  66. package/dist/auth/storage.js +602 -39
  67. package/dist/auth/storage.js.map +1 -1
  68. package/dist/auth/types.d.ts +69 -2
  69. package/dist/auth/types.d.ts.map +1 -1
  70. package/dist/goal-signal.d.ts +1 -1
  71. package/dist/goal-signal.d.ts.map +1 -1
  72. package/dist/goal-signal.js +1 -1
  73. package/dist/goal-signal.js.map +1 -1
  74. package/dist/index.d.ts +4 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +155 -38
  77. package/dist/index.js.map +1 -1
  78. package/dist/mcp/config.d.ts +17 -2
  79. package/dist/mcp/config.d.ts.map +1 -1
  80. package/dist/mcp/config.js +18 -3
  81. package/dist/mcp/config.js.map +1 -1
  82. package/dist/mcp/manager.d.ts +2 -1
  83. package/dist/mcp/manager.d.ts.map +1 -1
  84. package/dist/mcp/manager.js +13 -8
  85. package/dist/mcp/manager.js.map +1 -1
  86. package/dist/mcp/types.d.ts +15 -3
  87. package/dist/mcp/types.d.ts.map +1 -1
  88. package/dist/onboarding/custom-packs.d.ts.map +1 -1
  89. package/dist/onboarding/custom-packs.js +3 -0
  90. package/dist/onboarding/custom-packs.js.map +1 -1
  91. package/dist/onboarding/index.js +1 -1
  92. package/dist/onboarding/packs.d.ts +44 -0
  93. package/dist/onboarding/packs.d.ts.map +1 -1
  94. package/dist/onboarding/packs.js +80 -2
  95. package/dist/onboarding/packs.js.map +1 -1
  96. package/dist/onboarding/settings.d.ts +33 -0
  97. package/dist/onboarding/settings.d.ts.map +1 -1
  98. package/dist/onboarding/settings.js +88 -8
  99. package/dist/onboarding/settings.js.map +1 -1
  100. package/dist/plugin.d.ts +12 -0
  101. package/dist/plugin.d.ts.map +1 -1
  102. package/dist/plugin.js.map +1 -1
  103. package/dist/plugins/loader.d.ts.map +1 -1
  104. package/dist/plugins/loader.js +2 -1
  105. package/dist/plugins/loader.js.map +1 -1
  106. package/dist/providers/claude-max.d.ts.map +1 -1
  107. package/dist/providers/claude-max.js +4 -5
  108. package/dist/providers/claude-max.js.map +1 -1
  109. package/dist/providers/github-copilot.d.ts +1 -1
  110. package/dist/providers/github-copilot.d.ts.map +1 -1
  111. package/dist/providers/github-copilot.js +89 -37
  112. package/dist/providers/github-copilot.js.map +1 -1
  113. package/dist/providers/kimi-coding.d.ts.map +1 -1
  114. package/dist/providers/kimi-coding.js +16 -2
  115. package/dist/providers/kimi-coding.js.map +1 -1
  116. package/dist/providers/openai-codex.d.ts.map +1 -1
  117. package/dist/providers/openai-codex.js +42 -27
  118. package/dist/providers/openai-codex.js.map +1 -1
  119. package/dist/schema.d.ts +28 -1
  120. package/dist/schema.d.ts.map +1 -1
  121. package/dist/schema.js +9 -0
  122. package/dist/schema.js.map +1 -1
  123. package/dist/tools/workflows/save-workflow.d.ts +1 -1
  124. package/dist/tools/workflows/save-workflow.d.ts.map +1 -1
  125. package/dist/workflows/register-primitives.js +2 -2
  126. package/dist/workflows/register-primitives.js.map +1 -1
  127. package/package.json +17 -17
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":["path","os","fs","parseToml"],"sources":["../../src/mcp/config.ts"],"sourcesContent":["/**\n * MCP server configuration loading from filesystem.\n * Loads from, lowest to highest priority:\n * 1. ~/.claude.json (Claude Code global — opt-in)\n * 2. $CODEX_HOME/config.toml (Codex CLI global — opt-in)\n * 3. .claude/settings.local.json (Claude Code project compatibility)\n * 4. ~/.mastracode/mcp.json (Mastra Code global)\n * 5. .mcp.json (project root — Claude Code compatible)\n * 6. .mastracode/mcp.json (Mastra Code project)\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 { parse as parseToml } from 'smol-toml';\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 interface ExternalMcpDiscoveryOptions {\n claudeCodeGlobal?: boolean;\n codexGlobal?: boolean;\n}\n\nexport function loadMcpConfig(\n projectDir: string,\n configDirName = DEFAULT_CONFIG_DIR,\n externalDiscovery: ExternalMcpDiscoveryOptions = {},\n): McpConfig {\n const claudeGlobalConfig = externalDiscovery.claudeCodeGlobal ? loadClaudeGlobalConfig() : {};\n const codexGlobalConfig = externalDiscovery.codexGlobal ? loadCodexGlobalConfig() : {};\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(claudeGlobalConfig, codexGlobalConfig, 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\nexport function getClaudeGlobalMcpPath(): string {\n return path.join(os.homedir(), '.claude.json');\n}\n\nexport function getCodexGlobalMcpPath(): string {\n const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');\n return path.join(codexHome, 'config.toml');\n}\n\nfunction withoutSkippedServers(config: McpConfig): McpConfig {\n return config.mcpServers ? { mcpServers: config.mcpServers } : {};\n}\n\nfunction loadClaudeGlobalConfig(): McpConfig {\n try {\n const filePath = getClaudeGlobalMcpPath();\n if (!fs.existsSync(filePath)) return {};\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n return withoutSkippedServers(validateConfig({ mcpServers: parsed?.mcpServers }));\n } catch {\n return {};\n }\n}\n\nfunction stringRecord(value: unknown): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n const entries = Object.entries(value as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nfunction normalizeCodexServer(entry: unknown): Record<string, unknown> | undefined {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return undefined;\n const raw = entry as Record<string, unknown>;\n if (raw.enabled === false) return undefined;\n\n if (typeof raw.command === 'string') {\n return {\n command: raw.command,\n args: Array.isArray(raw.args) && raw.args.every(arg => typeof arg === 'string') ? raw.args : undefined,\n env: stringRecord(raw.env),\n };\n }\n\n if (typeof raw.url === 'string') {\n const headers = stringRecord(raw.http_headers) ?? {};\n if (\n typeof raw.bearer_token_env_var === 'string' &&\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(raw.bearer_token_env_var) &&\n !Object.keys(headers).some(name => name.toLowerCase() === 'authorization')\n ) {\n headers.Authorization = `Bearer \\${${raw.bearer_token_env_var}}`;\n }\n return {\n url: raw.url,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n };\n }\n\n return undefined;\n}\n\nfunction loadCodexGlobalConfig(): McpConfig {\n try {\n const filePath = getCodexGlobalMcpPath();\n if (!fs.existsSync(filePath)) return {};\n const parsed = parseToml(fs.readFileSync(filePath, 'utf-8')) as Record<string, unknown>;\n const rawServers = parsed.mcp_servers;\n if (!rawServers || typeof rawServers !== 'object' || Array.isArray(rawServers)) return {};\n\n const mcpServers: Record<string, unknown> = {};\n for (const [name, entry] of Object.entries(rawServers as Record<string, unknown>)) {\n const normalized = normalizeCodexServer(entry);\n if (normalized) mcpServers[name] = normalized;\n }\n return withoutSkippedServers(validateConfig({ mcpServers }));\n } catch {\n return {};\n }\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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"}
1
+ {"version":3,"file":"config.js","names":["path","os","fs","parseToml"],"sources":["../../src/mcp/config.ts"],"sourcesContent":["/**\n * MCP server configuration loading from filesystem.\n * Loads from, lowest to highest priority:\n * 1. ~/.claude.json (Claude Code global — opt-in)\n * 2. $CODEX_HOME/config.toml (Codex CLI global — opt-in)\n * 3. .claude/settings.local.json (Claude Code project compatibility)\n * 4. ~/.mastracode/mcp.json (Mastra Code global)\n * 5. .mcp.json (project root — Claude Code compatible)\n * 6. .mastracode/mcp.json (Mastra Code project)\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 { parse as parseToml } from 'smol-toml';\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 (see `@mastra/mcp`'s `getCallbackUrlCandidates`).\n * No registration covers those fallbacks: the hosted Client ID Metadata\n * Document at MASTRA_CODE_CLIENT_METADATA_URL must list every candidate URL,\n * so changing this default or the fallback range means republishing it.\n */\nexport const DEFAULT_OAUTH_REDIRECT_URL = 'http://127.0.0.1:1458/oauth/callback';\n\n/**\n * Mastra Code's Client ID Metadata Document (SEP-991 / OAuth Client ID\n * Metadata Documents). Servers without a pre-registered `clientId` identify\n * this client by the document URL itself: the authorization server fetches it\n * to learn the client name and loopback redirect URIs, so no client\n * registration step is needed. The document is served by the Mastra Code\n * docs site (mastra-ai/mastracode-website, `static/.well-known/oauth-client/`)\n * and lists the DEFAULT_OAUTH_REDIRECT_URL fallback candidates. A custom\n * `redirectUrl` or `callbackPort` is only accepted under this identity by\n * authorization servers that ignore the loopback port (RFC 8252 §7.3);\n * otherwise pair it with a pre-registered `clientId`.\n */\nexport const MASTRA_CODE_CLIENT_METADATA_URL = 'https://code.mastra.ai/.well-known/oauth-client/mastracode.json';\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 interface ExternalMcpDiscoveryOptions {\n claudeCodeGlobal?: boolean;\n codexGlobal?: boolean;\n}\n\nexport function loadMcpConfig(\n projectDir: string,\n configDirName = DEFAULT_CONFIG_DIR,\n externalDiscovery: ExternalMcpDiscoveryOptions = {},\n): McpConfig {\n const claudeGlobalConfig = externalDiscovery.claudeCodeGlobal ? loadClaudeGlobalConfig() : {};\n const codexGlobalConfig = externalDiscovery.codexGlobal ? loadCodexGlobalConfig() : {};\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(claudeGlobalConfig, codexGlobalConfig, 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\nexport function getClaudeGlobalMcpPath(): string {\n return path.join(os.homedir(), '.claude.json');\n}\n\nexport function getCodexGlobalMcpPath(): string {\n const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');\n return path.join(codexHome, 'config.toml');\n}\n\nfunction withoutSkippedServers(config: McpConfig): McpConfig {\n return config.mcpServers ? { mcpServers: config.mcpServers } : {};\n}\n\nfunction loadClaudeGlobalConfig(): McpConfig {\n try {\n const filePath = getClaudeGlobalMcpPath();\n if (!fs.existsSync(filePath)) return {};\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n return withoutSkippedServers(validateConfig({ mcpServers: parsed?.mcpServers }));\n } catch {\n return {};\n }\n}\n\nfunction stringRecord(value: unknown): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n const entries = Object.entries(value as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nfunction normalizeCodexServer(entry: unknown): Record<string, unknown> | undefined {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return undefined;\n const raw = entry as Record<string, unknown>;\n if (raw.enabled === false) return undefined;\n\n if (typeof raw.command === 'string') {\n return {\n command: raw.command,\n args: Array.isArray(raw.args) && raw.args.every(arg => typeof arg === 'string') ? raw.args : undefined,\n env: stringRecord(raw.env),\n };\n }\n\n if (typeof raw.url === 'string') {\n const headers = stringRecord(raw.http_headers) ?? {};\n if (\n typeof raw.bearer_token_env_var === 'string' &&\n /^[A-Za-z_][A-Za-z0-9_]*$/.test(raw.bearer_token_env_var) &&\n !Object.keys(headers).some(name => name.toLowerCase() === 'authorization')\n ) {\n headers.Authorization = `Bearer \\${${raw.bearer_token_env_var}}`;\n }\n return {\n url: raw.url,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n };\n }\n\n return undefined;\n}\n\nfunction loadCodexGlobalConfig(): McpConfig {\n try {\n const filePath = getCodexGlobalMcpPath();\n if (!fs.existsSync(filePath)) return {};\n const parsed = parseToml(fs.readFileSync(filePath, 'utf-8')) as Record<string, unknown>;\n const rawServers = parsed.mcp_servers;\n if (!rawServers || typeof rawServers !== 'object' || Array.isArray(rawServers)) return {};\n\n const mcpServers: Record<string, unknown> = {};\n for (const [name, entry] of Object.entries(rawServers as Record<string, unknown>)) {\n const normalized = normalizeCodexServer(entry);\n if (normalized) mcpServers[name] = normalized;\n }\n return withoutSkippedServers(validateConfig({ mcpServers }));\n } catch {\n return {};\n }\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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"}
@@ -24,7 +24,8 @@ export interface McpManager {
24
24
  /**
25
25
  * Run the OAuth authorization-code flow for an HTTP server, then reconnect it.
26
26
  * Servers without an `oauth` config are provisioned with a zero-config default
27
- * (dynamic client registration). The authorization URL is surfaced through
27
+ * that identifies Mastra Code by its Client ID Metadata Document. The
28
+ * authorization URL is surfaced through
28
29
  * `onAuthorizationUrl` for the caller to open in a browser.
29
30
  *
30
31
  * Resolves with the resulting {@link McpServerStatus}: a connected status on
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/mcp/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAQ/D,OAAO,KAAK,EACV,SAAS,EAGT,eAAe,EACf,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAIpB,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2FAA2F;IAC3F,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,kEAAkE;IAClE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,iEAAiE;IACjE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACxD;;;;;;;;;;OAUG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3E,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B;;;;;OAKG;IACH,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D;;;;;;;;OAQG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7G,oFAAoF;IACpF,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACtD;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,oFAAoF;IACpF,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,2EAA2E;IAC3E,kBAAkB,IAAI,MAAM,EAAE,CAAC;IAC/B,mEAAmE;IACnE,qBAAqB,IAAI,OAAO,CAAC;IACjC,oDAAoD;IACpD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,4DAA4D;IAC5D,UAAU,IAAI,OAAO,CAAC;IACtB,iCAAiC;IACjC,iBAAiB,IAAI,eAAe,EAAE,CAAC;IACvC,2DAA2D;IAC3D,iBAAiB,IAAI,gBAAgB,EAAE,CAAC;IACxC,yCAAyC;IACzC,cAAc,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,6BAA6B;IAC7B,SAAS,IAAI,SAAS,CAAC;IACvB,6CAA6C;IAC7C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACvC;AA0ED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,aAAa,SAAqB,EAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,iBAAiB,CAAC,EAAE,2BAA2B,GAC9C,UAAU,CA8uBZ"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/mcp/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAiBH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAQ/D,OAAO,KAAK,EACV,SAAS,EAGT,eAAe,EACf,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAIpB,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,2FAA2F;IAC3F,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,kEAAkE;IAClE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,iEAAiE;IACjE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACxD;;;;;;;;;;;OAWG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3E,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B;;;;;OAKG;IACH,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D;;;;;;;;OAQG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC7G,oFAAoF;IACpF,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACtD;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,oFAAoF;IACpF,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,2EAA2E;IAC3E,kBAAkB,IAAI,MAAM,EAAE,CAAC;IAC/B,mEAAmE;IACnE,qBAAqB,IAAI,OAAO,CAAC;IACjC,oDAAoD;IACpD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,4DAA4D;IAC5D,UAAU,IAAI,OAAO,CAAC;IACtB,iCAAiC;IACjC,iBAAiB,IAAI,eAAe,EAAE,CAAC;IACvC,2DAA2D;IAC3D,iBAAiB,IAAI,gBAAgB,EAAE,CAAC;IACxC,yCAAyC;IACzC,cAAc,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,6BAA6B;IAC7B,SAAS,IAAI,SAAS,CAAC;IACvB,6CAA6C;IAC7C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACvC;AA2ED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,aAAa,SAAqB,EAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,iBAAiB,CAAC,EAAE,2BAA2B,GAC9C,UAAU,CAsvBZ"}
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_CONFIG_DIR } from "../constants.js";
2
2
  import { getAppDataDir } from "../utils/project.js";
3
- import { DEFAULT_OAUTH_REDIRECT_URL, getClaudeSettingsPath, getGlobalMcpPath, getProjectMcpPath, loadMcpConfig, resolveOAuthRedirectUrl } from "./config.js";
3
+ import { DEFAULT_OAUTH_REDIRECT_URL, MASTRA_CODE_CLIENT_METADATA_URL, getClaudeSettingsPath, getGlobalMcpPath, getProjectMcpPath, loadMcpConfig, resolveOAuthRedirectUrl } from "./config.js";
4
4
  import { loadGlobalDisableState, loadProjectServerOverrides, saveGlobalDisableState, saveProjectServerOverrides } from "./state.js";
5
5
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
6
6
  import { dirname, join } from "path";
@@ -55,8 +55,9 @@ var FileOAuthStorage = class {
55
55
  }
56
56
  };
57
57
  /**
58
- * Zero-config OAuth defaults for servers with a bare `url` entry. Dynamic
59
- * client registration provisions the client, so no `clientId` is needed.
58
+ * Zero-config OAuth defaults for servers with a bare `url` entry. The client
59
+ * identifies itself by Mastra Code's Client ID Metadata Document URL, so no
60
+ * `clientId` is needed.
60
61
  */
61
62
  const DEFAULT_OAUTH_CONFIG = { redirectUrl: DEFAULT_OAUTH_REDIRECT_URL };
62
63
  function getOAuthStoragePath(projectDir, name, cfg) {
@@ -172,19 +173,23 @@ function createMcpManager(projectDir, configDirName = DEFAULT_CONFIG_DIR, extraS
172
173
  const oauth = cfg.oauth ?? (existsSync(getOAuthStoragePath(projectDir, name, cfg)) ? DEFAULT_OAUTH_CONFIG : void 0);
173
174
  if (!oauth) return void 0;
174
175
  const redirectUrl = resolveOAuthRedirectUrl(oauth);
176
+ const clientInformation = oauth.clientId ? {
177
+ client_id: oauth.clientId,
178
+ ...oauth.clientSecret ? { client_secret: oauth.clientSecret } : {}
179
+ } : void 0;
180
+ const clientMetadataUrl = clientInformation ? void 0 : MASTRA_CODE_CLIENT_METADATA_URL;
175
181
  return new MCPOAuthClientProvider({
176
182
  redirectUrl,
177
183
  clientMetadata: {
184
+ ...clientMetadataUrl ? { client_id: clientMetadataUrl } : {},
178
185
  redirect_uris: [redirectUrl],
179
- client_name: oauth.clientName ?? `Mastra Code MCP ${name}`,
186
+ client_name: oauth.clientName ?? (clientMetadataUrl ? "Mastra Code" : `Mastra Code MCP ${name}`),
180
187
  grant_types: ["authorization_code", "refresh_token"],
181
188
  response_types: ["code"],
182
189
  ...oauth.scopes?.length ? { scope: oauth.scopes.join(" ") } : {}
183
190
  },
184
- clientInformation: oauth.clientId ? {
185
- client_id: oauth.clientId,
186
- ...oauth.clientSecret ? { client_secret: oauth.clientSecret } : {}
187
- } : void 0,
191
+ clientInformation,
192
+ clientMetadataUrl,
188
193
  storage: new FileOAuthStorage(getOAuthStoragePath(projectDir, name, cfg)),
189
194
  onRedirectToAuthorization: (url) => {
190
195
  authUrlHandlers.get(name)?.(url.toString());
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","names":[],"sources":["../../src/mcp/manager.ts"],"sourcesContent":["/**\n * MCP manager — orchestrates MCP server connections using MCPClient directly.\n * Created once at startup, provides tools from connected MCP servers.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp';\nimport type { MastraMCPServerDefinition, OAuthClientInformation, OAuthStorage } from '@mastra/mcp';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { getAppDataDir } from '../utils/project.js';\nimport {\n DEFAULT_OAUTH_REDIRECT_URL,\n loadMcpConfig,\n getProjectMcpPath,\n getGlobalMcpPath,\n getClaudeSettingsPath,\n resolveOAuthRedirectUrl,\n} from './config.js';\nimport type { ExternalMcpDiscoveryOptions } from './config.js';\nimport {\n loadGlobalDisableState,\n loadProjectServerOverrides,\n saveGlobalDisableState,\n saveProjectServerOverrides,\n} from './state.js';\nimport type { McpProjectServerOverride } from './state.js';\nimport type {\n McpConfig,\n McpHttpOAuthConfig,\n McpHttpServerConfig,\n McpServerConfig,\n McpServerStatus,\n McpSkippedServer,\n} from './types.js';\n\nconst MASTRACODE_MCP_TIMEOUT_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n/** Summary of MCP initialization result. */\nexport interface McpInitResult {\n connected: McpServerStatus[];\n failed: McpServerStatus[];\n skipped: McpSkippedServer[];\n totalTools: number;\n}\n\n/** Public interface for the MCP manager returned by createMcpManager(). */\nexport interface McpManager {\n /** Connect to all configured MCP servers and collect their tools. */\n init(): Promise<void>;\n /** Start init in the background. Returns a promise that resolves with status when done. */\n initInBackground(): Promise<McpInitResult>;\n /** Disconnect all servers, reload config from disk, reconnect. */\n reload(): Promise<void>;\n /** Reconnect a single server by name. Returns updated status. */\n reconnectServer(name: string): Promise<McpServerStatus>;\n /**\n * Run the OAuth authorization-code flow for an HTTP server, then reconnect it.\n * Servers without an `oauth` config are provisioned with a zero-config default\n * (dynamic client registration). The authorization URL is surfaced through\n * `onAuthorizationUrl` for the caller to open in a browser.\n *\n * Resolves with the resulting {@link McpServerStatus}: a connected status on\n * success, or a status carrying an `error` message on failure (including\n * cancellation) — it does not reject. Inspect the returned status rather than\n * relying on a thrown error.\n */\n authenticateServer(\n name: string,\n options?: { onAuthorizationUrl?: (url: string) => void; timeoutMs?: number },\n ): Promise<McpServerStatus>;\n /**\n * Cancel a pending {@link authenticateServer} flow for a server (e.g. the\n * user closed the browser without completing consent). The pending\n * authenticate call rejects and the server returns to the `needsAuth` state\n * so it can be retried. Returns `true` if a flow was cancelled.\n */\n cancelServerAuthentication(name: string): Promise<boolean>;\n /**\n * Disable or enable a single server by name. The change is persisted (in\n * mastracode's app data, not the user's config files) and survives restarts.\n * With `global: true`, this changes the default for every project. Otherwise\n * it records an explicit project override, which can override a global\n * per-server disable. The global all-MCP kill switch remains absolute.\n * Connections are rebuilt, so other servers reconnect — same behavior as\n * {@link reload}. Returns the server's resulting status.\n */\n setServerDisabled(name: string, disabled: boolean, options?: { global?: boolean }): Promise<McpServerStatus>;\n /** Clear a server's explicit project override so it inherits the global default. */\n inheritServer(name: string): Promise<McpServerStatus>;\n /**\n * Disable or enable all servers at once. Project scope records an explicit\n * override for every currently configured server. Global scope sets/clears a\n * persisted all-MCP kill switch (and, when enabling, also clears globally\n * disabled server names). Persisted like {@link setServerDisabled}.\n */\n setAllDisabled(disabled: boolean, options?: { global?: boolean }): Promise<void>;\n /** Clear every explicit project override so all servers inherit global defaults. */\n inheritAllServers(): Promise<void>;\n /** Names of configured servers that are currently disabled (any scope). */\n getDisabledServers(): string[];\n /** Whether all MCP is disabled globally (across every project). */\n isAllDisabledGlobally(): boolean;\n /** Disconnect from all MCP servers and clean up. */\n disconnect(): Promise<void>;\n /** Get all tools from connected MCP servers (namespaced as serverName_toolName). */\n getTools(): Record<string, any>;\n /** Check if any MCP servers are configured (or skipped). */\n hasServers(): boolean;\n /** Get status of all servers. */\n getServerStatuses(): McpServerStatus[];\n /** Get servers that were skipped during config loading. */\n getSkippedServers(): McpSkippedServer[];\n /** Get config file paths for display. */\n getConfigPaths(): { project: string; global: string; claude: string };\n /** Get the merged config. */\n getConfig(): McpConfig;\n /** Get captured stderr logs for a server. */\n getServerLogs(name: string): string[];\n}\n\nfunction getTransport(cfg: McpServerConfig): 'stdio' | 'http' {\n return 'url' in cfg ? 'http' : 'stdio';\n}\n\nclass FileOAuthStorage implements OAuthStorage {\n constructor(private filePath: string) {}\n\n get(key: string): string | undefined {\n return this.read()[key];\n }\n\n set(key: string, value: string): void {\n const data = this.read();\n data[key] = value;\n this.write(data);\n }\n\n delete(key: string): void {\n const data = this.read();\n delete data[key];\n this.write(data);\n }\n\n private read(): Record<string, string> {\n if (!existsSync(this.filePath)) return {};\n try {\n return JSON.parse(readFileSync(this.filePath, 'utf-8')) as Record<string, string>;\n } catch {\n return {};\n }\n }\n\n private write(data: Record<string, string>): void {\n const dir = dirname(this.filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n const tmpPath = `${this.filePath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2), { encoding: 'utf-8', mode: 0o600 });\n renameSync(tmpPath, this.filePath);\n }\n}\n\n/**\n * Zero-config OAuth defaults for servers with a bare `url` entry. Dynamic\n * client registration provisions the client, so no `clientId` is needed.\n */\nconst DEFAULT_OAUTH_CONFIG: McpHttpOAuthConfig = { redirectUrl: DEFAULT_OAUTH_REDIRECT_URL };\n\nfunction getOAuthStoragePath(projectDir: string, name: string, cfg: McpHttpServerConfig): string {\n // The fingerprint always uses the resolved redirect URL so a bare `url`\n // entry keeps the same token file before and after zero-config provisioning.\n const key = JSON.stringify({\n projectDir,\n name,\n url: cfg.url,\n redirectUrl: resolveOAuthRedirectUrl(cfg.oauth),\n clientId: cfg.oauth?.clientId,\n scopes: cfg.oauth?.scopes ?? [],\n });\n return join(getAppDataDir(), 'mcp-oauth', `${getStorageKeyFingerprint(key)}.json`);\n}\n\nfunction getStorageKeyFingerprint(value: string): string {\n let fingerprint = 0xcbf29ce484222325n;\n for (let i = 0; i < value.length; i += 1) {\n fingerprint ^= BigInt(value.charCodeAt(i));\n fingerprint = BigInt.asUintN(64, fingerprint * 0x100000001b3n);\n }\n return fingerprint.toString(16).padStart(16, '0');\n}\n\n/**\n * Create an MCP manager that wraps MCPClient with config-file discovery\n * and per-server status tracking.\n */\nexport function createMcpManager(\n projectDir: string,\n configDirName = DEFAULT_CONFIG_DIR,\n extraServers?: Record<string, McpServerConfig>,\n externalDiscovery?: ExternalMcpDiscoveryOptions,\n): McpManager {\n /** Merge programmatic servers into a base config (highest priority). */\n const applyExtraServers = (base: McpConfig): McpConfig => {\n if (!extraServers || Object.keys(extraServers).length === 0) return base;\n return { ...base, mcpServers: { ...base.mcpServers, ...extraServers } };\n };\n\n let config = applyExtraServers(loadMcpConfig(projectDir, configDirName, externalDiscovery));\n let projectServerOverrides = new Map<string, McpProjectServerOverride>(\n Object.entries(loadProjectServerOverrides(projectDir)),\n );\n let globalDisableState = loadGlobalDisableState();\n let globallyDisabledServers = new Set(globalDisableState.disabledServers);\n\n /** Whether a server is disabled after applying the kill switch, project override, and global default. */\n const isDisabled = (name: string): boolean => {\n if (globalDisableState.allDisabled) return true;\n const projectOverride = projectServerOverrides.get(name);\n if (projectOverride) return projectOverride === 'disabled';\n return globallyDisabledServers.has(name);\n };\n\n /** Which scope currently disables a server. The global kill switch remains absolute. */\n const disabledScopeOf = (name: string): 'project' | 'global' | undefined => {\n if (globalDisableState.allDisabled) return 'global';\n const projectOverride = projectServerOverrides.get(name);\n if (projectOverride === 'disabled') return 'project';\n if (!projectOverride && globallyDisabledServers.has(name)) return 'global';\n return undefined;\n };\n let client: MCPClient | null = null;\n let serverDefs: Record<string, MastraMCPServerDefinition> = {};\n let tools: Record<string, any> = {};\n let serverStatuses = new Map<string, McpServerStatus>();\n let stderrLogs = new Map<string, string[]>();\n let initialized = false;\n\n /** Per-server handlers that receive the OAuth authorization URL during authenticateServer(). */\n const authUrlHandlers = new Map<string, (url: string) => void>();\n\n /**\n * Servers with an OAuth authorization flow currently in flight. Owned by the\n * manager (not the TUI) so the state survives a `/mcp` selector being closed\n * and reopened — the reopened selector reads it back off the server status and\n * can still offer \"Cancel authentication\".\n */\n const authenticatingServers = new Set<string>();\n\n /**\n * Servers whose in-flight authentication was cancelled by the caller. Set by\n * cancelServerAuthentication() so the resolving authenticateServer() call can\n * mark its failed status as a deliberate cancel rather than a genuine failure.\n * This lives on the manager (not the TUI selector) so the signal survives the\n * selector being closed and reopened mid-flow.\n */\n const cancelledAuthServers = new Set<string>();\n\n /** Overlay manager-owned state onto a status snapshot. */\n const withManagerState = (status: McpServerStatus): McpServerStatus => ({\n ...status,\n ...(authenticatingServers.has(status.name) ? { authenticating: true } : {}),\n ...(projectServerOverrides.has(status.name) ? { projectOverride: projectServerOverrides.get(status.name) } : {}),\n globalDefault: globallyDisabledServers.has(status.name) ? 'disabled' : 'enabled',\n globalKillSwitch: globalDisableState.allDisabled,\n });\n\n const MAX_STDERR_LINES = 200;\n\n /** Hook into a server's stderr stream and buffer its output. */\n function captureStderr(serverName: string): void {\n if (!client || typeof client.getServerStderr !== 'function') return;\n const stream = client.getServerStderr(serverName);\n if (!stream) return;\n\n let buffer = '';\n const lines = stderrLogs.get(serverName) ?? [];\n stderrLogs.set(serverName, lines);\n\n stream.on('data', (chunk: Buffer) => {\n buffer += chunk.toString();\n const parts = buffer.split('\\n');\n // Last element is incomplete line (or empty if ended with \\n)\n buffer = parts.pop()!;\n for (const line of parts) {\n if (line.trim()) {\n lines.push(line);\n if (lines.length > MAX_STDERR_LINES) {\n lines.shift();\n }\n }\n }\n });\n\n stream.on('end', () => {\n if (buffer.trim()) {\n lines.push(buffer);\n if (lines.length > MAX_STDERR_LINES) {\n lines.shift();\n }\n }\n });\n }\n\n function createOAuthProvider(name: string, cfg: McpHttpServerConfig) {\n // Bare `url` entries get no eager provider — auth is provisioned lazily\n // when the user authenticates — unless a previous session already stored\n // OAuth state for this server, in which case the provider is needed to\n // attach the persisted tokens on connect.\n const oauth =\n cfg.oauth ?? (existsSync(getOAuthStoragePath(projectDir, name, cfg)) ? DEFAULT_OAUTH_CONFIG : undefined);\n if (!oauth) return undefined;\n\n // redirectUrl is optional in the user-supplied config; resolve the stable\n // default (or the `callbackPort` shorthand, for programmatically registered\n // servers that bypass config parsing) so provider metadata always carries\n // a concrete URL.\n const redirectUrl = resolveOAuthRedirectUrl(oauth);\n\n return new MCPOAuthClientProvider({\n redirectUrl,\n clientMetadata: {\n redirect_uris: [redirectUrl],\n client_name: oauth.clientName ?? `Mastra Code MCP ${name}`,\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n ...(oauth.scopes?.length ? { scope: oauth.scopes.join(' ') } : {}),\n },\n clientInformation: oauth.clientId\n ? ({\n client_id: oauth.clientId,\n ...(oauth.clientSecret ? { client_secret: oauth.clientSecret } : {}),\n } satisfies OAuthClientInformation)\n : undefined,\n storage: new FileOAuthStorage(getOAuthStoragePath(projectDir, name, cfg)),\n onRedirectToAuthorization: url => {\n authUrlHandlers.get(name)?.(url.toString());\n },\n });\n }\n\n function buildServerDefs(servers: Record<string, McpServerConfig>): Record<string, MastraMCPServerDefinition> {\n const defs: Record<string, MastraMCPServerDefinition> = {};\n for (const [name, cfg] of Object.entries(servers)) {\n if ('url' in cfg) {\n const httpCfg = cfg as McpHttpServerConfig;\n defs[name] = {\n url: new URL(httpCfg.url),\n requestInit: httpCfg.headers ? { headers: httpCfg.headers } : undefined,\n authProvider: createOAuthProvider(name, httpCfg),\n };\n } else {\n defs[name] = { command: cfg.command, args: cfg.args, env: cfg.env, stderr: 'pipe' };\n }\n }\n return defs;\n }\n\n /** Seed a `disabled` status for every disabled server so it stays visible. */\n function setDisabledStatuses(): void {\n for (const [name, cfg] of Object.entries(config.mcpServers ?? {})) {\n const scope = disabledScopeOf(name);\n if (!scope) continue;\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n disabled: true,\n disabledScope: scope,\n });\n }\n }\n\n async function connectAndCollectTools(): Promise<void> {\n setDisabledStatuses();\n\n const servers = Object.fromEntries(Object.entries(config.mcpServers ?? {}).filter(([name]) => !isDisabled(name)));\n if (Object.keys(servers).length === 0) {\n return;\n }\n\n // Pre-populate statuses as \"connecting\" so callers can see in-progress state\n const serverNames = Object.keys(servers);\n for (const name of serverNames) {\n serverStatuses.set(name, {\n name,\n connected: false,\n connecting: true,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n });\n }\n\n serverDefs = buildServerDefs(servers);\n client = new MCPClient({\n id: 'mastra-code-mcp',\n servers: serverDefs,\n timeout: MASTRACODE_MCP_TIMEOUT_MS,\n });\n\n // Use listToolsetsWithErrors() to get tools grouped by server name,\n // plus per-server error messages for servers that failed to connect.\n\n try {\n const { toolsets, errors } = await client.listToolsetsWithErrors();\n const typedToolsets = toolsets as Record<string, Record<string, any>>;\n\n // Flatten toolsets into the namespaced tools map (serverName_toolName)\n for (const [serverName, serverTools] of Object.entries(typedToolsets)) {\n for (const [toolName, toolConfig] of Object.entries(serverTools)) {\n tools[`${serverName}_${toolName}`] = toolConfig;\n }\n }\n\n for (const name of serverNames) {\n const serverTools = typedToolsets[name];\n if (serverTools && Object.keys(serverTools).length > 0) {\n const toolNames = Object.keys(serverTools).map(t => `${name}_${t}`);\n serverStatuses.set(name, {\n name,\n connected: true,\n toolCount: toolNames.length,\n toolNames,\n transport: getTransport(servers[name]!),\n });\n } else {\n // Server failed — use the real error from listToolsetsWithErrors()\n const error = errors[name] ?? 'Failed to connect';\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n error,\n ...(serverNeedsAuth(name, servers[name]!, error) ? { needsAuth: true } : {}),\n });\n }\n }\n\n // Capture stderr from all stdio servers (connected or failed)\n for (const name of serverNames) {\n captureStderr(name);\n }\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n\n for (const name of serverNames) {\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n error: errMsg,\n ...(serverNeedsAuth(name, servers[name]!, errMsg) ? { needsAuth: true } : {}),\n });\n }\n }\n }\n\n /**\n * Whether a failed HTTP server is blocked on OAuth authorization.\n *\n * Provider-backed servers report the exact state tracked by `@mastra/mcp`.\n * Bare `url` entries carry no provider until the user authenticates, so the\n * signal is a 401 in the connect error — surfaced either as the status text\n * or as the RFC 6750 `invalid_token` bearer error code in the response body.\n */\n function serverNeedsAuth(name: string, cfg: McpServerConfig, error?: string): boolean {\n if (getTransport(cfg) !== 'http') return false;\n if (client?.getServerAuthState?.(name) === 'needs-auth') return true;\n if (serverDefs[name]?.authProvider) return false;\n return error !== undefined && /\\b401\\b|unauthorized|invalid_token/i.test(error);\n }\n\n /**\n * Runs a single-server connect action (reconnect or authenticate), then\n * refreshes that server's tools and status from the client.\n */\n async function connectSingleServer(\n name: string,\n cfg: McpServerConfig,\n connect: () => Promise<unknown>,\n ): Promise<McpServerStatus> {\n const transport = getTransport(cfg);\n\n // Remove old tools for this server\n const prefix = `${name}_`;\n for (const key of Object.keys(tools)) {\n if (key.startsWith(prefix)) {\n delete tools[key];\n }\n }\n\n // Clear old logs and mark as connecting\n stderrLogs.delete(name);\n serverStatuses.set(name, {\n name,\n connected: false,\n connecting: true,\n toolCount: 0,\n toolNames: [],\n transport,\n });\n\n try {\n await connect();\n\n // Recapture stderr for the reconnected server\n captureStderr(name);\n\n // Fetch updated toolsets to get this server's tools\n const { toolsets, errors } = await client!.listToolsetsWithErrors();\n const serverTools = toolsets[name];\n const serverError = errors[name];\n\n if (serverError) {\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: serverError,\n ...(serverNeedsAuth(name, cfg, serverError) ? { needsAuth: true } : {}),\n };\n serverStatuses.set(name, status);\n return status;\n } else if (serverTools && Object.keys(serverTools).length > 0) {\n const toolNames = Object.keys(serverTools).map(t => `${name}_${t}`);\n for (const [toolName, toolConfig] of Object.entries(serverTools)) {\n tools[`${name}_${toolName}`] = toolConfig;\n }\n const status: McpServerStatus = {\n name,\n connected: true,\n toolCount: toolNames.length,\n toolNames,\n transport,\n };\n serverStatuses.set(name, status);\n return status;\n } else {\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: 'Failed to connect',\n };\n serverStatuses.set(name, status);\n return status;\n }\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: errMsg,\n ...(serverNeedsAuth(name, cfg, errMsg) ? { needsAuth: true } : {}),\n };\n serverStatuses.set(name, status);\n return status;\n }\n }\n\n async function disconnect(): Promise<void> {\n if (client) {\n try {\n await client.disconnect();\n } catch {\n // Ignore disconnect errors\n }\n client = null;\n }\n }\n\n /** Tear down all connections and reconnect every enabled server. */\n async function rebuildConnections(): Promise<void> {\n await disconnect();\n tools = {};\n serverStatuses = new Map();\n stderrLogs = new Map();\n initialized = false;\n await connectAndCollectTools();\n initialized = true;\n }\n\n function disabledStatus(name: string): McpServerStatus {\n const cfg = config.mcpServers?.[name];\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: cfg ? getTransport(cfg) : 'stdio',\n disabled: true,\n disabledScope: disabledScopeOf(name),\n };\n }\n\n // Read-merge-write persistence: re-read the persisted state and apply this\n // operation's delta on top, then adopt the merged result in memory. This\n // way a concurrent mastracode process's changes (e.g. another window\n // flipping the global kill switch) are never clobbered by this manager's\n // construction-time snapshot.\n function persistProjectDelta(mutate: (overrides: Map<string, McpProjectServerOverride>) => void): void {\n const fresh = new Map<string, McpProjectServerOverride>(Object.entries(loadProjectServerOverrides(projectDir)));\n mutate(fresh);\n projectServerOverrides = fresh;\n saveProjectServerOverrides(projectDir, Object.fromEntries(fresh));\n }\n\n function persistGlobalDelta(mutate: (state: { allDisabled: boolean; disabledServers: Set<string> }) => void): void {\n const onDisk = loadGlobalDisableState();\n const fresh = { allDisabled: onDisk.allDisabled, disabledServers: new Set(onDisk.disabledServers) };\n mutate(fresh);\n globallyDisabledServers = fresh.disabledServers;\n globalDisableState = { allDisabled: fresh.allDisabled, disabledServers: Array.from(fresh.disabledServers) };\n saveGlobalDisableState(globalDisableState);\n }\n\n return {\n async init() {\n if (initialized) return;\n await connectAndCollectTools();\n initialized = true;\n },\n\n async initInBackground(): Promise<McpInitResult> {\n await this.init();\n const statuses = Array.from(serverStatuses.values(), withManagerState);\n const connected = statuses.filter(s => s.connected);\n const failed = statuses.filter(s => !s.connected && !s.disabled);\n return {\n connected,\n failed,\n skipped: [...(config.skippedServers ?? [])],\n totalTools: connected.reduce((sum, s) => sum + s.toolCount, 0),\n };\n },\n\n async reload() {\n config = applyExtraServers(loadMcpConfig(projectDir, configDirName, externalDiscovery));\n projectServerOverrides = new Map(Object.entries(loadProjectServerOverrides(projectDir)));\n globalDisableState = loadGlobalDisableState();\n globallyDisabledServers = new Set(globalDisableState.disabledServers);\n await rebuildConnections();\n },\n\n async setServerDisabled(name: string, disabled: boolean, options?: { global?: boolean }): Promise<McpServerStatus> {\n if (!config.mcpServers?.[name]) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n const wasEffectivelyDisabled = isDisabled(name);\n\n if (options?.global) {\n persistGlobalDelta(state => {\n if (disabled) {\n state.disabledServers.add(name);\n } else {\n state.disabledServers.delete(name);\n }\n });\n } else {\n persistProjectDelta(overrides => {\n overrides.set(name, disabled ? 'disabled' : 'enabled');\n });\n }\n\n // Only rebuild connections when the server's effective state actually\n // flipped. State changes that only alter an inherited default or an\n // override beneath the global kill switch leave connections alone.\n if (isDisabled(name) !== wasEffectivelyDisabled) {\n await rebuildConnections();\n return withManagerState(serverStatuses.get(name) ?? disabledStatus(name));\n }\n return withManagerState(\n isDisabled(name) ? disabledStatus(name) : (serverStatuses.get(name) ?? disabledStatus(name)),\n );\n },\n\n async inheritServer(name: string): Promise<McpServerStatus> {\n if (!config.mcpServers?.[name]) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n const wasEffectivelyDisabled = isDisabled(name);\n persistProjectDelta(overrides => {\n overrides.delete(name);\n });\n if (isDisabled(name) !== wasEffectivelyDisabled) {\n await rebuildConnections();\n }\n return withManagerState(\n isDisabled(name) ? disabledStatus(name) : (serverStatuses.get(name) ?? disabledStatus(name)),\n );\n },\n\n async setAllDisabled(disabled: boolean, options?: { global?: boolean }): Promise<void> {\n const configuredNames = Object.keys(config.mcpServers ?? {});\n const effectiveBefore = configuredNames.filter(name => isDisabled(name)).join(',');\n if (options?.global) {\n persistGlobalDelta(state => {\n state.allDisabled = disabled;\n if (!disabled) {\n // Enabling globally also clears globally disabled server names so\n // \"/mcp enable all --global\" fully restores global defaults.\n state.disabledServers.clear();\n }\n });\n } else {\n persistProjectDelta(overrides => {\n for (const name of configuredNames) {\n overrides.set(name, disabled ? 'disabled' : 'enabled');\n }\n });\n }\n const effectiveAfter = configuredNames.filter(name => isDisabled(name)).join(',');\n if (effectiveAfter !== effectiveBefore) {\n await rebuildConnections();\n }\n },\n\n async inheritAllServers(): Promise<void> {\n const configuredNames = Object.keys(config.mcpServers ?? {});\n const effectiveBefore = configuredNames.filter(name => isDisabled(name)).join(',');\n persistProjectDelta(overrides => {\n overrides.clear();\n });\n const effectiveAfter = configuredNames.filter(name => isDisabled(name)).join(',');\n if (effectiveAfter !== effectiveBefore) {\n await rebuildConnections();\n }\n },\n\n getDisabledServers() {\n return Object.keys(config.mcpServers ?? {})\n .filter(name => isDisabled(name))\n .sort();\n },\n\n isAllDisabledGlobally() {\n return globalDisableState.allDisabled;\n },\n\n async reconnectServer(name: string): Promise<McpServerStatus> {\n if (isDisabled(name)) {\n return { ...disabledStatus(name), error: `Server \"${name}\" is disabled — enable it first` };\n }\n const cfg = config.mcpServers?.[name];\n if (!cfg) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n if (!client) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n error: 'MCP client not initialized',\n };\n }\n\n // Use MCPClient's per-server reconnect\n return connectSingleServer(name, cfg, () => client!.reconnectServer(name));\n },\n\n async authenticateServer(\n name: string,\n options?: { onAuthorizationUrl?: (url: string) => void; timeoutMs?: number },\n ): Promise<McpServerStatus> {\n const cfg = config.mcpServers?.[name];\n if (!cfg) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n if (isDisabled(name)) {\n return { ...disabledStatus(name), error: `Server \"${name}\" is disabled — enable it first` };\n }\n\n if (!client) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n error: 'MCP client not initialized',\n };\n }\n\n if (getTransport(cfg) !== 'http') {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" uses stdio transport, which does not support OAuth`,\n };\n }\n\n // Zero-config provisioning: a bare `url` entry gets a provider with the\n // default redirect URL the first time the user authenticates. Dynamic\n // client registration takes care of the client credentials.\n //\n // NOTE: `serverDefs[name]` is the same object reference the MCPClient was\n // constructed with, and connectHttp reads `authProvider` live off it at\n // connect time, so mutating it here reaches the already-created client.\n // If MCPClient ever defensively copies its server config, zero-config auth\n // would need an explicit \"set this server's provider\" API instead.\n const def = serverDefs[name];\n if (def?.url && !def.authProvider) {\n def.authProvider = createOAuthProvider(name, { ...(cfg as McpHttpServerConfig), oauth: DEFAULT_OAUTH_CONFIG });\n }\n\n // Reject a concurrent second attempt for the same server. authUrlHandlers\n // has a single slot per server, so a second call with an onAuthorizationUrl\n // would overwrite the first caller's handler (which then never sees the\n // URL) and its finally would delete the handler out from under the other\n // in-flight call. Gate on authenticatingServers rather than authUrlHandlers\n // so callers without a URL handler are caught too. The underlying\n // client.authenticate already coalesces same-server calls into one flow.\n if (authenticatingServers.has(name)) {\n const current = serverStatuses.get(name);\n return {\n name,\n connected: current?.connected ?? false,\n connecting: current?.connecting,\n needsAuth: current?.needsAuth,\n toolCount: current?.toolCount ?? 0,\n toolNames: current?.toolNames ?? [],\n transport: current?.transport ?? getTransport(cfg),\n error: `Authentication for \"${name}\" is already in progress`,\n };\n }\n\n if (options?.onAuthorizationUrl) {\n authUrlHandlers.set(name, options.onAuthorizationUrl);\n }\n authenticatingServers.add(name);\n cancelledAuthServers.delete(name);\n try {\n const result = await connectSingleServer(name, cfg, () =>\n client!.authenticate(name, options?.timeoutMs === undefined ? undefined : { timeoutMs: options.timeoutMs }),\n );\n // A cancelled flow resolves with a failed status; mark it so callers can\n // distinguish a deliberate cancel from a genuine authentication failure.\n // Write the marker back to the durable status map (not just the returned\n // value) so a selector reopened after cancellation still sees it.\n const finalStatus =\n cancelledAuthServers.has(name) && !result.connected ? { ...result, cancelled: true } : result;\n serverStatuses.set(name, finalStatus);\n return finalStatus;\n } finally {\n authUrlHandlers.delete(name);\n authenticatingServers.delete(name);\n cancelledAuthServers.delete(name);\n }\n },\n\n async cancelServerAuthentication(name: string): Promise<boolean> {\n if (!client) {\n return false;\n }\n // Record the intent before aborting so the resolving authenticateServer()\n // call can tag its failed status as cancelled rather than failed.\n cancelledAuthServers.add(name);\n const cancelled = await client.cancelAuthentication(name);\n if (!cancelled) {\n cancelledAuthServers.delete(name);\n }\n return cancelled;\n },\n\n disconnect,\n\n getTools() {\n return { ...tools };\n },\n\n hasServers() {\n const hasConfigured = config.mcpServers !== undefined && Object.keys(config.mcpServers).length > 0;\n const hasSkipped = config.skippedServers !== undefined && config.skippedServers.length > 0;\n return hasConfigured || hasSkipped;\n },\n\n getServerStatuses() {\n return Array.from(serverStatuses.values(), withManagerState);\n },\n\n getSkippedServers() {\n return [...(config.skippedServers ?? [])];\n },\n\n getConfigPaths() {\n return {\n project: getProjectMcpPath(projectDir, configDirName),\n global: getGlobalMcpPath(configDirName),\n claude: getClaudeSettingsPath(projectDir),\n };\n },\n\n getConfig() {\n return config;\n },\n\n getServerLogs(name: string) {\n return [...(stderrLogs.get(name) ?? [])];\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","names":[],"sources":["../../src/mcp/manager.ts"],"sourcesContent":["/**\n * MCP manager — orchestrates MCP server connections using MCPClient directly.\n * Created once at startup, provides tools from connected MCP servers.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp';\nimport type { MastraMCPServerDefinition, OAuthClientInformation, OAuthStorage } from '@mastra/mcp';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { getAppDataDir } from '../utils/project.js';\nimport {\n DEFAULT_OAUTH_REDIRECT_URL,\n MASTRA_CODE_CLIENT_METADATA_URL,\n loadMcpConfig,\n getProjectMcpPath,\n getGlobalMcpPath,\n getClaudeSettingsPath,\n resolveOAuthRedirectUrl,\n} from './config.js';\nimport type { ExternalMcpDiscoveryOptions } from './config.js';\nimport {\n loadGlobalDisableState,\n loadProjectServerOverrides,\n saveGlobalDisableState,\n saveProjectServerOverrides,\n} from './state.js';\nimport type { McpProjectServerOverride } from './state.js';\nimport type {\n McpConfig,\n McpHttpOAuthConfig,\n McpHttpServerConfig,\n McpServerConfig,\n McpServerStatus,\n McpSkippedServer,\n} from './types.js';\n\nconst MASTRACODE_MCP_TIMEOUT_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n/** Summary of MCP initialization result. */\nexport interface McpInitResult {\n connected: McpServerStatus[];\n failed: McpServerStatus[];\n skipped: McpSkippedServer[];\n totalTools: number;\n}\n\n/** Public interface for the MCP manager returned by createMcpManager(). */\nexport interface McpManager {\n /** Connect to all configured MCP servers and collect their tools. */\n init(): Promise<void>;\n /** Start init in the background. Returns a promise that resolves with status when done. */\n initInBackground(): Promise<McpInitResult>;\n /** Disconnect all servers, reload config from disk, reconnect. */\n reload(): Promise<void>;\n /** Reconnect a single server by name. Returns updated status. */\n reconnectServer(name: string): Promise<McpServerStatus>;\n /**\n * Run the OAuth authorization-code flow for an HTTP server, then reconnect it.\n * Servers without an `oauth` config are provisioned with a zero-config default\n * that identifies Mastra Code by its Client ID Metadata Document. The\n * authorization URL is surfaced through\n * `onAuthorizationUrl` for the caller to open in a browser.\n *\n * Resolves with the resulting {@link McpServerStatus}: a connected status on\n * success, or a status carrying an `error` message on failure (including\n * cancellation) — it does not reject. Inspect the returned status rather than\n * relying on a thrown error.\n */\n authenticateServer(\n name: string,\n options?: { onAuthorizationUrl?: (url: string) => void; timeoutMs?: number },\n ): Promise<McpServerStatus>;\n /**\n * Cancel a pending {@link authenticateServer} flow for a server (e.g. the\n * user closed the browser without completing consent). The pending\n * authenticate call rejects and the server returns to the `needsAuth` state\n * so it can be retried. Returns `true` if a flow was cancelled.\n */\n cancelServerAuthentication(name: string): Promise<boolean>;\n /**\n * Disable or enable a single server by name. The change is persisted (in\n * mastracode's app data, not the user's config files) and survives restarts.\n * With `global: true`, this changes the default for every project. Otherwise\n * it records an explicit project override, which can override a global\n * per-server disable. The global all-MCP kill switch remains absolute.\n * Connections are rebuilt, so other servers reconnect — same behavior as\n * {@link reload}. Returns the server's resulting status.\n */\n setServerDisabled(name: string, disabled: boolean, options?: { global?: boolean }): Promise<McpServerStatus>;\n /** Clear a server's explicit project override so it inherits the global default. */\n inheritServer(name: string): Promise<McpServerStatus>;\n /**\n * Disable or enable all servers at once. Project scope records an explicit\n * override for every currently configured server. Global scope sets/clears a\n * persisted all-MCP kill switch (and, when enabling, also clears globally\n * disabled server names). Persisted like {@link setServerDisabled}.\n */\n setAllDisabled(disabled: boolean, options?: { global?: boolean }): Promise<void>;\n /** Clear every explicit project override so all servers inherit global defaults. */\n inheritAllServers(): Promise<void>;\n /** Names of configured servers that are currently disabled (any scope). */\n getDisabledServers(): string[];\n /** Whether all MCP is disabled globally (across every project). */\n isAllDisabledGlobally(): boolean;\n /** Disconnect from all MCP servers and clean up. */\n disconnect(): Promise<void>;\n /** Get all tools from connected MCP servers (namespaced as serverName_toolName). */\n getTools(): Record<string, any>;\n /** Check if any MCP servers are configured (or skipped). */\n hasServers(): boolean;\n /** Get status of all servers. */\n getServerStatuses(): McpServerStatus[];\n /** Get servers that were skipped during config loading. */\n getSkippedServers(): McpSkippedServer[];\n /** Get config file paths for display. */\n getConfigPaths(): { project: string; global: string; claude: string };\n /** Get the merged config. */\n getConfig(): McpConfig;\n /** Get captured stderr logs for a server. */\n getServerLogs(name: string): string[];\n}\n\nfunction getTransport(cfg: McpServerConfig): 'stdio' | 'http' {\n return 'url' in cfg ? 'http' : 'stdio';\n}\n\nclass FileOAuthStorage implements OAuthStorage {\n constructor(private filePath: string) {}\n\n get(key: string): string | undefined {\n return this.read()[key];\n }\n\n set(key: string, value: string): void {\n const data = this.read();\n data[key] = value;\n this.write(data);\n }\n\n delete(key: string): void {\n const data = this.read();\n delete data[key];\n this.write(data);\n }\n\n private read(): Record<string, string> {\n if (!existsSync(this.filePath)) return {};\n try {\n return JSON.parse(readFileSync(this.filePath, 'utf-8')) as Record<string, string>;\n } catch {\n return {};\n }\n }\n\n private write(data: Record<string, string>): void {\n const dir = dirname(this.filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n const tmpPath = `${this.filePath}.tmp`;\n writeFileSync(tmpPath, JSON.stringify(data, null, 2), { encoding: 'utf-8', mode: 0o600 });\n renameSync(tmpPath, this.filePath);\n }\n}\n\n/**\n * Zero-config OAuth defaults for servers with a bare `url` entry. The client\n * identifies itself by Mastra Code's Client ID Metadata Document URL, so no\n * `clientId` is needed.\n */\nconst DEFAULT_OAUTH_CONFIG: McpHttpOAuthConfig = { redirectUrl: DEFAULT_OAUTH_REDIRECT_URL };\n\nfunction getOAuthStoragePath(projectDir: string, name: string, cfg: McpHttpServerConfig): string {\n // The fingerprint always uses the resolved redirect URL so a bare `url`\n // entry keeps the same token file before and after zero-config provisioning.\n const key = JSON.stringify({\n projectDir,\n name,\n url: cfg.url,\n redirectUrl: resolveOAuthRedirectUrl(cfg.oauth),\n clientId: cfg.oauth?.clientId,\n scopes: cfg.oauth?.scopes ?? [],\n });\n return join(getAppDataDir(), 'mcp-oauth', `${getStorageKeyFingerprint(key)}.json`);\n}\n\nfunction getStorageKeyFingerprint(value: string): string {\n let fingerprint = 0xcbf29ce484222325n;\n for (let i = 0; i < value.length; i += 1) {\n fingerprint ^= BigInt(value.charCodeAt(i));\n fingerprint = BigInt.asUintN(64, fingerprint * 0x100000001b3n);\n }\n return fingerprint.toString(16).padStart(16, '0');\n}\n\n/**\n * Create an MCP manager that wraps MCPClient with config-file discovery\n * and per-server status tracking.\n */\nexport function createMcpManager(\n projectDir: string,\n configDirName = DEFAULT_CONFIG_DIR,\n extraServers?: Record<string, McpServerConfig>,\n externalDiscovery?: ExternalMcpDiscoveryOptions,\n): McpManager {\n /** Merge programmatic servers into a base config (highest priority). */\n const applyExtraServers = (base: McpConfig): McpConfig => {\n if (!extraServers || Object.keys(extraServers).length === 0) return base;\n return { ...base, mcpServers: { ...base.mcpServers, ...extraServers } };\n };\n\n let config = applyExtraServers(loadMcpConfig(projectDir, configDirName, externalDiscovery));\n let projectServerOverrides = new Map<string, McpProjectServerOverride>(\n Object.entries(loadProjectServerOverrides(projectDir)),\n );\n let globalDisableState = loadGlobalDisableState();\n let globallyDisabledServers = new Set(globalDisableState.disabledServers);\n\n /** Whether a server is disabled after applying the kill switch, project override, and global default. */\n const isDisabled = (name: string): boolean => {\n if (globalDisableState.allDisabled) return true;\n const projectOverride = projectServerOverrides.get(name);\n if (projectOverride) return projectOverride === 'disabled';\n return globallyDisabledServers.has(name);\n };\n\n /** Which scope currently disables a server. The global kill switch remains absolute. */\n const disabledScopeOf = (name: string): 'project' | 'global' | undefined => {\n if (globalDisableState.allDisabled) return 'global';\n const projectOverride = projectServerOverrides.get(name);\n if (projectOverride === 'disabled') return 'project';\n if (!projectOverride && globallyDisabledServers.has(name)) return 'global';\n return undefined;\n };\n let client: MCPClient | null = null;\n let serverDefs: Record<string, MastraMCPServerDefinition> = {};\n let tools: Record<string, any> = {};\n let serverStatuses = new Map<string, McpServerStatus>();\n let stderrLogs = new Map<string, string[]>();\n let initialized = false;\n\n /** Per-server handlers that receive the OAuth authorization URL during authenticateServer(). */\n const authUrlHandlers = new Map<string, (url: string) => void>();\n\n /**\n * Servers with an OAuth authorization flow currently in flight. Owned by the\n * manager (not the TUI) so the state survives a `/mcp` selector being closed\n * and reopened — the reopened selector reads it back off the server status and\n * can still offer \"Cancel authentication\".\n */\n const authenticatingServers = new Set<string>();\n\n /**\n * Servers whose in-flight authentication was cancelled by the caller. Set by\n * cancelServerAuthentication() so the resolving authenticateServer() call can\n * mark its failed status as a deliberate cancel rather than a genuine failure.\n * This lives on the manager (not the TUI selector) so the signal survives the\n * selector being closed and reopened mid-flow.\n */\n const cancelledAuthServers = new Set<string>();\n\n /** Overlay manager-owned state onto a status snapshot. */\n const withManagerState = (status: McpServerStatus): McpServerStatus => ({\n ...status,\n ...(authenticatingServers.has(status.name) ? { authenticating: true } : {}),\n ...(projectServerOverrides.has(status.name) ? { projectOverride: projectServerOverrides.get(status.name) } : {}),\n globalDefault: globallyDisabledServers.has(status.name) ? 'disabled' : 'enabled',\n globalKillSwitch: globalDisableState.allDisabled,\n });\n\n const MAX_STDERR_LINES = 200;\n\n /** Hook into a server's stderr stream and buffer its output. */\n function captureStderr(serverName: string): void {\n if (!client || typeof client.getServerStderr !== 'function') return;\n const stream = client.getServerStderr(serverName);\n if (!stream) return;\n\n let buffer = '';\n const lines = stderrLogs.get(serverName) ?? [];\n stderrLogs.set(serverName, lines);\n\n stream.on('data', (chunk: Buffer) => {\n buffer += chunk.toString();\n const parts = buffer.split('\\n');\n // Last element is incomplete line (or empty if ended with \\n)\n buffer = parts.pop()!;\n for (const line of parts) {\n if (line.trim()) {\n lines.push(line);\n if (lines.length > MAX_STDERR_LINES) {\n lines.shift();\n }\n }\n }\n });\n\n stream.on('end', () => {\n if (buffer.trim()) {\n lines.push(buffer);\n if (lines.length > MAX_STDERR_LINES) {\n lines.shift();\n }\n }\n });\n }\n\n function createOAuthProvider(name: string, cfg: McpHttpServerConfig) {\n // Bare `url` entries get no eager provider — auth is provisioned lazily\n // when the user authenticates — unless a previous session already stored\n // OAuth state for this server, in which case the provider is needed to\n // attach the persisted tokens on connect.\n const oauth =\n cfg.oauth ?? (existsSync(getOAuthStoragePath(projectDir, name, cfg)) ? DEFAULT_OAUTH_CONFIG : undefined);\n if (!oauth) return undefined;\n\n // redirectUrl is optional in the user-supplied config; resolve the stable\n // default (or the `callbackPort` shorthand, for programmatically registered\n // servers that bypass config parsing) so provider metadata always carries\n // a concrete URL.\n const redirectUrl = resolveOAuthRedirectUrl(oauth);\n\n // Without a pre-registered clientId the client is identified by Mastra\n // Code's Client ID Metadata Document: the document URL is the client_id and\n // the authorization server fetches it, so nothing is registered per server.\n const clientInformation = oauth.clientId\n ? ({\n client_id: oauth.clientId,\n ...(oauth.clientSecret ? { client_secret: oauth.clientSecret } : {}),\n } satisfies OAuthClientInformation)\n : undefined;\n const clientMetadataUrl = clientInformation ? undefined : MASTRA_CODE_CLIENT_METADATA_URL;\n\n return new MCPOAuthClientProvider({\n redirectUrl,\n clientMetadata: {\n ...(clientMetadataUrl ? { client_id: clientMetadataUrl } : {}),\n redirect_uris: [redirectUrl],\n client_name: oauth.clientName ?? (clientMetadataUrl ? 'Mastra Code' : `Mastra Code MCP ${name}`),\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n ...(oauth.scopes?.length ? { scope: oauth.scopes.join(' ') } : {}),\n },\n clientInformation,\n clientMetadataUrl,\n storage: new FileOAuthStorage(getOAuthStoragePath(projectDir, name, cfg)),\n onRedirectToAuthorization: url => {\n authUrlHandlers.get(name)?.(url.toString());\n },\n });\n }\n\n function buildServerDefs(servers: Record<string, McpServerConfig>): Record<string, MastraMCPServerDefinition> {\n const defs: Record<string, MastraMCPServerDefinition> = {};\n for (const [name, cfg] of Object.entries(servers)) {\n if ('url' in cfg) {\n const httpCfg = cfg as McpHttpServerConfig;\n defs[name] = {\n url: new URL(httpCfg.url),\n requestInit: httpCfg.headers ? { headers: httpCfg.headers } : undefined,\n authProvider: createOAuthProvider(name, httpCfg),\n };\n } else {\n defs[name] = { command: cfg.command, args: cfg.args, env: cfg.env, stderr: 'pipe' };\n }\n }\n return defs;\n }\n\n /** Seed a `disabled` status for every disabled server so it stays visible. */\n function setDisabledStatuses(): void {\n for (const [name, cfg] of Object.entries(config.mcpServers ?? {})) {\n const scope = disabledScopeOf(name);\n if (!scope) continue;\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n disabled: true,\n disabledScope: scope,\n });\n }\n }\n\n async function connectAndCollectTools(): Promise<void> {\n setDisabledStatuses();\n\n const servers = Object.fromEntries(Object.entries(config.mcpServers ?? {}).filter(([name]) => !isDisabled(name)));\n if (Object.keys(servers).length === 0) {\n return;\n }\n\n // Pre-populate statuses as \"connecting\" so callers can see in-progress state\n const serverNames = Object.keys(servers);\n for (const name of serverNames) {\n serverStatuses.set(name, {\n name,\n connected: false,\n connecting: true,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n });\n }\n\n serverDefs = buildServerDefs(servers);\n client = new MCPClient({\n id: 'mastra-code-mcp',\n servers: serverDefs,\n timeout: MASTRACODE_MCP_TIMEOUT_MS,\n });\n\n // Use listToolsetsWithErrors() to get tools grouped by server name,\n // plus per-server error messages for servers that failed to connect.\n\n try {\n const { toolsets, errors } = await client.listToolsetsWithErrors();\n const typedToolsets = toolsets as Record<string, Record<string, any>>;\n\n // Flatten toolsets into the namespaced tools map (serverName_toolName)\n for (const [serverName, serverTools] of Object.entries(typedToolsets)) {\n for (const [toolName, toolConfig] of Object.entries(serverTools)) {\n tools[`${serverName}_${toolName}`] = toolConfig;\n }\n }\n\n for (const name of serverNames) {\n const serverTools = typedToolsets[name];\n if (serverTools && Object.keys(serverTools).length > 0) {\n const toolNames = Object.keys(serverTools).map(t => `${name}_${t}`);\n serverStatuses.set(name, {\n name,\n connected: true,\n toolCount: toolNames.length,\n toolNames,\n transport: getTransport(servers[name]!),\n });\n } else {\n // Server failed — use the real error from listToolsetsWithErrors()\n const error = errors[name] ?? 'Failed to connect';\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n error,\n ...(serverNeedsAuth(name, servers[name]!, error) ? { needsAuth: true } : {}),\n });\n }\n }\n\n // Capture stderr from all stdio servers (connected or failed)\n for (const name of serverNames) {\n captureStderr(name);\n }\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n\n for (const name of serverNames) {\n serverStatuses.set(name, {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(servers[name]!),\n error: errMsg,\n ...(serverNeedsAuth(name, servers[name]!, errMsg) ? { needsAuth: true } : {}),\n });\n }\n }\n }\n\n /**\n * Whether a failed HTTP server is blocked on OAuth authorization.\n *\n * Provider-backed servers report the exact state tracked by `@mastra/mcp`.\n * Bare `url` entries carry no provider until the user authenticates, so the\n * signal is a 401 in the connect error — surfaced either as the status text\n * or as the RFC 6750 `invalid_token` bearer error code in the response body.\n */\n function serverNeedsAuth(name: string, cfg: McpServerConfig, error?: string): boolean {\n if (getTransport(cfg) !== 'http') return false;\n if (client?.getServerAuthState?.(name) === 'needs-auth') return true;\n if (serverDefs[name]?.authProvider) return false;\n return error !== undefined && /\\b401\\b|unauthorized|invalid_token/i.test(error);\n }\n\n /**\n * Runs a single-server connect action (reconnect or authenticate), then\n * refreshes that server's tools and status from the client.\n */\n async function connectSingleServer(\n name: string,\n cfg: McpServerConfig,\n connect: () => Promise<unknown>,\n ): Promise<McpServerStatus> {\n const transport = getTransport(cfg);\n\n // Remove old tools for this server\n const prefix = `${name}_`;\n for (const key of Object.keys(tools)) {\n if (key.startsWith(prefix)) {\n delete tools[key];\n }\n }\n\n // Clear old logs and mark as connecting\n stderrLogs.delete(name);\n serverStatuses.set(name, {\n name,\n connected: false,\n connecting: true,\n toolCount: 0,\n toolNames: [],\n transport,\n });\n\n try {\n await connect();\n\n // Recapture stderr for the reconnected server\n captureStderr(name);\n\n // Fetch updated toolsets to get this server's tools\n const { toolsets, errors } = await client!.listToolsetsWithErrors();\n const serverTools = toolsets[name];\n const serverError = errors[name];\n\n if (serverError) {\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: serverError,\n ...(serverNeedsAuth(name, cfg, serverError) ? { needsAuth: true } : {}),\n };\n serverStatuses.set(name, status);\n return status;\n } else if (serverTools && Object.keys(serverTools).length > 0) {\n const toolNames = Object.keys(serverTools).map(t => `${name}_${t}`);\n for (const [toolName, toolConfig] of Object.entries(serverTools)) {\n tools[`${name}_${toolName}`] = toolConfig;\n }\n const status: McpServerStatus = {\n name,\n connected: true,\n toolCount: toolNames.length,\n toolNames,\n transport,\n };\n serverStatuses.set(name, status);\n return status;\n } else {\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: 'Failed to connect',\n };\n serverStatuses.set(name, status);\n return status;\n }\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n const status: McpServerStatus = {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport,\n error: errMsg,\n ...(serverNeedsAuth(name, cfg, errMsg) ? { needsAuth: true } : {}),\n };\n serverStatuses.set(name, status);\n return status;\n }\n }\n\n async function disconnect(): Promise<void> {\n if (client) {\n try {\n await client.disconnect();\n } catch {\n // Ignore disconnect errors\n }\n client = null;\n }\n }\n\n /** Tear down all connections and reconnect every enabled server. */\n async function rebuildConnections(): Promise<void> {\n await disconnect();\n tools = {};\n serverStatuses = new Map();\n stderrLogs = new Map();\n initialized = false;\n await connectAndCollectTools();\n initialized = true;\n }\n\n function disabledStatus(name: string): McpServerStatus {\n const cfg = config.mcpServers?.[name];\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: cfg ? getTransport(cfg) : 'stdio',\n disabled: true,\n disabledScope: disabledScopeOf(name),\n };\n }\n\n // Read-merge-write persistence: re-read the persisted state and apply this\n // operation's delta on top, then adopt the merged result in memory. This\n // way a concurrent mastracode process's changes (e.g. another window\n // flipping the global kill switch) are never clobbered by this manager's\n // construction-time snapshot.\n function persistProjectDelta(mutate: (overrides: Map<string, McpProjectServerOverride>) => void): void {\n const fresh = new Map<string, McpProjectServerOverride>(Object.entries(loadProjectServerOverrides(projectDir)));\n mutate(fresh);\n projectServerOverrides = fresh;\n saveProjectServerOverrides(projectDir, Object.fromEntries(fresh));\n }\n\n function persistGlobalDelta(mutate: (state: { allDisabled: boolean; disabledServers: Set<string> }) => void): void {\n const onDisk = loadGlobalDisableState();\n const fresh = { allDisabled: onDisk.allDisabled, disabledServers: new Set(onDisk.disabledServers) };\n mutate(fresh);\n globallyDisabledServers = fresh.disabledServers;\n globalDisableState = { allDisabled: fresh.allDisabled, disabledServers: Array.from(fresh.disabledServers) };\n saveGlobalDisableState(globalDisableState);\n }\n\n return {\n async init() {\n if (initialized) return;\n await connectAndCollectTools();\n initialized = true;\n },\n\n async initInBackground(): Promise<McpInitResult> {\n await this.init();\n const statuses = Array.from(serverStatuses.values(), withManagerState);\n const connected = statuses.filter(s => s.connected);\n const failed = statuses.filter(s => !s.connected && !s.disabled);\n return {\n connected,\n failed,\n skipped: [...(config.skippedServers ?? [])],\n totalTools: connected.reduce((sum, s) => sum + s.toolCount, 0),\n };\n },\n\n async reload() {\n config = applyExtraServers(loadMcpConfig(projectDir, configDirName, externalDiscovery));\n projectServerOverrides = new Map(Object.entries(loadProjectServerOverrides(projectDir)));\n globalDisableState = loadGlobalDisableState();\n globallyDisabledServers = new Set(globalDisableState.disabledServers);\n await rebuildConnections();\n },\n\n async setServerDisabled(name: string, disabled: boolean, options?: { global?: boolean }): Promise<McpServerStatus> {\n if (!config.mcpServers?.[name]) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n const wasEffectivelyDisabled = isDisabled(name);\n\n if (options?.global) {\n persistGlobalDelta(state => {\n if (disabled) {\n state.disabledServers.add(name);\n } else {\n state.disabledServers.delete(name);\n }\n });\n } else {\n persistProjectDelta(overrides => {\n overrides.set(name, disabled ? 'disabled' : 'enabled');\n });\n }\n\n // Only rebuild connections when the server's effective state actually\n // flipped. State changes that only alter an inherited default or an\n // override beneath the global kill switch leave connections alone.\n if (isDisabled(name) !== wasEffectivelyDisabled) {\n await rebuildConnections();\n return withManagerState(serverStatuses.get(name) ?? disabledStatus(name));\n }\n return withManagerState(\n isDisabled(name) ? disabledStatus(name) : (serverStatuses.get(name) ?? disabledStatus(name)),\n );\n },\n\n async inheritServer(name: string): Promise<McpServerStatus> {\n if (!config.mcpServers?.[name]) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n const wasEffectivelyDisabled = isDisabled(name);\n persistProjectDelta(overrides => {\n overrides.delete(name);\n });\n if (isDisabled(name) !== wasEffectivelyDisabled) {\n await rebuildConnections();\n }\n return withManagerState(\n isDisabled(name) ? disabledStatus(name) : (serverStatuses.get(name) ?? disabledStatus(name)),\n );\n },\n\n async setAllDisabled(disabled: boolean, options?: { global?: boolean }): Promise<void> {\n const configuredNames = Object.keys(config.mcpServers ?? {});\n const effectiveBefore = configuredNames.filter(name => isDisabled(name)).join(',');\n if (options?.global) {\n persistGlobalDelta(state => {\n state.allDisabled = disabled;\n if (!disabled) {\n // Enabling globally also clears globally disabled server names so\n // \"/mcp enable all --global\" fully restores global defaults.\n state.disabledServers.clear();\n }\n });\n } else {\n persistProjectDelta(overrides => {\n for (const name of configuredNames) {\n overrides.set(name, disabled ? 'disabled' : 'enabled');\n }\n });\n }\n const effectiveAfter = configuredNames.filter(name => isDisabled(name)).join(',');\n if (effectiveAfter !== effectiveBefore) {\n await rebuildConnections();\n }\n },\n\n async inheritAllServers(): Promise<void> {\n const configuredNames = Object.keys(config.mcpServers ?? {});\n const effectiveBefore = configuredNames.filter(name => isDisabled(name)).join(',');\n persistProjectDelta(overrides => {\n overrides.clear();\n });\n const effectiveAfter = configuredNames.filter(name => isDisabled(name)).join(',');\n if (effectiveAfter !== effectiveBefore) {\n await rebuildConnections();\n }\n },\n\n getDisabledServers() {\n return Object.keys(config.mcpServers ?? {})\n .filter(name => isDisabled(name))\n .sort();\n },\n\n isAllDisabledGlobally() {\n return globalDisableState.allDisabled;\n },\n\n async reconnectServer(name: string): Promise<McpServerStatus> {\n if (isDisabled(name)) {\n return { ...disabledStatus(name), error: `Server \"${name}\" is disabled — enable it first` };\n }\n const cfg = config.mcpServers?.[name];\n if (!cfg) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n if (!client) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n error: 'MCP client not initialized',\n };\n }\n\n // Use MCPClient's per-server reconnect\n return connectSingleServer(name, cfg, () => client!.reconnectServer(name));\n },\n\n async authenticateServer(\n name: string,\n options?: { onAuthorizationUrl?: (url: string) => void; timeoutMs?: number },\n ): Promise<McpServerStatus> {\n const cfg = config.mcpServers?.[name];\n if (!cfg) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" not found in config`,\n };\n }\n\n if (isDisabled(name)) {\n return { ...disabledStatus(name), error: `Server \"${name}\" is disabled — enable it first` };\n }\n\n if (!client) {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: getTransport(cfg),\n error: 'MCP client not initialized',\n };\n }\n\n if (getTransport(cfg) !== 'http') {\n return {\n name,\n connected: false,\n toolCount: 0,\n toolNames: [],\n transport: 'stdio',\n error: `Server \"${name}\" uses stdio transport, which does not support OAuth`,\n };\n }\n\n // Zero-config provisioning: a bare `url` entry gets a provider with the\n // default redirect URL the first time the user authenticates. The Client\n // ID Metadata Document takes care of the client identity.\n //\n // NOTE: `serverDefs[name]` is the same object reference the MCPClient was\n // constructed with, and connectHttp reads `authProvider` live off it at\n // connect time, so mutating it here reaches the already-created client.\n // If MCPClient ever defensively copies its server config, zero-config auth\n // would need an explicit \"set this server's provider\" API instead.\n const def = serverDefs[name];\n if (def?.url && !def.authProvider) {\n def.authProvider = createOAuthProvider(name, { ...(cfg as McpHttpServerConfig), oauth: DEFAULT_OAUTH_CONFIG });\n }\n\n // Reject a concurrent second attempt for the same server. authUrlHandlers\n // has a single slot per server, so a second call with an onAuthorizationUrl\n // would overwrite the first caller's handler (which then never sees the\n // URL) and its finally would delete the handler out from under the other\n // in-flight call. Gate on authenticatingServers rather than authUrlHandlers\n // so callers without a URL handler are caught too. The underlying\n // client.authenticate already coalesces same-server calls into one flow.\n if (authenticatingServers.has(name)) {\n const current = serverStatuses.get(name);\n return {\n name,\n connected: current?.connected ?? false,\n connecting: current?.connecting,\n needsAuth: current?.needsAuth,\n toolCount: current?.toolCount ?? 0,\n toolNames: current?.toolNames ?? [],\n transport: current?.transport ?? getTransport(cfg),\n error: `Authentication for \"${name}\" is already in progress`,\n };\n }\n\n if (options?.onAuthorizationUrl) {\n authUrlHandlers.set(name, options.onAuthorizationUrl);\n }\n authenticatingServers.add(name);\n cancelledAuthServers.delete(name);\n try {\n const result = await connectSingleServer(name, cfg, () =>\n client!.authenticate(name, options?.timeoutMs === undefined ? undefined : { timeoutMs: options.timeoutMs }),\n );\n // A cancelled flow resolves with a failed status; mark it so callers can\n // distinguish a deliberate cancel from a genuine authentication failure.\n // Write the marker back to the durable status map (not just the returned\n // value) so a selector reopened after cancellation still sees it.\n const finalStatus =\n cancelledAuthServers.has(name) && !result.connected ? { ...result, cancelled: true } : result;\n serverStatuses.set(name, finalStatus);\n return finalStatus;\n } finally {\n authUrlHandlers.delete(name);\n authenticatingServers.delete(name);\n cancelledAuthServers.delete(name);\n }\n },\n\n async cancelServerAuthentication(name: string): Promise<boolean> {\n if (!client) {\n return false;\n }\n // Record the intent before aborting so the resolving authenticateServer()\n // call can tag its failed status as cancelled rather than failed.\n cancelledAuthServers.add(name);\n const cancelled = await client.cancelAuthentication(name);\n if (!cancelled) {\n cancelledAuthServers.delete(name);\n }\n return cancelled;\n },\n\n disconnect,\n\n getTools() {\n return { ...tools };\n },\n\n hasServers() {\n const hasConfigured = config.mcpServers !== undefined && Object.keys(config.mcpServers).length > 0;\n const hasSkipped = config.skippedServers !== undefined && config.skippedServers.length > 0;\n return hasConfigured || hasSkipped;\n },\n\n getServerStatuses() {\n return Array.from(serverStatuses.values(), withManagerState);\n },\n\n getSkippedServers() {\n return [...(config.skippedServers ?? [])];\n },\n\n getConfigPaths() {\n return {\n project: getProjectMcpPath(projectDir, configDirName),\n global: getGlobalMcpPath(configDirName),\n claude: getClaudeSettingsPath(projectDir),\n };\n },\n\n getConfig() {\n return config;\n },\n\n getServerLogs(name: string) {\n return [...(stderrLogs.get(name) ?? [])];\n },\n };\n}\n"],"mappings":"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"}
@@ -30,21 +30,33 @@ export interface McpHttpServerConfig {
30
30
  * OAuth client configuration for an HTTP MCP server.
31
31
  */
32
32
  export interface McpHttpOAuthConfig {
33
- /** Redirect URL for OAuth callbacks. Defaults to DEFAULT_OAUTH_REDIRECT_URL when omitted. */
33
+ /**
34
+ * Redirect URL for OAuth callbacks. Defaults to DEFAULT_OAUTH_REDIRECT_URL
35
+ * when omitted. Without a `clientId`, Mastra Code's hosted Client ID
36
+ * Metadata Document only lists the default URL and its port fallbacks, so
37
+ * a custom value works only with authorization servers that ignore the
38
+ * loopback port (RFC 8252 §7.3); otherwise set `clientId` too.
39
+ */
34
40
  redirectUrl?: string;
35
41
  /**
36
42
  * Shorthand for a loopback redirect URL: synthesizes
37
43
  * `http://localhost:<callbackPort>/callback`, matching the convention
38
44
  * Claude Code and Codex use. Config files reject entries that set both
39
45
  * `callbackPort` and `redirectUrl`; for programmatically registered
40
- * servers, `callbackPort` takes precedence over `redirectUrl`.
46
+ * servers, `callbackPort` takes precedence over `redirectUrl`. The same
47
+ * `clientId` caveat as `redirectUrl` applies.
41
48
  */
42
49
  callbackPort?: number;
43
50
  /** Human-readable OAuth client name */
44
51
  clientName?: string;
45
52
  /** Optional scopes requested during OAuth */
46
53
  scopes?: string[];
47
- /** Optional pre-registered OAuth client ID */
54
+ /**
55
+ * Optional pre-registered OAuth client ID. When omitted, Mastra Code
56
+ * identifies itself with its Client ID Metadata Document URL, which
57
+ * authorization servers supporting URL-based client IDs accept without
58
+ * any registration step.
59
+ */
48
60
  clientId?: string;
49
61
  /** Optional pre-registered OAuth client secret */
50
62
  clientSecret?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/mcp/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kEAAkE;IAClE,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wCAAwC;IACxC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC,gGAAgG;IAChG,eAAe,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACzC,kGAAkG;IAClG,aAAa,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvC,mGAAmG;IACnG,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/mcp/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kEAAkE;IAClE,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wCAAwC;IACxC,SAAS,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC,gGAAgG;IAChG,eAAe,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACzC,kGAAkG;IAClG,aAAa,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvC,mGAAmG;IACnG,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"custom-packs.d.ts","sourceRoot":"","sources":["../../src/onboarding/custom-packs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAsB3F"}
1
+ {"version":3,"file":"custom-packs.d.ts","sourceRoot":"","sources":["../../src/onboarding/custom-packs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA8B3F"}
@@ -10,6 +10,9 @@ function removeCustomPackFromSettings(settings, packId) {
10
10
  settings.models.modeDefaults = {};
11
11
  } else if (modeDefaultsMatchRemovedPack) settings.models.modeDefaults = {};
12
12
  if (settings.onboarding.modePackId === packId) settings.onboarding.modePackId = null;
13
+ const fallbacks = settings.models.packFallbacks ?? {};
14
+ settings.models.packFallbacks = Object.fromEntries(Object.entries(fallbacks).filter(([sourcePackId, targetPackId]) => sourcePackId !== packId && targetPackId !== packId));
15
+ delete settings.models.packAccountPreferences?.[packId];
13
16
  }
14
17
  //#endregion
15
18
  export { removeCustomPackFromSettings };
@@ -1 +1 @@
1
- {"version":3,"file":"custom-packs.js","names":[],"sources":["../../src/onboarding/custom-packs.ts"],"sourcesContent":["/**\n * Pure `GlobalSettings` mutation helpers for custom model packs. Shared by the\n * TUI `/models` command and the web settings routes — no UI dependencies.\n */\nimport type { GlobalSettings } from './settings.js';\n\nexport function removeCustomPackFromSettings(settings: GlobalSettings, packId: string): void {\n if (!packId.startsWith('custom:')) return;\n const packName = packId.slice('custom:'.length);\n const removedPack = settings.customModelPacks.find(p => p.name === packName);\n settings.customModelPacks = settings.customModelPacks.filter(p => p.name !== packName);\n\n const modeDefaultsMatchRemovedPack =\n !!removedPack &&\n settings.models.modeDefaults.plan === removedPack.models.plan &&\n settings.models.modeDefaults.build === removedPack.models.build &&\n settings.models.modeDefaults.fast === removedPack.models.fast;\n\n if (settings.models.activeModelPackId === packId) {\n settings.models.activeModelPackId = null;\n settings.models.modeDefaults = {};\n } else if (modeDefaultsMatchRemovedPack) {\n settings.models.modeDefaults = {};\n }\n\n if (settings.onboarding.modePackId === packId) {\n settings.onboarding.modePackId = null;\n }\n}\n"],"mappings":";AAMA,SAAgB,6BAA6B,UAA0B,QAAsB;CAC3F,IAAI,CAAC,OAAO,WAAW,SAAS,GAAG;CACnC,MAAM,WAAW,OAAO,MAAM,CAAgB;CAC9C,MAAM,cAAc,SAAS,iBAAiB,MAAK,MAAK,EAAE,SAAS,QAAQ;CAC3E,SAAS,mBAAmB,SAAS,iBAAiB,QAAO,MAAK,EAAE,SAAS,QAAQ;CAErF,MAAM,+BACJ,CAAC,CAAC,eACF,SAAS,OAAO,aAAa,SAAS,YAAY,OAAO,QACzD,SAAS,OAAO,aAAa,UAAU,YAAY,OAAO,SAC1D,SAAS,OAAO,aAAa,SAAS,YAAY,OAAO;CAE3D,IAAI,SAAS,OAAO,sBAAsB,QAAQ;EAChD,SAAS,OAAO,oBAAoB;EACpC,SAAS,OAAO,eAAe,CAAC;CAClC,OAAO,IAAI,8BACT,SAAS,OAAO,eAAe,CAAC;CAGlC,IAAI,SAAS,WAAW,eAAe,QACrC,SAAS,WAAW,aAAa;AAErC"}
1
+ {"version":3,"file":"custom-packs.js","names":[],"sources":["../../src/onboarding/custom-packs.ts"],"sourcesContent":["/**\n * Pure `GlobalSettings` mutation helpers for custom model packs. Shared by the\n * TUI `/models` command and the web settings routes — no UI dependencies.\n */\nimport type { GlobalSettings } from './settings.js';\n\nexport function removeCustomPackFromSettings(settings: GlobalSettings, packId: string): void {\n if (!packId.startsWith('custom:')) return;\n const packName = packId.slice('custom:'.length);\n const removedPack = settings.customModelPacks.find(p => p.name === packName);\n settings.customModelPacks = settings.customModelPacks.filter(p => p.name !== packName);\n\n const modeDefaultsMatchRemovedPack =\n !!removedPack &&\n settings.models.modeDefaults.plan === removedPack.models.plan &&\n settings.models.modeDefaults.build === removedPack.models.build &&\n settings.models.modeDefaults.fast === removedPack.models.fast;\n\n if (settings.models.activeModelPackId === packId) {\n settings.models.activeModelPackId = null;\n settings.models.modeDefaults = {};\n } else if (modeDefaultsMatchRemovedPack) {\n settings.models.modeDefaults = {};\n }\n\n if (settings.onboarding.modePackId === packId) {\n settings.onboarding.modePackId = null;\n }\n\n const fallbacks = settings.models.packFallbacks ?? {};\n settings.models.packFallbacks = Object.fromEntries(\n Object.entries(fallbacks).filter(\n ([sourcePackId, targetPackId]) => sourcePackId !== packId && targetPackId !== packId,\n ),\n );\n delete settings.models.packAccountPreferences?.[packId];\n}\n"],"mappings":";AAMA,SAAgB,6BAA6B,UAA0B,QAAsB;CAC3F,IAAI,CAAC,OAAO,WAAW,SAAS,GAAG;CACnC,MAAM,WAAW,OAAO,MAAM,CAAgB;CAC9C,MAAM,cAAc,SAAS,iBAAiB,MAAK,MAAK,EAAE,SAAS,QAAQ;CAC3E,SAAS,mBAAmB,SAAS,iBAAiB,QAAO,MAAK,EAAE,SAAS,QAAQ;CAErF,MAAM,+BACJ,CAAC,CAAC,eACF,SAAS,OAAO,aAAa,SAAS,YAAY,OAAO,QACzD,SAAS,OAAO,aAAa,UAAU,YAAY,OAAO,SAC1D,SAAS,OAAO,aAAa,SAAS,YAAY,OAAO;CAE3D,IAAI,SAAS,OAAO,sBAAsB,QAAQ;EAChD,SAAS,OAAO,oBAAoB;EACpC,SAAS,OAAO,eAAe,CAAC;CAClC,OAAO,IAAI,8BACT,SAAS,OAAO,eAAe,CAAC;CAGlC,IAAI,SAAS,WAAW,eAAe,QACrC,SAAS,WAAW,aAAa;CAGnC,MAAM,YAAY,SAAS,OAAO,iBAAiB,CAAC;CACpD,SAAS,OAAO,gBAAgB,OAAO,YACrC,OAAO,QAAQ,SAAS,CAAC,CAAC,QACvB,CAAC,cAAc,kBAAkB,iBAAiB,UAAU,iBAAiB,MAChF,CACF;CACA,OAAO,SAAS,OAAO,yBAAyB;AAClD"}
@@ -1,3 +1,3 @@
1
- import { getSettingsPath, loadSettings, resolveModelDefaults, resolveOmModel, resolveOmRoleModel, saveSettings } from "./settings.js";
2
1
  import { ONBOARDING_VERSION, getAvailableModePacks, getAvailableOmPacks, selectPreferredOMPack } from "./packs.js";
2
+ import { getSettingsPath, loadSettings, resolveModelDefaults, resolveOmModel, resolveOmRoleModel, saveSettings } from "./settings.js";
3
3
  export { ONBOARDING_VERSION, getAvailableModePacks, getAvailableOmPacks, getSettingsPath, loadSettings, resolveModelDefaults, resolveOmModel, resolveOmRoleModel, saveSettings, selectPreferredOMPack };
@@ -6,6 +6,12 @@ export interface ModePack {
6
6
  build: string;
7
7
  plan: string;
8
8
  fast: string;
9
+ /**
10
+ * Optional observational-memory model. When set, OM observer/reflector
11
+ * resolve from the pack (and its fallback chain) instead of the standalone
12
+ * OM settings, unless an explicit OM role override exists.
13
+ */
14
+ memory?: string;
9
15
  };
10
16
  }
11
17
  export interface OMPack {
@@ -29,6 +35,44 @@ export interface ProviderAccess {
29
35
  export declare function getBuiltinModePack(packId: string): (ModePack & {
30
36
  providerId: string;
31
37
  }) | undefined;
38
+ /**
39
+ * All builtin mode packs regardless of provider access (descriptions use the
40
+ * apikey variant). For resolution-time lookups — fallback chains must resolve
41
+ * even when an access probe is stale; actual auth failures surface through
42
+ * the provider call itself.
43
+ */
44
+ export declare function listBuiltinModePacks(): ModePack[];
45
+ /** A pack id is known when it is a builtin mode pack or a saved custom pack. */
46
+ export declare function isKnownModePackId(packId: string, savedCustomPacks?: Array<{
47
+ name: string;
48
+ }>): boolean;
49
+ /**
50
+ * Drop pack-fallback entries whose source or target pack no longer exists
51
+ * (e.g. a deleted custom pack). Shape validation happens before this; here we
52
+ * only prune dangling references, with the same tolerance the rest of settings
53
+ * parsing uses.
54
+ */
55
+ export declare function pruneUnknownModePackFallbacks(fallbacks: Record<string, string>, savedCustomPacks?: Array<{
56
+ name: string;
57
+ }>): Record<string, string>;
58
+ /** Drop preferred-account bindings for missing packs or models no longer used by that pack. */
59
+ export declare function pruneUnknownPackAccountPreferences(preferences: Record<string, Record<string, string>>, savedCustomPacks?: Array<{
60
+ name: string;
61
+ models: Record<string, string>;
62
+ }>, modePackOverrides?: Record<string, Record<string, string>>): Record<string, Record<string, string>>;
63
+ /**
64
+ * Walk `settings.models.packFallbacks` from `startPackId`, returning the pack
65
+ * ids in cascade order starting with the pack itself. Cycles are allowed but
66
+ * the cascade gets exactly one revisit total (Q15: "full circle then one
67
+ * revisit then surface"): the walk stops when the next link would add a pack
68
+ * already in the chain after the revisit was consumed, or when a link
69
+ * dangles at an unknown pack. An A⇄B cycle therefore yields [A, B, A#2] and
70
+ * stops. This cap is the cycle handling for both the fallback model chain
71
+ * (request time) and the /models picker's chain display.
72
+ */
73
+ export declare function resolveModePackFallbackChain(fallbacks: Record<string, string>, startPackId: string, savedCustomPacks?: Array<{
74
+ name: string;
75
+ }>): string[];
32
76
  /**
33
77
  * Build the list of available mode packs based on which providers the user
34
78
  * can actually reach (API key or OAuth login).
@@ -1 +1 @@
1
- {"version":3,"file":"packs.d.ts","sourceRoot":"","sources":["../../src/onboarding/packs.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE7D,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,gBAAgB,EAAE,mBAAmB,CAAC;IACtC,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;CACzC;AAiDD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,QAAQ,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAUlG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,cAAc,EACtB,gBAAgB,GAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAM,GAC7E,QAAQ,EAAE,CAsCZ;AAyCD,6FAA6F;AAC7F,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,SAAsB,GAAG,MAAM,CAkB1G;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAuBpE;AAED,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,EAAE,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAc9G;AAMD,eAAO,MAAM,kBAAkB,IAAI,CAAC"}
1
+ {"version":3,"file":"packs.d.ts","sourceRoot":"","sources":["../../src/onboarding/packs.ts"],"names":[],"mappings":"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"}