A tool-approval dialog can stop a requested tool call. It does not, by itself, define what the program hosting that tool can do.
With a local stdio MCP server, the client launches a program and communicates with it through standard input and output. That program can run startup code before receiving a tool request, within the permissions and restrictions of its execution environment. Reviewing the integration therefore involves two decisions: which code may start, and which requests may be sent to it. stdio transport · MCP trust model
This is ordinary process execution. MCP’s security policy identifies command execution through a configured stdio server as intended behavior. The practical question is whether the server gets only the access its job requires.
What happened before the first message
We used the official TypeScript SDK package, @modelcontextprotocol/sdk 1.32.2, with Node.js 24.22.1 on Ubuntu 24.04.3 LTS, Linux x64, as UID 0 in a container. Our deliberately constructed server registered a harmless test tool. Its startup code read two synthetic files, checked for a named dummy environment variable, and wrote a local observation record before beginning to listen for MCP messages.
The client started StdioClientTransport. It sent zero MCP messages, including zero tools/call requests. No model chose an action and no tool handler ran.
Both files were created by the test and were readable by the process. One was inside the configured working directory; the other was in a sibling directory. The isolated runner’s environment contained only PATH and ORBITALWAKE_MCP_DEMO_VALUE, whose value was synthetic. We ran two launch configurations:
| Launch configuration | File inside cwd | File outside cwd | Dummy variable in server environment | Client messages sent |
|---|---|---|---|---|
| SDK default environment | Read successfully | Read successfully | Absent | 0 |
Dummy variable explicitly supplied through env | Read successfully | Read successfully | Present and matched | 0 |
The child’s working directory matched the configured directory in both cases. Setting cwd had not turned that directory into a filesystem boundary. The sibling file remained readable under the process’s existing permissions.
These are launch-only tests of our own server. They did not complete an MCP handshake or negotiate a protocol version, and they do not establish compatibility with the current 2026-07-28 protocol revision. They also did not test Claude Code, Copilot, Cursor, an approval dialog, a network restriction, or a sandbox escape. No additional OS sandbox was configured around the child, and no access-denial claim is made from these deliberately readable fixtures. A planned non-root comparison was blocked by the execution environment before running; ordinary-user execution was not measured.
Reproduce the fixture
Download these files into a fresh directory, using the local filenames shown:
- Experiment source →
mcp-stdio-startup-check.mjs - Package manifest →
package.json - Dependency lockfile →
package-lock.json - Our recorded result, for comparison
Read the source before running it. On Linux with the tested Node version, install the locked dependencies and run the fixture:
npm ci --ignore-scripts --no-audit --no-fund --registry=https://registry.npmjs.org/
node mcp-stdio-startup-check.mjs
Dependency installation contacts the package registry. The experiment code makes no network requests; it creates and removes its own temporary fixtures and launches local Node processes. The JSON output records each observation, message counts, and an overall passed result. A process-launch error or failed assertion is an incomplete or failed check, not evidence that an MCP permission control worked. The lockfile records the dependency resolution we tested; review advisories before reusing it later.
The environment depends on the launcher
In this SDK version, the transport combines getDefaultEnvironment() with any explicitly supplied env entries. The default function selects particular variables from the parent environment. Our dummy variable was outside that selection, so it did not arrive in the default case. The explicit entry made it available in the second case. Tested SDK implementation
Two details matter when reading a configuration. The selected defaults can still be inherited when env is provided; an env object is not necessarily the entire resulting environment. And this SDK’s behavior does not tell you what a different language SDK or an editor’s own launcher passes. Inspect the actual implementation and effective child environment using dummy values.
Environment filtering and file isolation solve different problems. Removing a token from the child process’s environment does not remove a copy it can read from a configuration file. Conversely, restricting file reads does not revoke a credential deliberately supplied to the process. Review both routes. Credential isolation
Check what your sandbox actually covers
A setting named “sandbox” needs a scope. Check whether it wraps the MCP server process, which paths and connections it permits, and whether exceptions can move work outside that boundary.
Current client documentation illustrates why the distinction matters:
| Client feature | Documented coverage relevant to a local MCP server |
|---|---|
| Claude Code’s built-in shell sandbox | Wraps shell commands and their subprocesses. MCP servers run outside that particular boundary. |
| GitHub Copilot CLI, when local sandboxing is enabled | Local MCP server processes are sandboxed by default within that mode; settings and managed policies affect the effective behavior. |
These are documentation comparisons checked on 28 October 2026, not tests of either product in this experiment. An outer container or VM around an entire client can add another boundary. Copilot’s conditional default does not mean local sandboxing is always enabled. Inspect your client’s current settings and the process it actually starts. Claude Code coverage · Copilot CLI coverage
For the separate question of Claude Code’s built-in file tools and Bash commands, see our tested comparison of permissions and sandboxing.
Directory hints and read-only labels
MCP roots tell a cooperating server which files or directories are relevant. They do not make the operating system deny access outside those locations. Roots are also deprecated in the 2026-07-28 protocol revision; that revision advises new implementations against adopting them. Treat an existing roots setting as context to understand, rather than proof of confinement. Roots specification
Likewise, readOnlyHint: true is a server-supplied description of a tool’s behavior. It does not remove the server process’s write permissions. The schema explicitly says annotations are hints, can misdescribe behavior, and should not drive tool-use decisions when supplied by untrusted servers. Tool annotations
Server-side path checks and accurate annotations can still help a correctly implemented integration behave as intended. If the concern is that the server’s own code may be malicious or compromised, enforce the required restrictions outside that code.
What changes with a remote HTTP server
A direct connection to an MCP server hosted elsewhere does not start that server as a local stdio subprocess. The review shifts toward the destination, the data sent, the account and permissions authorized, and how the client handles the returned content. A local bridge that connects onward to a remote service still introduces local executable code to review. Local and remote deployment considerations
OAuth authorization governs access to a protected HTTP service. It does not certify the server operator or establish that every tool description or response is trustworthy. Check the account, resource and requested scopes before granting access. This follows from the role of authorization in MCP, rather than from an OAuth test in this article. Authorization scope
HTTP is a transport, not a statement about where a server runs. An HTTP MCP endpoint can also run on your own machine. For that case, the current specification requires Origin validation and recommends binding to localhost and implementing authentication. A localhost address alone is not authentication. We did not test HTTP endpoints or DNS-rebinding defenses here. Streamable HTTP security requirements
Before enabling the next server
Start with the actual launch configuration. If it contains a command and arguments, identify the executable, package, publisher, version, and any wrapper. If it points directly to a remote endpoint, verify who operates that endpoint. Package-version pinning makes changes easier to manage; it does not establish that the pinned code is trustworthy, and a top-level package pin does not necessarily lock every dependency. Keep an update and advisory-review process. Installation and version checks
Then record the access the integration needs:
| Question | Evidence to look for |
|---|---|
| Which files must it read or change? | The process’s effective filesystem policy, mounted directories, and separately configured server-side checks. |
| Which credentials must it receive? | The actual environment passed by the launcher and any accessible credential files; a token scoped to this server’s task. |
| Which network destinations must it reach? | An enforced network policy where available, with the permitted destinations tested. |
| Which actions should require approval? | The client’s tool rules and approval behavior, reviewed separately from process startup. |
| How will access end? | A way to stop the process, disable automatic reconnection, remove its configuration, and revoke credentials it no longer needs. |
Use dummy data to check the intended boundary. A permitted read should work while a read you intend to block should fail for the expected reason. Do not treat a broken interpreter, missing file, or a model’s refusal as evidence of enforced isolation.
The useful outcome is a concrete configuration you can explain: this server runs this version, can reach these files and services, receives this credential, and is constrained by this mechanism. Keep that record alongside the MCP configuration so the next update can be reviewed against the same expectations.
Sources & scope
Official MCP TypeScript SDK 1.32.2 startup tests on Ubuntu as UID 0, using synthetic files and a dummy environment variable. No MCP messages, model inference, or client approval UI were exercised. Non-root execution, OS sandbox enforcement, HTTP authorization, and network restrictions were not tested. Client-product comparisons are documentation reviews.
- MCP 2026-07-28: stdio transport
- MCP: Security policy and intended trust model
- MCP: Local server security
- Anthropic: Configure the sandboxed Bash tool
- GitHub: Understanding filesystem policies for local sandboxing in Copilot CLI
- MCP 2026-07-28: Roots
- MCP 2026-07-28: Tool annotations schema
- MCP 2026-07-28: Streamable HTTP transport
- MCP 2026-07-28: Authorization
- MCP TypeScript SDK: tested stdio implementation at commit 3b73021
Publishing details & corrections
Prepared with AI assistance. Sources, any tests performed, and their limitations are described in the article.
No material corrections recorded for this version.