Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.7 kB · 138 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139import { getIsNonInteractiveSession } from '../../bootstrap/state.js'import { checkHasTrustDialogAccepted } from '../../utils/config.js'import { logAntError } from '../../utils/debug.js'import { errorMessage } from '../../utils/errors.js'import { execFileNoThrowWithCwd } from '../../utils/execFileNoThrow.js'import { logError, logMCPDebug, logMCPError } from '../../utils/log.js'import { jsonParse } from '../../utils/slowOperations.js'import { logEvent } from '../analytics/index.js'import type { McpHTTPServerConfig, McpSSEServerConfig, McpWebSocketServerConfig, ScopedMcpServerConfig,} from './types.js'
/** * Check if the MCP server config comes from project settings (projectSettings or localSettings) * This is important for security checks */function isMcpServerFromProjectOrLocalSettings( config: ScopedMcpServerConfig,): boolean { return config.scope === 'project' || config.scope === 'local'}
/** * Get dynamic headers for an MCP server using the headersHelper script * @param serverName The name of the MCP server * @param config The MCP server configuration * @returns Headers object or null if not configured or failed */export async function getMcpHeadersFromHelper( serverName: string, config: McpSSEServerConfig | McpHTTPServerConfig | McpWebSocketServerConfig,): Promise<Record<string, string> | null> { if (!config.headersHelper) { return null }
// Security check for project/local settings // Skip trust check in non-interactive mode (e.g., CI/CD, automation) if ( 'scope' in config && isMcpServerFromProjectOrLocalSettings(config as ScopedMcpServerConfig) && !getIsNonInteractiveSession() ) { // Check if trust has been established for this project const hasTrust = checkHasTrustDialogAccepted() if (!hasTrust) { const error = new Error( `Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`, ) logAntError('MCP headersHelper invoked before trust check', error) logEvent('tengu_mcp_headersHelper_missing_trust', {}) return null } }
try { logMCPDebug(serverName, 'Executing headersHelper to get dynamic headers') const execResult = await execFileNoThrowWithCwd(config.headersHelper, [], { shell: true, timeout: 10000, // Pass server context so one helper script can serve multiple MCP servers // (git credential-helper style). See deshaw/anthropic-issues#28. env: { ...process.env, CLAUDE_CODE_MCP_SERVER_NAME: serverName, CLAUDE_CODE_MCP_SERVER_URL: config.url, }, }) if (execResult.code !== 0 || !execResult.stdout) { throw new Error( `headersHelper for MCP server '${serverName}' did not return a valid value`, ) } const result = execResult.stdout.trim()
const headers = jsonParse(result) if ( typeof headers !== 'object' || headers === null || Array.isArray(headers) ) { throw new Error( `headersHelper for MCP server '${serverName}' must return a JSON object with string key-value pairs`, ) }
// Validate all values are strings for (const [key, value] of Object.entries(headers)) { if (typeof value !== 'string') { throw new Error( `headersHelper for MCP server '${serverName}' returned non-string value for key "${key}": ${typeof value}`, ) } }
logMCPDebug( serverName, `Successfully retrieved ${Object.keys(headers).length} headers from headersHelper`, ) return headers as Record<string, string> } catch (error) { logMCPError( serverName, `Error getting headers from headersHelper: ${errorMessage(error)}`, ) logError( new Error( `Error getting MCP headers from headersHelper for server '${serverName}': ${errorMessage(error)}`, ), ) // Return null instead of throwing to avoid blocking the connection return null }}
/** * Get combined headers for an MCP server (static + dynamic) * @param serverName The name of the MCP server * @param config The MCP server configuration * @returns Combined headers object */export async function getMcpServerHeaders( serverName: string, config: McpSSEServerConfig | McpHTTPServerConfig | McpWebSocketServerConfig,): Promise<Record<string, string>> { const staticHeaders = config.headers || {} const dynamicHeaders = (await getMcpHeadersFromHelper(serverName, config)) || {}
// Dynamic headers override static headers if both are present return { ...staticHeaders, ...dynamicHeaders, }}