From 170128b7fbf21b8afe679aded2cb8712ecf1d51d Mon Sep 17 00:00:00 2001 From: haosenwang1018 Date: Sun, 22 Mar 2026 04:27:54 +0000 Subject: [PATCH 1/2] fix: publish linux arm64 release binary --- .github/workflows/release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7bdc987..07eb62b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,6 +51,9 @@ jobs: - name: Build Linux x64 run: bun build --compile --minify --target=bun-linux-x64 src/index.ts --outfile dist/mcp-cli-linux-x64 + + - name: Build Linux ARM64 + run: bun build --compile --minify --target=bun-linux-arm64 src/index.ts --outfile dist/mcp-cli-linux-arm64 - name: Build macOS x64 run: bun build --compile --minify --target=bun-darwin-x64 src/index.ts --outfile dist/mcp-cli-darwin-x64 @@ -95,6 +98,7 @@ jobs: generate_release_notes: true files: | dist/mcp-cli-linux-x64 + dist/mcp-cli-linux-arm64 dist/mcp-cli-darwin-x64 dist/mcp-cli-darwin-arm64 dist/checksums.txt From bb332993b8b85fca4e24a8e9e77bf854796bf1d3 Mon Sep 17 00:00:00 2001 From: haosenwang1018 Date: Sun, 22 Mar 2026 04:53:20 +0000 Subject: [PATCH 2/2] fix: scope env substitution to targeted servers --- src/commands/call.ts | 22 +++++++++++++++------- src/commands/info.ts | 9 ++++++--- src/config.ts | 36 +++++++++++++++++++++++++++++++----- tests/config.test.ts | 29 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/commands/call.ts b/src/commands/call.ts index 4579847..6040c52 100644 --- a/src/commands/call.ts +++ b/src/commands/call.ts @@ -110,13 +110,6 @@ async function parseArgs( export async function callCommand(options: CallOptions): Promise { let config: McpServersConfig; - try { - config = await loadConfig(options.configPath); - } catch (error) { - console.error((error as Error).message); - process.exit(ErrorCode.CLIENT_ERROR); - } - let serverName: string; let toolName: string; @@ -129,6 +122,21 @@ export async function callCommand(options: CallOptions): Promise { process.exit(ErrorCode.CLIENT_ERROR); } + try { + config = await loadConfig({ + explicitPath: options.configPath, + serverNames: [serverName], + }); + } catch (error) { + console.error((error as Error).message); + process.exit(ErrorCode.CLIENT_ERROR); + } + + try { + console.error((error as Error).message); + process.exit(ErrorCode.CLIENT_ERROR); + } + let serverConfig: ServerConfig; try { serverConfig = getServerConfig(config, serverName); diff --git a/src/commands/info.ts b/src/commands/info.ts index e88bea6..8e8c208 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -40,15 +40,18 @@ function parseTarget(target: string): { server: string; tool?: string } { export async function infoCommand(options: InfoOptions): Promise { let config: McpServersConfig; + const { server: serverName, tool: toolName } = parseTarget(options.target); + try { - config = await loadConfig(options.configPath); + config = await loadConfig({ + explicitPath: options.configPath, + serverNames: [serverName], + }); } catch (error) { console.error((error as Error).message); process.exit(ErrorCode.CLIENT_ERROR); } - const { server: serverName, tool: toolName } = parseTarget(options.target); - let serverConfig: ServerConfig; try { serverConfig = getServerConfig(config, serverName); diff --git a/src/config.ts b/src/config.ts index 99a4e25..2f8ffa0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -56,6 +56,11 @@ export interface McpServersConfig { mcpServers: Record; } +export interface LoadConfigOptions { + explicitPath?: string; + serverNames?: string[]; +} + // ============================================================================ // Tool Filtering // ============================================================================ @@ -397,13 +402,16 @@ function getDefaultConfigPaths(): string[] { * Load and parse MCP servers configuration */ export async function loadConfig( - explicitPath?: string, + input?: string | LoadConfigOptions, ): Promise { + const options: LoadConfigOptions = + typeof input === 'string' ? { explicitPath: input } : input ?? {}; + let configPath: string | undefined; // Check explicit path from argument or environment - if (explicitPath) { - configPath = resolve(explicitPath); + if (options.explicitPath) { + configPath = resolve(options.explicitPath); } else if (process.env.MCP_CONFIG_PATH) { configPath = resolve(process.env.MCP_CONFIG_PATH); } @@ -496,8 +504,26 @@ export async function loadConfig( } } - // Substitute environment variables - config = substituteEnvVarsInObject(config); + // Substitute environment variables only for the relevant servers. + // This avoids warning about unrelated missing variables when a command + // targets a single server (for example: `mcp-cli info chrome-devtools`). + if (options.serverNames && options.serverNames.length > 0) { + const targetServers = new Set(options.serverNames); + const substitutedServers: Record = {}; + + for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) { + substitutedServers[serverName] = targetServers.has(serverName) + ? substituteEnvVarsInObject(serverConfig) + : serverConfig; + } + + config = { + ...config, + mcpServers: substitutedServers, + }; + } else { + config = substituteEnvVarsInObject(config); + } return config; } diff --git a/tests/config.test.ts b/tests/config.test.ts index a48602c..3253595 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -128,6 +128,35 @@ describe('config', () => { await expect(loadConfig(configPath)).rejects.toThrow('MISSING_ENV_VAR'); }); + test('only substitutes env vars for targeted servers', async () => { + delete process.env.MCP_STRICT_ENV; + + const configPath = join(tempDir, 'targeted_env.json'); + await writeFile( + configPath, + JSON.stringify({ + mcpServers: { + chrome: { + command: 'echo', + args: ['ok'], + }, + context7: { + command: 'echo', + env: { API_KEY: '${MISSING_CONTEXT7_API_KEY}' }, + }, + }, + }) + ); + + const config = await loadConfig({ + explicitPath: configPath, + serverNames: ['chrome'], + }); + + expect((config.mcpServers.chrome as any).command).toBe('echo'); + expect((config.mcpServers.context7 as any).env.API_KEY).toBe('${MISSING_CONTEXT7_API_KEY}'); + }); + test('throws error on empty server config', async () => { const configPath = join(tempDir, 'empty_server.json'); await writeFile(