Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion packages/mcp-server/src/code-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ const localDenoHandler = async ({
const packageNodeModulesPath = path.resolve(packageRoot, 'node_modules');

// Check if deno is in PATH
const { execSync } = await import('node:child_process');
const { execSync, spawn } = await import('node:child_process');
try {
execSync('command -v deno', { stdio: 'ignore' });
denoPath = 'deno';
Expand All @@ -159,6 +159,16 @@ const localDenoHandler = async ({
}
}

// Deno >= 2 gates unix sockets behind --allow-net=unix:<path>, while Deno 1.x panics on a
// `unix:` token in --allow-net — so the socket grant below must be gated on the major version.
let denoMajor = 2;
try {
const versionOutput = execSync(`"${denoPath}" --version`, { encoding: 'utf8' });
denoMajor = Number(/deno (\d+)\./.exec(versionOutput)?.[1] ?? denoMajor);
} catch {
// Version detection failed; assume a current (2.x) Deno.
}

const allowReadPaths = [
'code-tool-worker.mjs',
`${workerPath.replace(/([\/\\]node_modules)[\/\\].+$/, '$1')}/`,
Expand Down Expand Up @@ -190,6 +200,29 @@ const localDenoHandler = async ({
'--allow-env',
],
printOutput: true,
// deno-http-worker grants its internal unix socket via --allow-read/--allow-write (the
// Deno 1.x permission model), but Deno >= 2 requires --allow-net=unix:<path> or the worker
// dies on startup ("Deno exited before being ready"). The socket path is generated inside
// newDenoHTTPWorker, so pluck it out of the --allow-write flag the library injects and graft
// it onto the --allow-net scope (Deno rejects repeated --allow-net flags, hence the comma).
...(denoMajor >= 2 && {
spawnFunc: (
command: string,
spawnArgs: string[],
options: import('node:child_process').SpawnOptions,
) => {
const socketPath = spawnArgs
.find((flag) => flag.startsWith('--allow-write='))
?.slice('--allow-write='.length)
.split(',')
.find((segment) => segment.endsWith('-deno-http.sock'));
const patchedArgs =
socketPath ?
spawnArgs.map((flag) => (flag.startsWith('--allow-net=') ? `${flag},unix:${socketPath}` : flag))
: spawnArgs;
return spawn(command, patchedArgs, options);
},
}),
spawnOptions: {
cwd: path.dirname(workerPath),
// Merge any upstream client envs into the Deno subprocess environment,
Expand Down
Loading