Changed
TAILSCALE_MCP_SANDBOX=1grants the network asapi.tailscale.com:443instead of the bare host. oam 0.18.0, the floor, is the first release whosefetchhonours a port-scoped--allow-netentry, and every request this server makes is HTTPS, so nothing it does changes; anhttp://URL or a redirect to another port on that host is now refused as well.TAILSCALE_MCP_RUNTIME=oamnames the remedy for what it found when no usable oam turns up:oam self-updatefor an oam older than the floor, a check of the binary for one that would not run or report a version, a fix for anOAM_BINthat does not exist, and installing oam only when none was found at all. It used to say "Install or update from https://oamjs.org" in every case, and on linux-arm64, which oam publishes no build for, it now says so instead of sending you to a download that does not exist.- The launcher searches
OAM_INSTALL_DIRfirst when that is set. It is where oam's installer andoam self-updateput the binary, so an oam installed to a custom directory and left offPATHused to go unfound. - An API request that runs into fetch's own connect, response-header or body timeout is now reported as
<METHOD> request timed out while connecting/waiting for the response headers/reading the response body, read from the error'scause.code(UND_ERR_CONNECT_TIMEOUTand the rest), instead of as a genericrequest failed: fetch failed (...). npm run build:binary:oamverifies a cross-build carrier against the oam release's signedRELEASE-MANIFEST--ssh-keygen -Y verifyagainst oam's release keys, vendored inscripts/oam-release-keys/-- and takes the checksum from the manifest instead of from the unsignedSHA256SUMSbeside the binary. It fails closed: nossh-keygenable to verify, a missing manifest, a bad signature or a tag outside the key's range aborts the build. AnOAM_VERSIONolder than v0.18.0, released before oam signed its releases, has no manifest: itsSHA256SUMSis used only when it hashes to the digest pinned for that tag in the vendoredpresigning-sums, as oam's installers do, and a pre-signing tag not pinned there is refused. A checksum file that lists an asset twice is refused too. Build tooling only.release.shchecks the oam floor before releasing:scripts/check-oam-floor.mjs(alsonpm run check:oam-floor) fails when the README, the launcher or its tests quote a floor other thanOAM_MIN, and when a newer oam has been published (TAILSCALE_MCP_ALLOW_STALE_OAM=1releases on the old floor deliberately; no network is not a failure). The drift half runs in the test suite too. Ported from aws-mcp. Release tooling only.
Fixed
- Under
TAILSCALE_MCP_SANDBOX=1thetailscaleCLI child now gets the environment a Windows program needs to start. The CLI is spawned without anenvoption, so it inherits the server's environment, which under the sandbox's--allow-envlist holds only the granted variables -- and oam 0.18.0 takes even the variables libuv adds to every Windows child (SYSTEMROOT,TEMP,USERPROFILE, ...) from that filtered environment. Measured on oam 0.18.0 on Windows: the child sawCOMSPEC,NODE_OPTIONS,PATH,PATHEXT,PROCESSOR_ARCHITECTURE,PROMPTandTAILSCALE_API_KEY, and noSYSTEMROOT, without which a program cannot start Winsock. The grant now addsAPPDATA,HOME,HOMEDRIVE,HOMEPATH,LOCALAPPDATA,SYSTEMDRIVE,SYSTEMROOT,TEMP,TMP,USERPROFILEandWINDIRfor the child. On Windows each of these is also granted in the spelling the environment actually uses: oam matches a grant exactly, case included, and an MCP client started from Explorer passes onPath,SystemRoot,SystemDriveandwindir-- measured on oam 0.18.0, a grant speltPATH,SYSTEMROOT,...alone let neither through, to the server or the child (a Git Bash shell upper-cases them, which hides this). The grant also addsWSL_DISTRO_NAME, which the server reads to recognise WSL when the CLI is missing (its fallback, reading/proc/version, is denied by the sandbox).tailscale_local_statusanswered under the sandbox both before and after on the Windows box this was measured on (Tailscale 1.102.4, which reaches tailscaled over a named pipe), so what this fixes is the environment, not a failure observed in that one tool. A new test,src/oam-sandbox.integration.test.ts, runs the launcher's real grant on a real oam 0.18.0 or newer when one is found (it skips otherwise) and asserts what a child receives; it fails against the old grant. - A Node handoff from an oam host strips
--permissionand--allow-*from theNODE_OPTIONSit passes on. oam hands its permission flags to children through that variable, and Node refuses--allow-netand--allow-envthere with exit 9, so a server handed to Node by an oam that carried them in its ownNODE_OPTIONSdied before running. Flags oam was given on its own command line are appended at the spawn by oam itself and cannot be removed this way; the README now says that under the sandboxTAILSCALE_BINARYmust be the native CLI, not a Node-based wrapper, for the same reason.
Security
- The bundled MCP SDK moves from 1.30.0 to 1.32.1 (GHSA-6qxp-vccf-f47h, high: the SDK's OAuth client could send credentials to an authorization server chosen by the MCP server).
build.mjsbundles the SDK intodist/index.js, so this ships in the package, and the dependency floor is now^1.32.1. Itsfast-uri, also bundled through ajv, moves from 3.1.7 to 3.1.8 (GHSA-hrr3-gc8f-f4qj). Development-scope only, since the bundle never imports express:proxy-addr2.0.8 (GHSA-jqcg-44mw-7w3h) andip-address10.7.3 (GHSA-j6r3-76f7-8jcv, GHSA-h3mg-xc3c-68pw). Theoverridesfloors forfast-uriandip-addressmove to the patched versions,^3.1.8and^10.7.3.npm auditreports 0 vulnerabilities.