Deployment guide — MCP servers

Generate a governed MCP server from an OpenAPI spec, then run it as a plain container on any platform. Config is all environment variables; health is GET /health.

Table of contents
  1. Two models — pick one
  2. Transports
  3. Step 1 — generate the server + deploy kit
  4. Step 2 — configure (environment only)
  5. Step 3 — deploy (same container, your platform)
    1. Local / on-prem (Docker)
    2. Google Cloud Run
    3. Fly.io
    4. Railway / Render / Heroku
    5. Kubernetes / ECS
    6. Air-gapped
    7. RunPod
  6. Step 4 — connect an agent
  7. Step 5 — verify
  8. Deploying Shield itself
  9. Operational notes
  10. Where to next

Two models — pick one

  • Shield-hosted (zero extra infra). Import the spec into Shield; agents connect to Shield’s MCP URL. Tools run inside Shield — nothing else to deploy. Best for a fast start.
  • Standalone generated server (this guide). Deploy the generated container in your environment; it calls your API and phones Shield for enforcement. Best for data-residency and isolation.

Transports

A generated server supports both, selected by MCP_TRANSPORT:

MCP_TRANSPORT Use How clients connect
stdio (default) Desktop (Claude Desktop, Cursor) on the same machine client launches it as a subprocess
http Anything remote / cloud client uses the server’s https://…/mcp URL

For any deployment below, you want MCP_TRANSPORT=http (the Dockerfile sets it).


Step 1 — generate the server + deploy kit

SHIELD=https://your-shield ; KEY='X-API-Key: your-tenant-key'

curl -s -X POST "$SHIELD/v1/openapi/generate" -H "$KEY" -H 'Content-Type: application/json' -d '{
  "language": "python",        # or "typescript"
  "style": "typed",
  "base_url": "https://api.yourcompany.com",
  "deploy": true,              # also emit Dockerfile + manifests
  "spec": { ...your OpenAPI spec... }
}' | python3 -c "import sys,json,os; \
    [ (os.makedirs(os.path.dirname(n) or '.', exist_ok=True), open(n,'w').write(c)) \
      for n,c in json.load(sys.stdin)['files'].items() ]; print('wrote files')"

You get the server (server.py / src/index.ts) plus: Dockerfile, docker-compose.yml, fly.toml, Procfile, cloudrun.service.yaml, k8s.yaml, DEPLOY.md, and requirements.txt / package.json.


Step 2 — configure (environment only)

Variable Purpose
API_BASE_URL the upstream API base URL
MCP_TRANSPORT http for deploys (Dockerfile default)
PORT / HOST listen port / interface (default 8080 / 0.0.0.0)
SHIELD_URL Shield data-plane endpoint (the full app, e.g. a RunPod URL) — omit to run ungoverned. Not the admin/portal URL.
SHIELD_API_KEY, SHIELD_AGENT_KEY, SHIELD_USER_ROLE Shield enforcement context
SHIELD_AUTH_TOKEN bearer for a proxied data plane (e.g. RunPod) — sent as Authorization: Bearer. Required if the endpoint is proxied, else enforcement calls 401 and the server fails open.
upstream auth per the spec’s security schemes (e.g. API_TOKEN, API_KEY_*, OAUTH_CLIENT_ID/SECRET)
API_MAX_PAGES >1 enables auto-pagination

Always inject secrets via the platform’s secret store — never bake them into the image.


Step 3 — deploy (same container, your platform)

Local / on-prem (Docker)

docker compose up --build          # http://localhost:8080/mcp  (health: /health)
# or:
docker build -t my-mcp . && docker run -p 8080:8080 -e API_BASE_URL=https://api.yourcompany.com my-mcp

Google Cloud Run

gcloud run deploy my-mcp --source . --port 8080 \
  --set-env-vars MCP_TRANSPORT=http,API_BASE_URL=https://api.yourcompany.com
# (or push an image and `kubectl apply -f cloudrun.service.yaml`)

Fly.io

fly launch --copy-config --now     # uses the generated fly.toml
fly secrets set API_BASE_URL=https://api.yourcompany.com SHIELD_API_KEY=...

Railway / Render / Heroku

Point the platform at the repo — it uses the generated Procfile. Set the env vars in the dashboard. ($PORT is provided by the platform.)

Kubernetes / ECS

# edit the image in k8s.yaml, then:
kubectl apply -f k8s.yaml          # Deployment (2 replicas) + Service, /health probes
kubectl set env deploy/<name> API_BASE_URL=https://api.yourcompany.com

Scale with an HPA on CPU; the server is stateless.

Air-gapped

docker save my-mcp | gzip > my-mcp.tar.gz     # move into the enclave, then `docker load`

Run Shield self-hosted in the same enclave; leave fetch_external off. No outbound calls except to your own API.

RunPod

Works as a normal container — no GPU needed for MCP servers (GPU is only for Shield’s LLM content guardrails).


Step 4 — connect an agent

Remote (deployed, HTTP):

{ "mcpServers": { "my-mcp": {
    "url": "https://my-mcp.example.com/mcp",
    "headers": { "Authorization": "Bearer <client-token-if-any>" }
}}}

Local (stdio, no deploy):

{ "mcpServers": { "my-mcp": {
    "command": "python", "args": ["server.py"],
    "env": { "API_BASE_URL": "https://api.yourcompany.com",
             "SHIELD_URL": "https://your-shield-data-plane", "SHIELD_API_KEY": "...",
             "SHIELD_AUTH_TOKEN": "...proxy bearer if any...",
             "SHIELD_AGENT_KEY": "my-agent", "SHIELD_USER_ROLE": "reader" }
}}}

Step 5 — verify

curl -s https://my-mcp.example.com/health        # {"status":"ok"}
npx @modelcontextprotocol/inspector              # connect via the /mcp URL, list + call tools

With SHIELD_URL set, a call the agent’s role isn’t allowed returns “Blocked by Shield: …” instead of executing.


Deploying Shield itself

Shield (the governance plane) deploys separately and already supports many targets — Docker, Docker Compose, the lightweight admin-only image, RunPod, and on-prem. See the Quickstart and the on-premises deployment guide. For production, point Shield at Redis (REDIS_URL) so tenant/agent state persists across restarts.


Operational notes

  • Stateless servers — scale horizontally; no sticky sessions needed for the HTTP transport’s stateless mode.
  • Health checksGET /health (wired into the compose/k8s manifests).
  • Governance is optional per deploy — unset SHIELD_URL to run a plain MCP server; set it to enforce. The server fails open if Shield is unreachable.
  • Logs — the server logs each request; route them to your platform’s log sink and Shield’s SIEM feed for the audit trail.

Where to next