Community-maintained FDE reference. Not an official Vercel or Anthropic project. About this project
Sidecar
Summary
A sidecar is the isolation shape: the risky part of an integration runs in its own runtime next to, not inside, the server that uses it, with its own dependencies, its own credentials (usually none), and its own blast radius. The classic pattern assumes a container you can place beside another container. Vercel has no such placement: a Vercel Function is not a pod, and there is no second container to attach. On Vercel the pattern reshapes into two forms: a Sandbox microVM the function creates per request for untrusted work, or a separate project behind Deployment Protection for a long-lived service sidecar. Be clear-eyed about this page: it describes a reshaping, not a translation.
Problem addressed
The naive approach runs the risky work, model-generated code, a heavyweight vendor SDK, a crash-prone document parser, inside the same function invocation that serves MCP. That collapses several trust boundaries at once: whatever executes in the invocation can read every environment variable the project mounts, reach every network destination the function can reach, and hang or crash the invocation that carried it. Serverless makes the failure quieter, not smaller: there is no resident process to watch die, just an invocation that timed out while holding all of your credentials.
The sidecar restores the boundary by moving the risky work into a runtime that starts with nothing: no credentials, no filesystem you care about, no network beyond what you explicitly allow, and only the inputs the server chooses to pass in.
When to use
- A tool executes untrusted or model-generated code, or parses hostile input formats.
- The integration pulls in heavy or risky dependencies (native binaries, large vendor SDKs) you do not want in the server’s bundle or memory space.
- The work may misbehave, spin, exhaust memory, or attempt exfiltration, and you need that failure contained and killable.
- The integration is owned by another team, ships on its own cadence, or holds a credential that must not share a process with the rest of your surface: that is the service-sidecar form.
When not to use
- The integration is dependency-light and trusted at the same level as the server. Inline it as a plain adapter; the isolation hop buys nothing.
- The latency budget cannot absorb microVM creation or an extra authenticated HTTPS hop on every call.
- What you actually need is one surface over many backends. That is a facade, a composition problem rather than an isolation problem.
- The work is long-running rather than dangerous. Reach for async jobs; a sandbox does not extend
maxDuration.
Architecture / flow diagram
Diagram source (Mermaid)
flowchart TB
Host[Host] --> C1[MCP Client A]
Host --> C2[MCP Client B]
C1 -->|Streamable HTTP| Fn[Server Function]
C2 -->|Streamable HTTP plus auth| Side[Sidecar project]
subgraph Iso[Per-request isolation]
VM[Sandbox microVM]
end
Fn -->|create, run, discard| VMProtocol implications
- In the Sandbox form, the sidecar is invisible to MCP. It is an implementation detail behind a normal tool: the declared
inputSchemaand the returned content are the whole contract, and isolation happens entirely server-side. No capability negotiation changes. - In the service form, the sidecar is a full MCP server: its own identity and capabilities answered through
server/discover, its own discovery lists, its own credentials, reached over the Streamable HTTP transport. Under MCP 2026-07-28 there is noinitializehandshake or protocol session to manage per sidecar; every request carries what the exchange needs in_meta(SEP-2575). The host composes it like any other server; that composition is the orchestrator pattern’s job. Wire status: the pinned stack (mcp-handler2.1.1 on@modelcontextprotocol/server2.0.0) serves the 2026-07-28 contract natively over Streamable HTTP and falls back to stateless 2025-11-25 Streamable HTTP for legacy clients; the SDKClientdefaults to that legacy handshake unless you opt in to modern version negotiation, so the examples’ in-memory test suites exercise only the legacy path. - Sandbox runs that outlive a quick call should emit progress notifications and honor cancellation: on cancel, stop the sandbox explicitly rather than letting it run unattended to its timeout.
- Budget the clock. Sandbox creation plus the run must fit inside the function’s
maxDuration(Hobby 300s; Pro and Enterprise 800s, extended 1800s in beta), and the sandbox’s own sessiontimeoutdefaults to 5 minutes. Work that cannot fit belongs in async jobs, not in a longer-held request.
Vercel mapping
Shape 1: Vercel Sandbox for per-request isolation. Sandbox is GA: each sandbox is a Firecracker microVM with its own filesystem and network, created from inside your function with the @vercel/sandbox SDK and authenticated by the project’s OIDC token, no static key required.
- Egress defaults to open; close it.
Sandbox.create({ networkPolicy })defaults to"allow-all". For untrusted work, pass"deny-all", or an{ allow: [...] }list of named hosts when the code legitimately needs specific destinations. Allowlist matching is SNI-based, so it governs TLS traffic; non-TLS destinations need explicitsubnetsrules. To change the policy of a running sandbox callsandbox.update({ networkPolicy }); the olderupdateNetworkPolicy()is deprecated. - No ambient credentials. The sandbox does not inherit the function’s environment variables. Only the
envyou pass explicitly exists inside the microVM. That is the point of the pattern: pass the per-call minimum, which is usually nothing. - Opt out of persistence. Vercel Sandbox is persistent by default (
@vercel/sandboxv2 and later): when a sandbox stops, its filesystem is snapshotted and the snapshot is billed as storage until it expires (30 days by default), which leaves a resumable copy of whatever the untrusted code did. For isolation duty passpersistent: falsetoSandbox.create(), then create, run, read the output, stop. A discarded sandbox is a cleaned-up crime scene; a snapshot is evidence you now pay to keep.
Shape 2: a separate project as the service sidecar. When the integration is long-lived, team-owned, or credential-bearing, deploy it as its own Vercel project: its own environment variables, deploys, logs, and rollbacks. Then make it callable only by trusted sources:
- Turn on Deployment Protection so none of its URLs, preview deployments included, are public.
- Admit the host by verified identity, not by network position: either verify the caller’s Vercel-issued OIDC token in the sidecar (check
issuer,audience, and thesubjectclaim of the formowner:<team>:project:<project>:environment:<env>, the flow Vercel documents as “Connect to your own API”), or use a Protection Bypass for Automation secret sent as thex-vercel-protection-bypassheader, held only by the host. - There is no private network between projects without Secure Compute (Enterprise-only). Project-to-project traffic rides public HTTPS, so authentication is the boundary. There is no security group to hide behind.
What the reshaping costs, stated plainly: the pod sidecar gave you a shared lifecycle and a loopback interface. The Sandbox form gives you stronger isolation than the original (hardware virtualization, a default-deniable network) but only for the span of an invocation; the service form gives you the lifecycle independence, but its “next to” is an authenticated HTTPS hop, not a shared host. Pick the form per tool, not per repository.
Security considerations
- The default
networkPolicyis"allow-all": an untrusted program in a fresh sandbox can reach the entire internet unless you say otherwise. Set"deny-all"unless the tool needs egress, and allowlist named hosts when it does; anything looser is an exfiltration channel. See Trust boundaries. - Never forward the function’s own environment into the sandbox. The
envparameter is an explicit allowlist of values; treat every entry as a disclosure decision. See Authorization & scoping. - Sandbox output is model input. Cap its size, strip control characters, and treat it as untrusted before it enters a tool result; code you isolated for being untrustworthy does not become trustworthy by finishing. See Output trust.
- A service sidecar must reject unauthenticated requests before any MCP handling runs, and Deployment Protection must cover all of its deployments; an unprotected preview URL of the sidecar is a public bypass of everything above. See Deployment posture and Authentication.
- Bound the sandbox’s resources and lifetime explicitly:
persistent: false,resources.vcpus, atimeoutsized to the tool’s real budget, and an explicitstop()on every exit path, including cancellation. See Deployment posture. - Pin what runs: pass
image(theruntimeoption is deprecated) as a versioned Vercel Managed Image or a digest-pinned custom image from Vercel Container Registry, never a floating tag, and record which tools may create sandboxes at all in your server inventory. See Inventory & supply chain.
The microVM boundary is real; the default network policy is not. Isolation you did not configure is isolation you do not have.
Example implementation
examples/sandbox-isolation-server(in the repository) - a server whose tool runs untrusted work inside Vercel Sandbox. The tests stub the Sandbox client and assert the exactSandbox.createoptions, which asatisfiesclause checks against the installed SDK’s types: thenetworkPolicyis the SDK’s object form{ allow: [...] }with an explicit allowlist (everything not listed is denied),persistent: false, the image pinned to the tagvercel/sandbox/node:22, atimeoutandresourcesbudget, and noenvkey at all, so the function’s environment never reaches the sandbox. There is no server-held credential: the SDK uses the deployment’s OIDC token, and the tests plant a canary inprocess.envand assert it appears in neither the options nor the tool output. Sandbox stdout and stderr come back capped with an explicit[truncated N chars]marker and framed as untrusted data. Asserting the config is the honest offline test: the security property lives in what you pass toSandbox.create, so that is what the suite pins down.- The service-sidecar form has no dedicated example on purpose: any server example deployed to its own protected project is one.
examples/secure-tools-server(in the repository) is the natural candidate; its default-deny authorization and output minimization are exactly the discipline a credential-bearing sidecar needs.
Trade-offs
| Pros | Cons |
|---|---|
| Hardware-virtualized boundary around untrusted code, per request. | Sandbox creation and teardown add latency and cost to every isolated call. |
| No ambient credentials or network: both are explicit allowlists. | The secure configuration is opt-in; the defaults (allow-all egress) are not the pattern. |
| Service form keeps credentials, deploys, and ownership per project. | Service form adds an authenticated HTTPS hop and a second project to operate. |
| Faults are contained and killable; the server survives the sidecar. | Two shapes to choose between, and the choice is per tool, not global. |
Related patterns
- adapter - the trusted, dependency-light integration shape; what you inline when a sidecar is overkill.
- facade - one surface over many backends; reach for it when the problem is composition, not isolation.
- least-privilege - decides what, if anything, is passed into the sandbox or granted to the sidecar project.
- trust-boundaries - names the boundary this pattern rebuilds and what must not cross it.
- orchestrator - how a host composes a service sidecar alongside other servers.
Vercel deployment (Terraform)
An illustrative Vercel expression of this pattern lives in terraform/patterns/sidecar (in the repository): two projects (a host and a sidecar), the sidecar’s deployment-protection configuration, and the protection-bypass resources that express “only the host may call the sidecar”, built with the official vercel/vercel provider. It is tofu validate-checked, never applied in CI. See terraform/README.md (in the repository) for scope and caveats.
Bibliography
- Model Context Protocol Specification, Architecture overview, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/architecture
- Model Context Protocol Specification, Transports, version 2026-07-28 - https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
- Model Context Protocol Documentation, Security Best Practices - https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices
- Vercel Documentation, Vercel Sandbox - https://vercel.com/docs/sandbox
- Vercel Documentation, Sandbox JS SDK Reference (
persistent,image,update(), deprecations) - https://vercel.com/docs/sandbox/sdk-reference - Vercel Documentation, Persistent sandboxes - https://vercel.com/docs/sandbox/concepts/persistent-sandboxes
- Vercel Documentation, Sandbox images - https://vercel.com/docs/sandbox/concepts/images
- Vercel Documentation, Sandbox network firewall - https://vercel.com/docs/sandbox/concepts/firewall
- Vercel Documentation, Deployment Protection - https://vercel.com/docs/deployment-protection
- Vercel Documentation, Methods to bypass Deployment Protection - https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection
- Vercel Documentation, OIDC: Connect to your own API - https://vercel.com/docs/oidc/api
- OWASP Top 10 for Large Language Model Applications - https://owasp.org/www-project-top-10-for-large-language-model-applications/