From 9fb2ced8efac891a073e86a88e550fd691220627 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 1 Aug 2026 13:22:19 +0000 Subject: [PATCH 1/2] fix(mcp): grant Deno --allow-net for the execute control socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The execute tool runs model-supplied code in a Deno subprocess, and @valtown/deno-http-worker talks to that subprocess over a Unix socket served with `Deno.serve({ path })`. Since Deno 2.9, binding a Unix socket requires net access in addition to read/write access, so the worker died at startup with: NotCapable: Requires net access to "unix:/.../-deno-http.sock", run again with the --allow-net flag which surfaced as "Deno exited before being ready" on every execute call. The socket path is generated inside the worker library and is not exposed to callers, so grant net access by patching the argv the library builds: find the socket path it passes to the bootstrap script and append `unix:` to the existing --allow-net allowlist. Deno's allowlist matches a Unix socket only by its full path, so this is as tight as the permission can be scoped, and the sandbox is otherwise unchanged — code running in the worker still reaches only the API base URL host. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01H7CWSE6BSsshnh7rZNgomB --- packages/mcp-server/src/code-tool.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/code-tool.ts b/packages/mcp-server/src/code-tool.ts index 88e2dd3..78034d3 100644 --- a/packages/mcp-server/src/code-tool.ts +++ b/packages/mcp-server/src/code-tool.ts @@ -122,6 +122,27 @@ export function codeTool({ return { metadata, tool, handler }; } +/** + * Grants the Deno subprocess net access to its own control socket, and nothing else. + * + * `@valtown/deno-http-worker` serves its control channel with `Deno.serve({ path })` over a Unix + * socket, and since Deno 2.9 binding one needs net access on top of read/write access: + * + * NotCapable: Requires net access to "unix:/.../-deno-http.sock" + * + * The library generates that path itself and never exposes it, so recover it from the argv the + * library built: it is the only bare (non-flag) argument ending in the socket suffix. Deno's + * allowlist matches a Unix socket by full path only — a directory prefix is not accepted — so this + * is as tightly as the permission can be scoped. + */ +function withControlSocketNetAccess(args: string[]): string[] { + const socketPath = args.find((arg) => !arg.startsWith('-') && arg.endsWith('-deno-http.sock')); + if (socketPath === undefined) { + return args; + } + return args.map((arg) => (arg.startsWith('--allow-net=') ? `${arg},unix:${socketPath}` : arg)); +} + const localDenoHandler = async ({ reqContext, args, @@ -146,7 +167,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'; @@ -195,6 +216,9 @@ const localDenoHandler = async ({ '--allow-env', ], printOutput: true, + // The worker library appends its own permission flags for the control socket, so patch the + // final argv rather than the flags above. + spawnFunc: (command, args, options) => spawn(command, withControlSocketNetAccess(args), options), spawnOptions: { cwd: path.dirname(workerPath), // Merge any upstream client envs into the Deno subprocess environment, From 8fe641f5b626aadbf82873a3abac3d88593535e3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 1 Aug 2026 13:22:19 +0000 Subject: [PATCH 2/2] fix(mcp): print the package version for --version yargs treats a single argument to .version() as the version string, so .version(true) made `mcp-server --version` print "true". Calling .version() with no arguments lets yargs read the version out of package.json. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01H7CWSE6BSsshnh7rZNgomB --- packages/mcp-server/src/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-server/src/options.ts b/packages/mcp-server/src/options.ts index c1b638c..c3eca8b 100644 --- a/packages/mcp-server/src/options.ts +++ b/packages/mcp-server/src/options.ts @@ -108,7 +108,7 @@ export function parseCLIOptions(): CLIOptions { description: 'What transport to use; stdio for local servers or http for remote servers', }) .env('MCP_SERVER') - .version(true) + .version() .help(); const argv = opts.parseSync();