Skip to content

Community-maintained FDE reference. Not an official Vercel or Anthropic project. About this project

Sidecar

Audience:engineerarchitectsecuritynon-technicalMCP spec 2026-07-28

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

Per-request isolationStreamable HTTPStreamable HTTP plusauthcreate, run, discardHostMCP Client AMCP Client BServer FunctionSidecar projectSandbox microVM
Per-request isolationStreamable HTTPStreamable HTTP plusauthcreate, run, discardHostMCP Client AMCP Client BServer FunctionSidecar projectSandbox microVM
Mermaid flowchartOpen in Mermaid Live Editor
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| VM

Protocol implications

  • In the Sandbox form, the sidecar is invisible to MCP. It is an implementation detail behind a normal tool: the declared inputSchema and 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 no initialize handshake 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-handler 2.1.1 on @modelcontextprotocol/server 2.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 SDK Client defaults 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 session timeout defaults 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 explicit subnets rules. To change the policy of a running sandbox call sandbox.update({ networkPolicy }); the older updateNetworkPolicy() is deprecated.
  • No ambient credentials. The sandbox does not inherit the function’s environment variables. Only the env you 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/sandbox v2 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 pass persistent: false to Sandbox.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 the subject claim of the form owner:<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 the x-vercel-protection-bypass header, 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 networkPolicy is "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 env parameter 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, a timeout sized to the tool’s real budget, and an explicit stop() on every exit path, including cancellation. See Deployment posture.
  • Pin what runs: pass image (the runtime option 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 exact Sandbox.create options, which a satisfies clause checks against the installed SDK’s types: the networkPolicy is the SDK’s object form { allow: [...] } with an explicit allowlist (everything not listed is denied), persistent: false, the image pinned to the tag vercel/sandbox/node:22, a timeout and resources budget, and no env key 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 in process.env and 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 to Sandbox.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

ProsCons
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.
  • 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