Skip to content

Deployment guide

This guide covers deployment concerns that belong around the Blurt MCP server, not inside it. The application stays a small neutral MCP component: it exposes /mcp, /healthz, and /readyz; TLS, public rate limiting, bot protection, and global abuse controls should normally live in infrastructure.

HTTP surface

PathMethodPurpose
/mcpPOSTStreamable HTTP MCP endpoint. Stateless; no session id.
/mcpGET, DELETEReturns 405; not supported in stateless mode.
/healthzGETCheap process liveness check. Does not touch upstream RPC.
/readyzGETCheap readiness check with non-sensitive RPC configuration/readiness counters.

The app uses Express' JSON parser default request-body limit unless BLURT_HTTP_JSON_BODY_LIMIT is set. For public HTTP deployments, prefer enforcing body size in the reverse proxy or ingress layer where it can be monitored and changed without rebuilding the MCP server.

The public /mcp route intentionally does not maintain an AI-product Origin allow-list. This endpoint's security boundary is instead a public read-only, no-ambient-authority deployment model: no posting key on public HTTP, no cookies, no credentialed CORS, no user-specific private data, and TLS / host routing at the reverse proxy. This avoids turning interoperability with current and future MCP clients into an operator-maintained list of browser origins.

JSON-RPC batch arrays are rejected before the request reaches the MCP SDK transport because MCP 2025-06-18 removed batching support.

text
Internet / client
  -> reverse proxy / ingress / CDN
  -> blurt-mcp-server HTTP process
  -> Blurt blockchain RPC nodes, Nexus/Bridge API, price feed

The public hosted endpoint should stay anonymous and read-only. Do not configure BLURT_POSTING_KEY on a public HTTP deployment. HTTP signing is available only through the deliberately unsafe trusted-deployment override documented in write operations.

Caddy example

txt
mcp.example.org {
  encode zstd gzip

  @mcp method POST
  @mcp path /mcp
  reverse_proxy @mcp 127.0.0.1:3000

  @health path /healthz /readyz
  reverse_proxy @health 127.0.0.1:3000

  # Keep request bodies small; MCP JSON-RPC requests are tiny.
  request_body {
    max_size 256KB
  }

  # Caddy rate limits require a plugin/module. If unavailable, apply rate limits
  # at your CDN, load balancer, firewall, or hosting platform instead.
}

nginx example

nginx
limit_req_zone $binary_remote_addr zone=blurt_mcp:10m rate=60r/m;

server {
  listen 443 ssl http2;
  server_name mcp.example.org;

  client_max_body_size 256k;

  location = /mcp {
    limit_req zone=blurt_mcp burst=30 nodelay;
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location = /healthz {
    proxy_pass http://127.0.0.1:3000;
  }

  location = /readyz {
    proxy_pass http://127.0.0.1:3000;
  }
}

Tune the rate-limit numbers for your own traffic and abuse profile. The example is intentionally coarse; production limits should be validated against real usage.

Deployment methods

MethodUse whenGuide
PM2You deploy from a normal Node.js host or VPS and want process restart, boot integration, logs and pm2 monit.PM2 deployment

The deployment method should not change the server architecture: keep runtime configuration in BLURT_ENV_FILE, keep TLS/rate limits/body limits at the infrastructure boundary, and keep posting keys out of public HTTP deployments.

Production environment checklist

  • NODE_ENV=production for JSON logs.
  • LOG_LEVEL=info unless actively debugging.
  • Explicit BLURT_RPC_URLS for maintained deployments; provide multiple nodes so the node checker can avoid unhealthy nodes.
  • Explicit BLURT_PRICE_URL if price-dependent tools should be available.
  • Optional transport/cache/body settings (BLURT_RPC_TIMEOUT_MS, BLURT_PRICE_CACHE_TTL_MS, BLURT_HTTP_JSON_BODY_LIMIT, etc.) are set by deployment configuration, not by source edits.
  • Reverse proxy terminates TLS and applies public rate limits.
  • /healthz and /readyz are routed to monitoring.
  • No posting key on public HTTP deployments.
  • Secrets for stdio/private deployments are kept outside the repository.

What is intentionally deferred

The app does not ship a full OpenTelemetry stack, built-in distributed rate limiter, WAF, dashboard, alerting setup, or generic response cache. Those belong to deployment infrastructure until concrete hosting requirements justify pulling a small part into the server itself.