Skip to content

Commit 9d81080

Browse files
authored
mcp: respect cwd in configs, allow proper variable resolution (microsoft#249999)
Keeps it as a string, rather than forcing it into a URI, so that variables can be resolved.
1 parent 3087dba commit 9d81080

File tree

8 files changed

+24
-16
lines changed

8 files changed

+24
-16
lines changed

src/vs/platform/mcp/common/mcpPlatformTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface IMcpConfigurationStdio extends IMcpConfigurationCommon {
3232
args?: readonly string[];
3333
env?: Record<string, string | number | null>;
3434
envFile?: string;
35+
cwd?: string;
3536
}
3637

3738
export interface IMcpConfigurationHTTP extends IMcpConfigurationCommon {

src/vs/workbench/api/common/extHostTypeConverters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3406,7 +3406,7 @@ export namespace McpServerDefinition {
34063406
}
34073407
: {
34083408
type: McpServerTransportType.Stdio,
3409-
cwd: item.cwd,
3409+
cwd: item.cwd?.fsPath,
34103410
args: item.args,
34113411
command: item.command,
34123412
env: item.env,

src/vs/workbench/api/node/extHostMcpNode.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
77
import { readFile } from 'fs/promises';
88
import { homedir } from 'os';
99
import { parseEnvFile } from '../../../base/common/envfile.js';
10-
import { URI } from '../../../base/common/uri.js';
10+
import { untildify } from '../../../base/common/labels.js';
1111
import { StreamSplitter } from '../../../base/node/nodeStreams.js';
12+
import { findExecutable } from '../../../base/node/processes.js';
1213
import { LogLevel } from '../../../platform/log/common/log.js';
1314
import { McpConnectionState, McpServerLaunch, McpServerTransportStdio, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js';
1415
import { ExtHostMcpService } from '../common/extHostMcp.js';
1516
import { IExtHostRpcService } from '../common/extHostRpcService.js';
16-
import { findExecutable } from '../../../base/node/processes.js';
17-
import { untildify } from '../../../base/common/labels.js';
17+
import * as path from '../../../base/common/path.js';
1818

1919
export class NodeExtHostMpcService extends ExtHostMcpService {
2020
constructor(
@@ -83,7 +83,11 @@ export class NodeExtHostMpcService extends ExtHostMcpService {
8383
let child: ChildProcessWithoutNullStreams;
8484
try {
8585
const home = homedir();
86-
const cwd = launch.cwd ? URI.revive(launch.cwd).fsPath : home;
86+
let cwd = launch.cwd ? untildify(launch.cwd, home) : home;
87+
if (!path.isAbsolute(cwd)) {
88+
cwd = path.join(home, cwd);
89+
}
90+
8791
const { executable, args, shell } = await formatSubprocessArguments(
8892
untildify(launch.command, home),
8993
launch.args.map(a => untildify(a, home)),
@@ -94,7 +98,7 @@ export class NodeExtHostMpcService extends ExtHostMcpService {
9498
this._proxy.$onDidPublishLog(id, LogLevel.Debug, `Server command line: ${executable} ${args.join(' ')}`);
9599
child = spawn(executable, args, {
96100
stdio: 'pipe',
97-
cwd: launch.cwd ? URI.revive(launch.cwd).fsPath : homedir(),
101+
cwd,
98102
signal: abortCtrl.signal,
99103
env,
100104
shell,

src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { equals as arrayEquals } from '../../../../../base/common/arrays.js';
77
import { Throttler } from '../../../../../base/common/async.js';
88
import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
99
import { autorunDelta, ISettableObservable, observableValue } from '../../../../../base/common/observable.js';
10+
import { isAbsolute, join } from '../../../../../base/common/path.js';
1011
import { URI } from '../../../../../base/common/uri.js';
1112
import { Location } from '../../../../../editor/common/languages.js';
1213
import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';
@@ -128,7 +129,11 @@ export class ConfigMcpDiscovery extends Disposable implements IMcpDiscovery {
128129
command: value.command,
129130
env: value.env || {},
130131
envFile: value.envFile,
131-
cwd: src.path.workspaceFolder?.uri,
132+
cwd: value.cwd
133+
// if the cwd is defined in a workspace folder but not absolute (and not
134+
// a variable or tilde-expansion) then resolve it in the workspace folder
135+
? (!isAbsolute(value.cwd) && !value.cwd.startsWith('~') && !value.cwd.startsWith('${') && src.path.workspaceFolder ? join(src.path.workspaceFolder.uri.fsPath, value.cwd) : value.cwd)
136+
: src.path.workspaceFolder?.uri.fsPath,
132137
},
133138
roots: src.path.workspaceFolder ? [src.path.workspaceFolder.uri] : [],
134139
variableReplacement: {

src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function claudeConfigToServerDefinition(idPrefix: string, contents: VSBuf
5151
command: server.command,
5252
env: server.env || {},
5353
envFile: undefined,
54-
cwd,
54+
cwd: cwd?.fsPath,
5555
}
5656
};
5757
});

src/vs/workbench/contrib/mcp/common/mcpTypes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ export const enum McpServerTransportType {
365365
*/
366366
export interface McpServerTransportStdio {
367367
readonly type: McpServerTransportType.Stdio;
368-
readonly cwd: URI | undefined;
368+
readonly cwd: string | undefined;
369369
readonly command: string;
370370
readonly args: readonly string[];
371371
readonly env: Record<string, string | number | null>;
@@ -390,7 +390,7 @@ export type McpServerLaunch =
390390
export namespace McpServerLaunch {
391391
export type Serialized =
392392
| { type: McpServerTransportType.HTTP; uri: UriComponents; headers: [string, string][] }
393-
| { type: McpServerTransportType.Stdio; cwd: UriComponents | undefined; command: string; args: readonly string[]; env: Record<string, string | number | null>; envFile: string | undefined };
393+
| { type: McpServerTransportType.Stdio; cwd: string | undefined; command: string; args: readonly string[]; env: Record<string, string | number | null>; envFile: string | undefined };
394394

395395
export function toSerialized(launch: McpServerLaunch): McpServerLaunch.Serialized {
396396
return launch;
@@ -403,7 +403,7 @@ export namespace McpServerLaunch {
403403
case McpServerTransportType.Stdio:
404404
return {
405405
type: launch.type,
406-
cwd: launch.cwd ? URI.revive(launch.cwd) : undefined,
406+
cwd: launch.cwd,
407407
command: launch.command,
408408
args: launch.args,
409409
env: launch.env,

src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import * as sinon from 'sinon';
88
import { timeout } from '../../../../../base/common/async.js';
99
import { ISettableObservable, observableValue } from '../../../../../base/common/observable.js';
1010
import { upcast } from '../../../../../base/common/types.js';
11-
import { URI } from '../../../../../base/common/uri.js';
1211
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
1312
import { ConfigurationTarget, IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
1413
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
@@ -169,7 +168,7 @@ suite('Workbench - MCP - Registry', () => {
169168
args: [],
170169
env: {},
171170
envFile: undefined,
172-
cwd: URI.parse('file:///test')
171+
cwd: '/test',
173172
}
174173
};
175174
});
@@ -223,7 +222,7 @@ suite('Workbench - MCP - Registry', () => {
223222
PATH: '${input:testInteractive}'
224223
},
225224
envFile: undefined,
226-
cwd: URI.parse('file:///test')
225+
cwd: '/test',
227226
},
228227
variableReplacement: {
229228
section: 'mcp',

src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { timeout } from '../../../../../base/common/async.js';
88
import { Disposable } from '../../../../../base/common/lifecycle.js';
99
import { autorun, observableValue } from '../../../../../base/common/observable.js';
1010
import { upcast } from '../../../../../base/common/types.js';
11-
import { URI } from '../../../../../base/common/uri.js';
1211
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
1312
import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';
1413
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
@@ -100,7 +99,7 @@ suite('Workbench - MCP - ServerConnection', () => {
10099
args: [],
101100
env: {},
102101
envFile: undefined,
103-
cwd: URI.parse('file:///test')
102+
cwd: '/test'
104103
}
105104
};
106105
});

0 commit comments

Comments
 (0)