fix(core): show clear error when MCP server cwd does not exist (#3192)

* fix(core): show clear error when MCP server cwd does not exist

Validate that the configured cwd directory exists before spawning the
MCP server process. Previously, a non-existent cwd caused Node.js to
emit "spawn <cmd> ENOENT" — indistinguishable from the command binary
being missing. Now throws a descriptive error naming the server and
the missing path.

Fixes #3163

* test(core): add test for MCP stdio transport without cwd
This commit is contained in:
tanzhenxin 2026-04-13 18:02:14 +08:00 committed by GitHub
parent 9a889dc614
commit 9cdf7bd7c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 0 deletions

View file

@ -23,6 +23,11 @@ import {
} from './mcp-client.js';
import type { ToolRegistry } from './tool-registry.js';
const mockExistsSync = vi.hoisted(() => vi.fn(() => true));
vi.mock('node:fs', () => ({
existsSync: mockExistsSync,
}));
vi.mock('@modelcontextprotocol/sdk/client/stdio.js');
vi.mock('@modelcontextprotocol/sdk/client/index.js');
vi.mock('@google/genai');
@ -289,6 +294,46 @@ describe('mcp-client', () => {
});
});
it('should connect via command without cwd', async () => {
const mockedTransport = vi
.spyOn(SdkClientStdioLib, 'StdioClientTransport')
.mockReturnValue({} as SdkClientStdioLib.StdioClientTransport);
await createTransport(
'test-server',
{
command: 'test-command',
args: ['--foo', 'bar'],
},
false,
);
expect(mockedTransport).toHaveBeenCalledWith({
command: 'test-command',
args: ['--foo', 'bar'],
cwd: undefined,
env: expect.any(Object),
stderr: 'pipe',
});
});
it('should throw if cwd does not exist', async () => {
mockExistsSync.mockReturnValueOnce(false);
await expect(
createTransport(
'test-server',
{
command: 'test-command',
cwd: '/nonexistent/path',
},
false,
),
).rejects.toThrow(
"MCP server 'test-server': configured cwd does not exist: /nonexistent/path",
);
});
describe('useGoogleCredentialProvider', () => {
it('should use GoogleCredentialProvider when specified', async () => {
const transport = await createTransport(

View file

@ -34,6 +34,7 @@ import { SdkControlClientTransport } from './sdk-control-client-transport.js';
import type { FunctionDeclaration } from '@google/genai';
import { mcpToTool } from '@google/genai';
import { existsSync } from 'node:fs';
import { basename } from 'node:path';
import { pathToFileURL } from 'node:url';
import { MCPOAuthProvider } from '../mcp/oauth-provider.js';
@ -1402,6 +1403,12 @@ export async function createTransport(
}
if (mcpServerConfig.command) {
if (mcpServerConfig.cwd && !existsSync(mcpServerConfig.cwd)) {
throw new Error(
`MCP server '${mcpServerName}': configured cwd does not exist: ${mcpServerConfig.cwd}`,
);
}
const transport = new StdioClientTransport({
command: mcpServerConfig.command,
args: mcpServerConfig.args || [],