@schoolai/shipyard 3.5.3 → 3.6.0-rc.20260507.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/capabilities/mcp-servers.ts","../src/shared/mcp/claude-code-credentials.ts","../src/shared/mcp/schemas.ts","../src/shared/mcp/resolve-servers.ts","../src/shared/auth/anthropic-credentials.ts","../src/shared/capabilities/account-integrations.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { AnthropicAuthMethod } from '@shipyard/loro-schema';\nimport type { GitRepoInfo, MCPServerInfo, MCPServerSource } from '@shipyard/session';\nimport { z } from 'zod';\nimport { readClaudeCodeCredentials } from '../mcp/claude-code-credentials.js';\nimport { VAULT_REF_PATTERN } from '../mcp/resolve-servers.js';\nimport { fetchClaudeAiIntegrations } from './account-integrations.js';\n\nconst MCPStdioEntrySchema = z\n .object({\n type: z.literal('stdio').optional(),\n command: z.string(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPOAuthConfigSchema = z\n .object({\n clientId: z.string().optional(),\n callbackPort: z.number().optional(),\n })\n .passthrough();\n\nconst MCPHttpEntrySchema = z\n .object({\n type: z.literal('http'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n oauth: MCPOAuthConfigSchema.optional(),\n })\n .passthrough();\n\nconst MCPSSEEntrySchema = z\n .object({\n type: z.literal('sse'),\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n })\n .passthrough();\n\nconst MCPServerEntrySchema = z.union([MCPStdioEntrySchema, MCPHttpEntrySchema, MCPSSEEntrySchema]);\n\n/**\n * Pure decision: should `fetchClaudeAiIntegrations` (the claude.ai Connectors\n * fetch) actually run for this user?\n *\n * Only the claude.ai OAuth method has access to that endpoint. `null` or\n * `undefined` (preference unknown — pre-backfill or caller didn't plumb the\n * value through) returns true to preserve the legacy behaviour for OAuth\n * users during the rollout window before the boot backfill writes a real\n * value. After backfill, `null` should be vanishingly rare.\n */\nexport function shouldFetchClaudeAiIntegrations(\n preferredAuth: AnthropicAuthMethod | null | undefined\n): boolean {\n if (preferredAuth == null) return true;\n return preferredAuth === 'claude-ai';\n}\n\nconst SECRET_PATTERNS = /^(sk-|ghp_|gho_|glpat-|xoxb-|xoxp-|Bearer\\s|token\\s)/i;\nconst SECRET_FLAGS = new Set(['--api-key', '--token', '--secret', '--password', '--key', '-k']);\n\nexport function redactArgs(args: string[]): string[] {\n return args.map((arg, i) => {\n if (SECRET_PATTERNS.test(arg)) return '***';\n const prevArg = i > 0 ? args[i - 1] : undefined;\n if (prevArg && SECRET_FLAGS.has(prevArg)) return '***';\n const eqIdx = arg.indexOf('=');\n if (eqIdx > 0 && SECRET_FLAGS.has(arg.slice(0, eqIdx))) {\n return `${arg.slice(0, eqIdx + 1)}***`;\n }\n return arg;\n });\n}\n\n/**\n * Redact all environment variable values. Env vars passed to MCP servers\n * frequently contain secrets (API keys, tokens) and should never be\n * exposed to the browser.\n */\nexport function redactEnv(env: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const key of Object.keys(env)) {\n result[key] = '***';\n }\n return result;\n}\n\ntype MCPStdioEntry = z.infer<typeof MCPStdioEntrySchema>;\ntype MCPHttpEntry = z.infer<typeof MCPHttpEntrySchema>;\ntype MCPSSEEntry = z.infer<typeof MCPSSEEntrySchema>;\n\nfunction isHttpEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPHttpEntry {\n return 'type' in data && data.type === 'http';\n}\n\nfunction isSSEEntry(data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry): data is MCPSSEEntry {\n return 'type' in data && data.type === 'sse';\n}\n\nfunction entryToServerInfo(\n name: string,\n data: MCPStdioEntry | MCPHttpEntry | MCPSSEEntry,\n source: MCPServerSource\n): MCPServerInfo {\n if (isHttpEntry(data)) {\n return {\n name,\n type: 'http',\n url: data.url,\n headers: data.headers,\n oauth: data.oauth,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n if (isSSEEntry(data)) {\n return {\n name,\n type: 'sse',\n url: data.url,\n headers: data.headers,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n }\n return {\n name,\n type: 'stdio',\n command: data.command,\n args: data.args,\n env: data.env,\n enabled: true,\n source,\n authStatus: 'unknown' as const,\n };\n}\n\nasync function readMCPConfig(\n filePath: string,\n source: MCPServerSource,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Promise<MCPServerInfo[]> {\n try {\n const raw = await readFile(filePath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; validated per-entry below\n const json = JSON.parse(raw) as Record<string, unknown>;\n\n const entries =\n typeof json.mcpServers === 'object' && json.mcpServers !== null ? json.mcpServers : json;\n\n const servers: MCPServerInfo[] = [];\n for (const [name, value] of Object.entries(entries)) {\n if (name === 'mcpServers') continue;\n /** Skip non-object values — settings files may have non-MCP keys (e.g. enabledMcpjsonServers: []). */\n if (typeof value !== 'object' || value === null || Array.isArray(value)) continue;\n const result = MCPServerEntrySchema.safeParse(value);\n if (!result.success) {\n const entry = {\n event: 'mcp_config_entry_invalid',\n name,\n source,\n filePath,\n error: result.error.message,\n };\n if (log) log(entry);\n // biome-ignore lint/suspicious/noConsole: diagnostic fallback when no structured logger is injected\n else console.warn('[mcp-servers]', entry.event, entry.name, entry.error);\n continue;\n }\n servers.push(entryToServerInfo(name, result.data, source));\n }\n return servers;\n } catch {\n return [];\n }\n}\n\nasync function readPluginMCPServers(): Promise<MCPServerInfo[]> {\n const servers: MCPServerInfo[] = [];\n try {\n const settingsPath = join(homedir(), '.claude', 'settings.json');\n const settingsRaw = await readFile(settingsPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking enabledPlugins field only\n const settings = JSON.parse(settingsRaw) as { enabledPlugins?: Record<string, boolean> };\n if (!settings.enabledPlugins) return [];\n\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const installedRaw = await readFile(installedPath, 'utf-8');\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; checking plugins field only\n const installed = JSON.parse(installedRaw) as {\n plugins?: Record<string, Array<{ installPath?: string }>>;\n };\n if (!installed.plugins) return [];\n\n for (const [pluginId, enabled] of Object.entries(settings.enabledPlugins)) {\n if (!enabled) continue;\n const installs = installed.plugins[pluginId];\n if (!installs || installs.length === 0) continue;\n const installPath = installs[0]?.installPath;\n if (!installPath) continue;\n\n const mcpJsonPath = join(installPath, '.mcp.json');\n const pluginServers = await readMCPConfig(mcpJsonPath, 'plugin');\n servers.push(...pluginServers);\n }\n } catch {}\n return servers;\n}\n\n/** Priority order for source-based deduplication (higher index wins). */\nconst SOURCE_PRIORITY: readonly MCPServerSource[] = [\n 'claudeai',\n 'plugin',\n 'user',\n 'project',\n 'local',\n 'mcp-json',\n];\n\n/**\n * Check Claude Code's credential store for servers that still have\n * `authStatus === 'unknown'` after checking Shipyard's own token store.\n * If Claude Code has a valid (non-expired) token, mark the server as authenticated.\n */\nasync function resolveClaudeCodeAuthStatuses(servers: Map<string, MCPServerInfo>): Promise<void> {\n for (const server of servers.values()) {\n if (server.authStatus !== 'unknown') continue;\n if (server.type === 'stdio') continue;\n const ccCreds = await readClaudeCodeCredentials(server.name, server.url ?? '');\n if (ccCreds?.accessToken && ccCreds.expiresAt && ccCreds.expiresAt > Date.now()) {\n server.authStatus = 'authenticated';\n }\n }\n}\n\nfunction resolveStdioAuthStatus(\n server: MCPServerInfo,\n tokens: Record<string, import('../mcp/token-store.js').MCPOAuthToken>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): void {\n if (!server.env) return;\n for (const value of Object.values(server.env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (!match) continue;\n const vaultKey = match[1] ?? '';\n const token = tokens[vaultKey];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n return;\n }\n }\n}\n\nasync function resolveAuthStatuses(\n servers: Map<string, MCPServerInfo>,\n tokenStore: import('../mcp/token-store.js').MCPTokenStore\n): Promise<void> {\n const tokens = await tokenStore.getAllTokens();\n for (const server of servers.values()) {\n if (server.type === 'stdio') {\n resolveStdioAuthStatus(server, tokens, tokenStore);\n continue;\n }\n const token = tokens[server.name];\n if (token) {\n server.authStatus = tokenStore.isExpired(token) ? 'unauthenticated' : 'authenticated';\n }\n }\n\n await resolveClaudeCodeAuthStatuses(servers);\n}\n\n/**\n * Detect MCP servers across user/project/plugin/claude.ai sources.\n *\n * `lastKnown` preserves the previously-detected MCP server list when the\n * detection itself throws (rare — readMCPConfig swallows its own errors —\n * but defense in depth against future regressions and unhandled rejections\n * inside `fetchClaudeAiIntegrations`). Mirror of the `lastKnownAgents`\n * pattern in `agents.ts`.\n *\n * Note: under normal operation, errors are swallowed inside the per-file\n * readers, so this function reaches the dedupe step with a possibly-empty\n * but well-shaped `allServers`. The `try/catch` here is the chokepoint that\n * catches anything escaping that — particularly account-integrations\n * fetches that hit the network.\n */\nexport async function detectMCPServers(\n environments: GitRepoInfo[],\n tokenStore?: import('../mcp/token-store.js').MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void,\n lastKnown?: MCPServerInfo[],\n /**\n * Gates the `fetchClaudeAiIntegrations` network fetch (Connectors page on\n * claude.ai). The fetch only succeeds with claude.ai OAuth tokens; for\n * console / api-key / SSO users it 401s and floods logs every refresh tick.\n *\n * Skipped when the user's chosen method is anything other than 'claude-ai'.\n * `null`/`undefined` (preference unknown — not yet backfilled) keeps the\n * current behaviour of attempting the fetch so OAuth users still get their\n * connectors before the boot-time backfill lands.\n */\n preferredAuth?: AnthropicAuthMethod | null\n): Promise<MCPServerInfo[]> {\n try {\n return await detectMCPServersInner(environments, tokenStore, log, preferredAuth);\n } catch (err) {\n /**\n * Preserve the original \"always resolves\" contract: inner readers\n * already swallow per-file errors and return [], so this catch only\n * fires for unhandled rejections (network failure in\n * `fetchClaudeAiIntegrations`, future regressions). Returning\n * lastKnown if available, otherwise [] — never rethrow, because the\n * Promise.all in detectCapabilities would tear down the whole refresh\n * for a single slice's failure.\n */\n const { logger } = await import('../logger.js');\n if (lastKnown && lastKnown.length > 0) {\n logger.warn(\n { err, lastKnownCount: lastKnown.length },\n 'detectMCPServers threw — preserving lastKnown'\n );\n return lastKnown;\n }\n logger.debug({ err }, 'detectMCPServers threw with no lastKnown — returning []');\n return [];\n }\n}\n\nasync function detectMCPServersInner(\n environments: GitRepoInfo[],\n tokenStore?: import('../mcp/token-store.js').MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void,\n preferredAuth?: AnthropicAuthMethod | null\n): Promise<MCPServerInfo[]> {\n const allServers: MCPServerInfo[] = [];\n\n const userSettingsPath = join(homedir(), '.claude', 'settings.json');\n const userLocalSettingsPath = join(homedir(), '.claude', 'settings.local.json');\n const userMcpJsonPath = join(homedir(), '.mcp.json');\n const userClaudeJsonPath = join(homedir(), '.claude.json');\n\n /**\n * Only the claude.ai OAuth flow exposes the Connectors page that\n * `fetchClaudeAiIntegrations` reads. Console / api-key / SSO / cloud-provider\n * users would 401 and produce log noise on every refresh tick. `null` (not\n * yet backfilled) and `undefined` (caller didn't plumb through) keep the\n * legacy behaviour to preserve OAuth users' connectors during rollout.\n */\n const fetchClaudeAi = shouldFetchClaudeAiIntegrations(preferredAuth);\n const claudeAiPromise: Promise<MCPServerInfo[]> = fetchClaudeAi\n ? fetchClaudeAiIntegrations(log)\n : Promise.resolve([]);\n if (!fetchClaudeAi && log) {\n log({ event: 'claudeai_integrations_skipped_by_method', method: preferredAuth ?? null });\n }\n\n const [\n userServers,\n userLocalServers,\n userMcpJsonServers,\n userClaudeJsonServers,\n pluginServers,\n claudeAiServers,\n ] = await Promise.all([\n readMCPConfig(userSettingsPath, 'user', log),\n readMCPConfig(userLocalSettingsPath, 'user', log),\n readMCPConfig(userMcpJsonPath, 'user', log),\n readMCPConfig(userClaudeJsonPath, 'user', log),\n readPluginMCPServers(),\n claudeAiPromise,\n ]);\n allServers.push(\n ...claudeAiServers,\n ...pluginServers,\n ...userServers,\n ...userLocalServers,\n ...userMcpJsonServers,\n ...userClaudeJsonServers\n );\n\n const envResults = await Promise.all(\n environments.map((env) => {\n const projectPath = join(env.path, '.claude', 'settings.json');\n const localPath = join(env.path, '.claude', 'settings.local.json');\n const mcpJsonPath = join(env.path, '.mcp.json');\n return Promise.all([\n readMCPConfig(projectPath, 'project', log),\n readMCPConfig(localPath, 'local', log),\n readMCPConfig(mcpJsonPath, 'mcp-json', log),\n ]);\n })\n );\n for (const [projectServers, localServers, mcpJsonServers] of envResults) {\n allServers.push(...projectServers, ...localServers, ...mcpJsonServers);\n }\n\n const deduped = new Map<string, MCPServerInfo>();\n for (const server of allServers) {\n const existing = deduped.get(server.name);\n if (\n !existing ||\n SOURCE_PRIORITY.indexOf(server.source) >= SOURCE_PRIORITY.indexOf(existing.source)\n ) {\n deduped.set(server.name, server);\n }\n }\n\n if (tokenStore) {\n await resolveAuthStatuses(deduped, tokenStore);\n }\n\n return [...deduped.values()];\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport type { OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';\nimport { z } from 'zod';\n\nimport { logger } from '../logger.js';\nimport { DiscoveryStateSchema } from './schemas.js';\n\nconst execFile = promisify(execFileCb);\n\n/**\n * Represents a single OAuth entry from Claude Code's credential store.\n *\n * Claude Code v2.1.81+ stores credentials in macOS Keychain under\n * service name \"Claude Code-credentials\". Older versions used\n * `~/.claude/.credentials.json`.\n *\n * The format is owned by Claude Code and may change without notice.\n * All fields are optional to tolerate schema drift.\n */\nexport interface ClaudeCodeOAuthEntry {\n serverName: string;\n serverUrl: string;\n clientId?: string;\n clientSecret?: string;\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: number;\n discoveryState?: OAuthDiscoveryState;\n}\n\n/**\n * Schema for a single entry in `mcpOAuth`. We use `.passthrough()` so\n * unknown future fields are preserved.\n *\n * CC stores `null` for cleared fields (e.g., `accessToken: null` after\n * logout). Zod's `.optional()` only accepts `undefined`, not `null`,\n * so we must use `.nullable().optional()` to tolerate both.\n */\nconst ClaudeCodeOAuthEntrySchema = z\n .object({\n serverName: z.string().nullable().optional(),\n serverUrl: z.string().nullable().optional(),\n clientId: z.string().nullable().optional(),\n clientSecret: z.string().nullable().optional(),\n accessToken: z.string().nullable().optional(),\n refreshToken: z.string().nullable().optional(),\n expiresAt: z.number().nullable().optional(),\n discoveryState: DiscoveryStateSchema.nullable().optional(),\n })\n .passthrough();\n\nconst CredentialsFileSchema = z\n .object({\n mcpOAuth: z.record(z.string(), z.unknown()).optional(),\n })\n .passthrough();\n\nconst CREDENTIALS_PATH = join(homedir(), '.claude', '.credentials.json');\nconst KEYCHAIN_SERVICE = 'Claude Code-credentials';\n\nconst PluginMcpJsonSchema = z.record(\n z.string(),\n z\n .object({\n url: z.string().optional(),\n oauth: z.object({ clientId: z.string(), callbackPort: z.number().optional() }).optional(),\n })\n .passthrough()\n);\n\n/**\n * Read OAuth clientIds from CC plugin `.mcp.json` files.\n * Returns a Map from server URL to clientId.\n */\nasync function extractClientIdsFromPlugin(\n installPath: string,\n result: Map<string, string>\n): Promise<void> {\n const raw = await readFile(join(installPath, '.mcp.json'), 'utf-8');\n const json: unknown = JSON.parse(raw);\n const outer = z.record(z.string(), z.unknown()).safeParse(json);\n if (!outer.success) return;\n const servers = outer.data.mcpServers ?? outer.data;\n const validated = PluginMcpJsonSchema.safeParse(servers);\n if (!validated.success) return;\n\n for (const config of Object.values(validated.data)) {\n if (config.url && config.oauth?.clientId) {\n result.set(config.url, config.oauth.clientId);\n }\n }\n}\n\nexport async function readPluginClientIds(): Promise<Map<string, string>> {\n const result = new Map<string, string>();\n try {\n const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');\n const InstalledPluginsSchema = z.object({\n plugins: z\n .record(z.string(), z.array(z.object({ installPath: z.string().optional() })))\n .optional(),\n });\n const parsed = InstalledPluginsSchema.safeParse(\n JSON.parse(await readFile(installedPath, 'utf-8'))\n );\n if (!parsed.success || !parsed.data.plugins) return result;\n\n for (const installs of Object.values(parsed.data.plugins)) {\n const installPath = installs?.[0]?.installPath;\n if (!installPath) continue;\n await extractClientIdsFromPlugin(installPath, result).catch(() => {});\n }\n } catch {}\n return result;\n}\n\n/**\n * Attempt to read Claude Code's credential blob from macOS Keychain.\n * Returns the parsed JSON object or null if unavailable.\n */\nasync function readKeychainCredentials(): Promise<Record<string, unknown> | null> {\n if (process.platform !== 'darwin') return null;\n\n try {\n const { stdout } = await execFile(\n 'security',\n ['find-generic-password', '-s', KEYCHAIN_SERVICE, '-w'],\n { timeout: 5_000 }\n );\n const result = z.record(z.string(), z.unknown()).safeParse(JSON.parse(stdout.trim()));\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Search a parsed credentials object for the best MCP OAuth entry\n * matching the given `serverUrl`. Prefers entries with an access token;\n * separately gathers clientId from any matching entry.\n *\n * CC stores entries under both bare (`slack`) and plugin-prefixed\n * (`plugin:slack:slack`) keys. The prefixed entries typically have\n * tokens while the bare entries may only have clientId. We merge\n * across all matching entries to get the most complete picture.\n */\n/** Scan a flat mcpOAuth record for entries matching serverUrl. */\nfunction scanEntries(\n mcpOAuth: Record<string, unknown>,\n serverUrl: string\n): {\n bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null;\n clientId?: string;\n clientSecret?: string;\n discoveryState?: OAuthDiscoveryState;\n} {\n let bestWithToken: z.infer<typeof ClaudeCodeOAuthEntrySchema> | null = null;\n let clientId: string | undefined;\n let clientSecret: string | undefined;\n let discoveryState: OAuthDiscoveryState | undefined;\n\n for (const value of Object.values(mcpOAuth)) {\n const entryResult = ClaudeCodeOAuthEntrySchema.safeParse(value);\n if (!entryResult.success) continue;\n\n const entry = entryResult.data;\n if (entry.serverUrl !== serverUrl) continue;\n\n if (entry.clientId && !clientId) {\n clientId = entry.clientId;\n clientSecret = entry.clientSecret || undefined;\n }\n if (entry.discoveryState && !discoveryState) {\n discoveryState = entry.discoveryState;\n }\n if (entry.accessToken && !bestWithToken) {\n bestWithToken = entry;\n }\n }\n\n return { bestWithToken, clientId, clientSecret, discoveryState };\n}\n\nfunction findMatchingEntry(\n credentialsData: Record<string, unknown>,\n serverName: string,\n serverUrl: string\n): ClaudeCodeOAuthEntry | null {\n const fileResult = CredentialsFileSchema.safeParse(credentialsData);\n if (!fileResult.success) return null;\n\n const mcpOAuth = fileResult.data.mcpOAuth;\n if (!mcpOAuth) return null;\n\n const { bestWithToken, clientId, clientSecret, discoveryState } = scanEntries(\n mcpOAuth,\n serverUrl\n );\n if (!bestWithToken && !clientId) return null;\n\n return {\n serverName: bestWithToken?.serverName ?? serverName,\n serverUrl,\n clientId: bestWithToken?.clientId || clientId,\n clientSecret: bestWithToken?.clientSecret || clientSecret || undefined,\n accessToken: bestWithToken?.accessToken || undefined,\n refreshToken: bestWithToken?.refreshToken || undefined,\n expiresAt: bestWithToken?.expiresAt ?? undefined,\n discoveryState: bestWithToken?.discoveryState ?? discoveryState ?? undefined,\n };\n}\n\n/**\n * Search Claude Code's credential store for an MCP OAuth entry\n * matching the given `serverUrl`.\n *\n * Tries macOS Keychain first (Claude Code v2.1.81+), then falls back\n * to the legacy file at `~/.claude/.credentials.json`.\n *\n * Keys in the store use hashes we cannot reproduce, so we match\n * by `serverUrl` instead. Returns the first entry whose `serverUrl`\n * matches and that has a non-empty `clientId`.\n */\nexport async function readClaudeCodeCredentials(\n serverName: string,\n serverUrl: string\n): Promise<ClaudeCodeOAuthEntry | null> {\n if (!serverUrl) return null;\n\n try {\n const keychainData = await readKeychainCredentials();\n if (keychainData) {\n const entry = findMatchingEntry(keychainData, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in macOS Keychain');\n return entry;\n }\n }\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from Keychain');\n }\n\n try {\n const raw = await readFile(CREDENTIALS_PATH, 'utf-8');\n const fileResult = CredentialsFileSchema.safeParse(JSON.parse(raw));\n if (fileResult.success) {\n const entry = findMatchingEntry(fileResult.data, serverName, serverUrl);\n if (entry) {\n logger.debug({ serverUrl }, 'Found Claude Code credentials in legacy file');\n return entry;\n }\n }\n\n return null;\n } catch (err: unknown) {\n logger.debug({ err }, 'Failed to read Claude Code credentials from file');\n return null;\n }\n}\n","import {\n OAuthMetadataSchema,\n OAuthProtectedResourceMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n} from '@modelcontextprotocol/sdk/shared/auth.js';\nimport { z } from 'zod';\n\nexport const AuthorizationServerMetadataSchema = z.union([\n OAuthMetadataSchema,\n OpenIdProviderDiscoveryMetadataSchema,\n]);\n\nexport const DiscoveryStateSchema = z\n .object({\n authorizationServerUrl: z.string(),\n authorizationServerMetadata: AuthorizationServerMetadataSchema.optional(),\n resourceMetadata: OAuthProtectedResourceMetadataSchema.optional(),\n resourceMetadataUrl: z.string().optional(),\n })\n .passthrough();\n","import type { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';\nimport type { MCPServerInfo } from '@shipyard/session';\n\nimport type { MCPTokenStore } from './token-store.js';\n\nexport const ENV_VAR_PATTERN = /\\$\\{([^}]+)\\}/g;\nexport const VAULT_REF_PATTERN = /^\\$\\{vault:([^}]+)\\}$/;\n\nexport function interpolateEnvVars(headers: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n result[key] = value.replace(\n ENV_VAR_PATTERN,\n (_, varName: string) => process.env[varName] ?? ''\n );\n }\n return result;\n}\n\n/**\n * Pass through user-configured static headers only. OAuth tokens for HTTP\n * servers are managed by the SDK subprocess via CC's Keychain — injecting\n * Shipyard's own tokens here would conflict with the SDK's OAuth transport.\n * Token store injection is only used for stdio env vars (resolveStdioEnv).\n */\nexport function resolveHeaders(server: MCPServerInfo): Record<string, string> | undefined {\n if (!server.headers) return undefined;\n return interpolateEnvVars(server.headers);\n}\n\nexport function resolveStdioEnv(\n env: Record<string, string> | undefined,\n store?: MCPTokenStore,\n log?: (entry: { event: string; [key: string]: unknown }) => void\n): Record<string, string> | undefined {\n if (!env) return undefined;\n if (!store) return env;\n\n const result: Record<string, string> = {};\n let changed = false;\n\n for (const [key, value] of Object.entries(env)) {\n const match = VAULT_REF_PATTERN.exec(value);\n if (match) {\n const vaultKey = match[1] ?? '';\n const token = store.getTokenSync(vaultKey);\n if (token && !store.isExpired(token)) {\n result[key] = token.accessToken;\n changed = true;\n log?.({\n event: 'vault_ref_resolved',\n envKey: key,\n vaultKey,\n tokenType: token.tokenType,\n hasExpiry: token.expiresAt !== undefined,\n });\n continue;\n }\n log?.({\n event: 'vault_ref_unresolved',\n envKey: key,\n vaultKey,\n tokenFound: token !== null,\n tokenExpired: token ? store.isExpired(token) : false,\n });\n }\n result[key] = value;\n }\n\n return changed ? result : env;\n}\n\nexport function resolveEnabledMcpServers(\n overrides: Array<{ name: string; enabled: boolean }> | undefined,\n available: MCPServerInfo[] | undefined,\n mcpTokenStore?: MCPTokenStore\n): Record<string, McpServerConfig> | undefined {\n if (!overrides || !available) return undefined;\n const enabledNames = new Set(overrides.filter((o) => o.enabled).map((o) => o.name));\n if (enabledNames.size === 0) return {};\n const result: Record<string, McpServerConfig> = {};\n for (const server of available) {\n if (!enabledNames.has(server.name)) continue;\n if (server.type === 'http' && server.url) {\n result[server.name] = {\n type: 'http',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.type === 'sse' && server.url) {\n result[server.name] = {\n type: 'sse',\n url: server.url,\n headers: resolveHeaders(server),\n };\n } else if (server.command) {\n result[server.name] = {\n command: server.command,\n args: server.args ?? undefined,\n env: resolveStdioEnv(server.env, mcpTokenStore),\n };\n }\n }\n return result;\n}\n","import { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport { logger } from '../logger.js';\n\nconst execFile = promisify(execFileCb);\n\nexport type AnthropicCredentialsLog = (entry: {\n event: string;\n reason?: string;\n [key: string]: unknown;\n}) => void;\n\nconst defaultLog: AnthropicCredentialsLog = (entry) => logger.debug(entry, entry.event);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nasync function readKeychainEntry(service: string): Promise<string | null> {\n if (process.platform !== 'darwin') return null;\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-w'], {\n timeout: 5_000,\n });\n const value = stdout.trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the OAuth access token from the `Claude Code-credentials` Keychain entry,\n * stored as `claudeAiOauth.accessToken` by `claude setup-token` / `claude auth login`.\n *\n * This is the canonical token for talking to claude.ai-hosted services\n * (e.g. `https://api.anthropic.com/v1/mcp_servers`) on behalf of an OAuth-authed user.\n */\nexport async function readKeychainOAuthToken(\n log: AnthropicCredentialsLog = defaultLog\n): Promise<string | null> {\n if (process.platform !== 'darwin') {\n log({ event: 'anthropic_keychain_read_skipped', reason: 'non_darwin' });\n return null;\n }\n const raw = await readKeychainEntry('Claude Code-credentials');\n if (!raw) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_credentials_entry' });\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n log({ event: 'anthropic_keychain_oauth_corrupt', reason: 'json_parse_error' });\n return null;\n }\n if (!isRecord(parsed)) {\n log({ event: 'anthropic_keychain_oauth_corrupt', reason: 'json_parse_error' });\n return null;\n }\n const oauth = parsed.claudeAiOauth;\n if (!isRecord(oauth)) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_oauth_field' });\n return null;\n }\n const token = oauth.accessToken;\n if (typeof token !== 'string' || token.length === 0) {\n log({ event: 'anthropic_keychain_oauth_missing', reason: 'no_oauth_field' });\n return null;\n }\n return token;\n}\n\n/**\n * Read the managed API key from the `Claude Code` Keychain entry —\n * the value stored by `claude login` for direct Messages API calls\n * (paired with the billing header prefix).\n */\nexport async function readKeychainManagedKey(): Promise<string | null> {\n const value = await readKeychainEntry('Claude Code');\n if (!value || !value.startsWith('sk-')) return null;\n return value;\n}\n","import type { MCPServerInfo } from '@shipyard/session';\n\nimport { readKeychainOAuthToken } from '../auth/anthropic-credentials.js';\nimport { logger } from '../logger.js';\n\ntype Log = (entry: { event: string; [key: string]: unknown }) => void;\n\n/**\n * Minimal shape we expect from the Anthropic MCP servers API response.\n * Hand-rolled instead of Zod to keep best-effort parsing lightweight.\n */\nfunction parseClaudeAiServers(data: unknown): Array<{ name: string; url: string }> {\n if (typeof data !== 'object' || data === null) return [];\n // eslint-disable-next-line no-restricted-syntax -- JSON.parse returns unknown; manually validated below\n const obj = data as Record<string, unknown>;\n if (!Array.isArray(obj.data)) return [];\n const results: Array<{ name: string; url: string }> = [];\n for (const entry of obj.data) {\n if (typeof entry !== 'object' || entry === null) continue;\n // eslint-disable-next-line no-restricted-syntax -- narrowing unknown entry fields\n const rec = entry as Record<string, unknown>;\n if (typeof rec.name === 'string' && typeof rec.url === 'string') {\n results.push({ name: rec.name, url: rec.url });\n }\n }\n return results;\n}\n\n/**\n * Fetch MCP server integrations from the user's Claude.ai account.\n * Best-effort: returns [] on any failure.\n */\nexport async function fetchClaudeAiIntegrations(log?: Log): Promise<MCPServerInfo[]> {\n const debugLog: Log = log ?? ((entry) => logger.debug(entry, entry.event));\n try {\n const token = await readKeychainOAuthToken(debugLog);\n if (!token) {\n debugLog({ event: 'claudeai_integrations_skipped', reason: 'no_oauth_token' });\n return [];\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10_000);\n\n try {\n const response = await fetch('https://api.anthropic.com/v1/mcp_servers?limit=1000', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n debugLog({\n event: 'claudeai_integrations_fetch_failed',\n status: response.status,\n });\n return [];\n }\n\n const body: unknown = await response.json();\n const servers = parseClaudeAiServers(body);\n\n return servers.map((s) => ({\n name: s.name,\n type: 'http' as const,\n url: s.url,\n enabled: true,\n source: 'claudeai' as const,\n authStatus: 'unknown' as const,\n }));\n } finally {\n clearTimeout(timeout);\n }\n } catch (err: unknown) {\n debugLog({\n event: 'claudeai_integrations_fetch_error',\n error: err instanceof Error ? err.message : String(err),\n });\n return [];\n }\n}\n"],"mappings":"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","names":["readFile","homedir","join","execFileCb","promisify","execFile","promisify","execFileCb","readFile","join","homedir","logger"]}
package/dist/index.js CHANGED
@@ -101,7 +101,7 @@ async function loadAuthFromConfig(env) {
101
101
  async function handleSubcommand() {
102
102
  const subcommand = process.argv[2];
103
103
  if (subcommand === "login") {
104
- const { loginCommand } = await import("./login-G6OYPHJL.js");
104
+ const { loginCommand } = await import("./login-WYC24WPA.js");
105
105
  const hasCheck = process.argv.includes("--check");
106
106
  await loginCommand({ check: hasCheck });
107
107
  return true;
@@ -112,7 +112,7 @@ async function handleSubcommand() {
112
112
  return true;
113
113
  }
114
114
  if (subcommand === "start") {
115
- const { startCommand } = await import("./start-D4NXCWOI.js");
115
+ const { startCommand } = await import("./start-VNYBVNI7.js");
116
116
  await startCommand();
117
117
  return true;
118
118
  }
@@ -129,7 +129,7 @@ async function main() {
129
129
  const args = parseCliArgs();
130
130
  if (args.serve) {
131
131
  await loadAuthFromConfig(env);
132
- const { serve } = await import("./serve-Q2AWZ4S2.js");
132
+ const { serve } = await import("./serve-IWCW4D3N.js");
133
133
  return serve({ isDev: env.SHIPYARD_DEV });
134
134
  }
135
135
  logger.error("Use `shipyard start` to run the daemon. Use --help for usage.");
@@ -3,8 +3,8 @@ import {
3
3
  ensureAuthenticated,
4
4
  getSignalingUrl,
5
5
  loginCommand
6
- } from "./chunk-5B5MBXHW.js";
7
- import "./chunk-EE3Z2DJM.js";
6
+ } from "./chunk-52T3USYD.js";
7
+ import "./chunk-DBAPXL44.js";
8
8
  import "./chunk-EHQITHQX.js";
9
9
  import "./chunk-IISLTKYY.js";
10
10
  import "./chunk-2UN5AR7V.js";
@@ -17,4 +17,4 @@ export {
17
17
  getSignalingUrl,
18
18
  loginCommand
19
19
  };
20
- //# sourceMappingURL=login-G6OYPHJL.js.map
20
+ //# sourceMappingURL=login-WYC24WPA.js.map
@@ -2,8 +2,9 @@
2
2
  import {
3
3
  detectMCPServers,
4
4
  redactArgs,
5
- redactEnv
6
- } from "./chunk-GIFN3IPT.js";
5
+ redactEnv,
6
+ shouldFetchClaudeAiIntegrations
7
+ } from "./chunk-L2WQMPWS.js";
7
8
  import "./chunk-2UN5AR7V.js";
8
9
  import "./chunk-PI77CUEP.js";
9
10
  import "./chunk-VPMN47TL.js";
@@ -11,6 +12,7 @@ import "./chunk-2H7UOFLK.js";
11
12
  export {
12
13
  detectMCPServers,
13
14
  redactArgs,
14
- redactEnv
15
+ redactEnv,
16
+ shouldFetchClaudeAiIntegrations
15
17
  };
16
- //# sourceMappingURL=mcp-servers-FZV2P2ZO.js.map
18
+ //# sourceMappingURL=mcp-servers-MXS5VAWI.js.map