Skip to content

Commit db82f11

Browse files
fix(exec): show MCP spawn args in trust prompt (#1165)
1 parent 6d07b36 commit db82f11

2 files changed

Lines changed: 66 additions & 6 deletions

File tree

‎src/exec/runner.ts‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { type Config } from "../config/index.js";
88
import {
99
shellTimeoutFromSettings,
1010
toolWatchdogFromSettings,
11+
type MCPServerConfig,
1112
} from "../config/settings.js";
1213
import {
1314
codexProfileFromProviderName,
@@ -162,6 +163,17 @@ const logger = getLogger([LOG_NAMESPACE_ROOT, "exec"]);
162163

163164
const SELECTED_PROVIDER_FAILURE = "SelectedProviderFailure";
164165

166+
export function formatExecMcpTrustQuestion(server: MCPServerConfig): string {
167+
return (
168+
`Trust local MCP server "${server.name}" for this project?` +
169+
(server.command !== undefined
170+
? `\nCommand: ${server.command}${(server.args ?? []).length > 0 ? ` ${(server.args ?? []).join(" ")}` : ""}`
171+
: server.url !== undefined
172+
? `\nURL: ${server.url}`
173+
: "")
174+
);
175+
}
176+
165177
export async function refreshSelectedProviderCredential<T>(
166178
refresh: () => Promise<T>,
167179
): Promise<T> {
@@ -706,12 +718,7 @@ export async function runExec(config: Config): Promise<ExecResult> {
706718
requestMcpTrust: async (server) => {
707719
if (!interactive) return false;
708720
const result = await promptOperator(
709-
`Trust local MCP server "${server.name}" for this project?` +
710-
(server.command !== undefined
711-
? `\nCommand: ${server.command}`
712-
: server.url !== undefined
713-
? `\nURL: ${server.url}`
714-
: ""),
721+
formatExecMcpTrustQuestion(server),
715722
["Trust and connect", "Deny"],
716723
true,
717724
);

‎tests/unit/exec/runner.test.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
createExecToolCallGate,
1313
createExecToolPromoter,
1414
execUserFailureMessage,
15+
formatExecMcpTrustQuestion,
1516
refreshSelectedProviderCredential,
1617
resolveExecDirectorOverlay,
1718
runExec,
@@ -70,6 +71,58 @@ function bareConfig(task: string): Config {
7071
} as unknown as Config;
7172
}
7273

74+
describe("exec MCP trust prompt", () => {
75+
test("shows a stdio command with literal space-joined args", () => {
76+
const question = formatExecMcpTrustQuestion({
77+
name: "filesystem",
78+
command: "npx",
79+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp/work"],
80+
});
81+
82+
expect(question).toBe(
83+
'Trust local MCP server "filesystem" for this project?\nCommand: npx -y @modelcontextprotocol/server-filesystem /tmp/work',
84+
);
85+
});
86+
87+
test("shows only the stdio command when args are omitted", () => {
88+
expect(formatExecMcpTrustQuestion({ name: "local", command: "node" })).toBe(
89+
'Trust local MCP server "local" for this project?\nCommand: node',
90+
);
91+
});
92+
93+
test("shows only the stdio command when args are empty", () => {
94+
expect(
95+
formatExecMcpTrustQuestion({ name: "local", command: "node", args: [] }),
96+
).toBe('Trust local MCP server "local" for this project?\nCommand: node');
97+
});
98+
99+
test("shows an HTTP server URL", () => {
100+
expect(
101+
formatExecMcpTrustQuestion({
102+
name: "remote",
103+
type: "http",
104+
url: "https://mcp.example.test/api",
105+
}),
106+
).toBe(
107+
'Trust local MCP server "remote" for this project?\nURL: https://mcp.example.test/api',
108+
);
109+
});
110+
111+
test("does not show environment secrets", () => {
112+
const question = formatExecMcpTrustQuestion({
113+
name: "private",
114+
command: "private-server",
115+
env: { API_TOKEN: "super-secret" },
116+
});
117+
118+
expect(question).toBe(
119+
'Trust local MCP server "private" for this project?\nCommand: private-server',
120+
);
121+
expect(question).not.toContain("API_TOKEN");
122+
expect(question).not.toContain("super-secret");
123+
});
124+
});
125+
73126
describe("formatCaughtError", () => {
74127
test("prefers Error.message and stringifies other values", () => {
75128
expect(formatCaughtError(new Error("disk full"))).toBe("disk full");

0 commit comments

Comments
 (0)