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
| Path | Method | Purpose |
|---|---|---|
/mcp | POST | Streamable HTTP MCP endpoint. Stateless; no session id. |
/mcp | GET, DELETE | Returns 405; not supported in stateless mode. |
/healthz | GET | Cheap process liveness check. Does not touch upstream RPC. |
/readyz | GET | Cheap 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.
Recommended topology
Internet / client
-> reverse proxy / ingress / CDN
-> blurt-mcp-server HTTP process
-> Blurt blockchain RPC nodes, Nexus/Bridge API, price feedThe 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
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
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
| Method | Use when | Guide |
|---|---|---|
| PM2 | You 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=productionfor JSON logs.LOG_LEVEL=infounless actively debugging.- Explicit
BLURT_RPC_URLSfor maintained deployments; provide multiple nodes so the node checker can avoid unhealthy nodes. - Explicit
BLURT_PRICE_URLif 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.
/healthzand/readyzare 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.