(options: {
verbose?: boolean
})
| 125 | * ``` |
| 126 | */ |
| 127 | export async function loadMCPConfig(options: { |
| 128 | verbose?: boolean |
| 129 | }): Promise<LoadedMCPConfig> { |
| 130 | const { verbose = false } = options |
| 131 | |
| 132 | const mergedConfig: LoadedMCPConfig = { |
| 133 | mcpServers: {}, |
| 134 | _sourceFilePath: '', |
| 135 | } |
| 136 | |
| 137 | const mcpConfigDirs = getDefaultMcpConfigDirs() |
| 138 | |
| 139 | for (const dir of mcpConfigDirs) { |
| 140 | const configPath = path.join(dir, MCP_CONFIG_FILE_NAME) |
| 141 | |
| 142 | try { |
| 143 | // Check if file exists asynchronously |
| 144 | try { |
| 145 | await fsPromises.access(configPath) |
| 146 | } catch { |
| 147 | continue |
| 148 | } |
| 149 | |
| 150 | const content = await fsPromises.readFile(configPath, 'utf8') |
| 151 | const rawConfig = JSON.parse(content) |
| 152 | const parseResult = mcpFileSchema.safeParse(rawConfig) |
| 153 | |
| 154 | if (!parseResult.success) { |
| 155 | if (verbose) { |
| 156 | console.error( |
| 157 | `Invalid mcp.json at ${configPath}: ${parseResult.error.message}`, |
| 158 | ) |
| 159 | } |
| 160 | continue |
| 161 | } |
| 162 | |
| 163 | const parsedConfig = parseResult.data |
| 164 | |
| 165 | // Resolve environment variable references |
| 166 | try { |
| 167 | resolveMcpConfigEnv(parsedConfig) |
| 168 | } catch (error) { |
| 169 | if (verbose) { |
| 170 | console.error(error instanceof Error ? error.message : String(error)) |
| 171 | } |
| 172 | continue |
| 173 | } |
| 174 | |
| 175 | // Merge MCP servers (later directories override earlier ones) |
| 176 | for (const [serverName, serverConfig] of Object.entries( |
| 177 | parsedConfig.mcpServers, |
| 178 | )) { |
| 179 | mergedConfig.mcpServers[serverName] = serverConfig |
| 180 | } |
| 181 | |
| 182 | // Track the last successfully loaded config path |
| 183 | if (Object.keys(parsedConfig.mcpServers).length > 0) { |
| 184 | mergedConfig._sourceFilePath = configPath |
no test coverage detected