Skip to content

HTTP Transport

Chris edited this page Jul 18, 2026 · 122 revisions

Enterprise HTTP Transport

Tools

Resources

Prompts
OAuth

Code Mode

Rate Limiting

Value Proposition Scale your AI integrations confidently with our enterprise-grade HTTP transport. Engineered for cloud-native and remote deployments, it features OAuth 2.0 / OIDC security, robust connection pooling, and multi-agent concurrency to manage intensive workloads.


Architect Your Deployment: Stdio vs. HTTP Transport

Note

--transport http and --transport sse are functionally identical in mysql-mcp. The sse alias is maintained for backwards compatibility with earlier MCP specifications.

Use HTTP Transport to unlock enterprise-scale capabilities when:

  • Deploying globally to remote or cloud environments
  • Facilitating concurrent multi-agent access to a centralized server instance
  • Enforcing strict enterprise security via native OAuth 2.0 / OIDC
  • Running the server as a standalone network service
  • Serverless/stateless deployments (--stateless)
  • Horizontally scalable, stateless execution

Use stdio Transport (default) when:

  • Running locally with Claude Desktop or Cursor IDE
  • Single-user development environment
  • Simplest setup with no network configuration needed

Tip

Most users should use stdio Transport. HTTP Transport is for advanced deployments.


Get Started Quickly

Important

You must set --allowed-io-roots to explicitly authorize filesystem access for Code Mode. It is strictly required for HTTP transport (will fail to start), but only emits a warning for stdio (filesystem tools will be blocked, but the server will start).

Local Installation

# Local installation
npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3000 --allowed-io-roots /data --mysql mysql://user:password@localhost:3306/database

Docker

# Run with port mapping (include Redis and rate limiting env vars for production/enterprise)
docker run -p 3000:3000 \
  -e MYSQL_HOST=host.docker.internal \
  -e MYSQL_USER=user \
  -e MYSQL_PASSWORD=password \
  -e MYSQL_DATABASE=database \
  -e REDIS_URL=redis://host.docker.internal:6379 \
  -e MCP_RATE_LIMIT_MAX=100 \
  -e CODEMODE_RATE_LIMIT_MAX=60 \
  writenotenow/mysql-mcp:latest \
  --transport http \
  --server-host 0.0.0.0 \
  --port 3000 \
  --allowed-io-roots /data

With Simple Bearer Auth

npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3000 --allowed-io-roots /data --auth-token my-secret --mysql mysql://user:password@localhost:3306/database

Stateless Mode

npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3000 --stateless --allowed-io-roots /data --mysql mysql://user:password@localhost:3306/database

Understand Transport Protocols

The unified HTTP transport layer concurrently serves both modern and legacy protocol endpoints on the same port. This allows both modern and legacy clients to connect simultaneously without requiring separate deployments.

  • /mcp (Streamable HTTP): The modern protocol uses a single, session-based endpoint for all communication.
  • /sse (Legacy SSE): The backward-compatible protocol establishes a long-lived Server-Sent Events connection.

Streamable HTTP (Recommended)

The modern protocol uses a single, session-based endpoint:

Method Endpoint Purpose
POST /mcp JSON-RPC requests (initialize, tools/list, etc.)
GET /mcp SSE stream for server notifications
DELETE /mcp Session termination
GET /metrics Prometheus metrics export (when enabled)

In stateless mode (--stateless), all continuous streaming endpoints (e.g. /sse, GET /mcp) are explicitly disabled to guarantee strict stateless execution via POST /mcp. GET /mcp returns 405 (Method Not Allowed). DELETE /mcp returns 204. /sse and /messages return 404. Each POST /mcp creates a fresh transport. It relies solely on synchronous request-response cycles.

Warning

Stateless Limitations: --stateless disables resource subscriptions and progress notifications. It entirely disables Server-Sent Events. Do not use this mode if your AI needs long-running task updates.

The Mcp-Session-Id header manages sessions. The server returns a session ID during initialize. Clients must include it in subsequent requests. (Note: Strict MCP clients might structurally require the Mcp-Session-Id header. The --stateless mode ignores it internally.)

Example — Initialize a session:

curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"<protocol-version>","capabilities":{},"clientInfo":{"name":"my-client","version":"<client-version>"}}}'

Example — List tools (with session):

curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: <session-id-from-initialize>" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

Important

Streamable HTTP requests must include Accept: application/json, text/event-stream.

Legacy SSE (Backward Compatibility)

The legacy protocol supports older MCP clients:

Method Endpoint Purpose
GET /sse Opens SSE stream, returns /messages?sessionId=<id> endpoint
POST /messages?sessionId=<id> Send JSON-RPC messages to the session

Example:

# Establish SSE connection (will stream events)
curl -N http://localhost:3000/sse
# Send a message (in another terminal, using the sessionId from the SSE stream)
curl -X POST "http://localhost:3000/messages?sessionId=<id>" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"<protocol-version>","capabilities":{},"clientInfo":{"name":"my-client","version":"<client-version>"}}}'

Review Security Headers

All HTTP responses include the following headers:

Header Value
X-Content-Type-Options nosniff
X-Frame-Options DENY
Cache-Control no-store, no-cache, must-revalidate
Content-Security-Policy default-src 'none'; frame-ancestors 'none'
Permissions-Policy camera=(), microphone=(), geolocation=()
Referrer-Policy no-referrer
Strict-Transport-Security Opt-in via the --enable-hsts CLI flag or MCP_ENABLE_HSTS environment variable

Configure Server Timeouts (Slowloris Protection)

Warning

Load Balancer Trap: Set your proxy's idle timeout lower than the server's keepAliveTimeout (MCP_KEEPALIVE_TIMEOUT=65000). Otherwise, the proxy might close active connections, which causes 502 Bad Gateway errors.

The HTTP server applies three timeout layers to prevent slow-connection DoS attacks:

Timeout Value Purpose
requestTimeout MCP_REQUEST_TIMEOUT Maximum time for the entire request lifecycle
keepAliveTimeout MCP_KEEPALIVE_TIMEOUT Idle time before closing keep-alive connections
headersTimeout MCP_HEADERS_TIMEOUT Maximum time to receive complete headers

Manage Session Timeouts

The HTTP transport enforces strict session lifecycle management to prevent memory leaks:

  • Idle TTL: Configurable via environment variables with sensible defaults. The server terminates idle sessions.
  • Absolute TTL: Configurable via environment variables with sensible defaults. Hard limit for any session duration.
  • Reaper Interval: Configurable via environment variables with sensible defaults. A background sweep cleans up orphaned sessions. In-flight requests protect active sessions from early termination.

Configure CORS

We hardcode CORS to allow all origins (*). This permissiveness defaults to internal routing for seamless orchestrator integration. Secure CORS at the reverse proxy in production. We do not yet support restricting origins via CLI flags.

Enable Trust Proxy

Enable trustProxy behind reverse proxies. This reads the client IP from X-Forwarded-For. Rate limiting and logging use the real IP.

Enforce Distributed Rate Limiting for Resiliency

Default per-IP request throttling prevents abuse. The Transport Rate limiter uses a sliding window with deterministic cleanup. Redis distributes rate limiting across deployments via REDIS_URL. An in-memory fallback exists for all rate limits.

  • /health bypass — The server processes health checks before rate limiting. This ensures monitoring probes always succeed.
  • Retry-After header — Rate-limited responses include a Retry-After header. It indicates the seconds remaining until the window resets.
  • Environment override — Set MCP_RATE_LIMIT_MAX to customize the HTTP request limit. Set CODEMODE_RATE_LIMIT_MAX to customize the Code Mode execution limit.

Enforce Body Size Limits

Two layers enforce request body size:

  1. Content-Length header check — fast rejection for well-behaved clients
  2. Streaming byte tracking — catches missing headers and chunked encoding

The default maximum body size is 1MB. Configure this via the MCP_MAX_BODY_SIZE environment variable.

Configure Your Server

Environment Variables

For production deployments, use a structured .env file following fleet groupings:

# Server
MYSQLMCP_PORT=3000
MCP_HOST=0.0.0.0
TRUST_PROXY=false
REDIS_URL=redis://localhost:6379
MCP_RATE_LIMIT_MAX=100
CODEMODE_RATE_LIMIT_MAX=60
# Database
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=app_user
MYSQL_PASSWORD=secure_password
MYSQL_DATABASE=production

Server Configurations

Note

This table highlights HTTP-relevant arguments and general server configurations. See Configuration for the complete list of CLI arguments.

Argument Environment Variable Default Description
--transport, -t - stdio Transport type (stdio, http, or sse)
--port, -p MYSQLMCP_PORT, PORT 3000 HTTP server port
--server-host MCP_HOST localhost Host to bind HTTP transport to (Alias: HOST)
--mysql, -m - - MySQL connection string
--auth-token MCP_AUTH_TOKEN - Simple bearer token for HTTP auth
--stateless - false Enable strictly stateless HTTP transport (disables sessions and SSE to support serverless scaling)
--trust-proxy TRUST_PROXY false Trust X-Forwarded-For for client IP
--enable-hsts MCP_ENABLE_HSTS false Enable HTTP Strict Transport Security
--metrics-export MCP_METRICS_EXPORT disabled Enable Prometheus metrics endpoint /metrics (requires a string provider, e.g., prometheus)
--allowed-io-roots ALLOWED_IO_ROOTS - Explicitly authorize filesystem boundaries (Required for HTTP Transport)
--oauth-enabled, -o OAUTH_ENABLED false Enable OAuth 2.0 / OIDC authentication (Enterprise Identity Providers like Okta, Auth0, etc.)
--oauth-issuer OAUTH_ISSUER - OAuth issuer URL
--oauth-audience OAUTH_AUDIENCE mysql-mcp-client OAuth audience
--oauth-jwks-uri OAUTH_JWKS_URI - JWKS URI (auto-discovered)
--oauth-clock-tolerance OAUTH_CLOCK_TOLERANCE 60 Clock tolerance in seconds
--audit-log AUDIT_LOG - Path to the audit log file (enables forensic logging)
--audit-backup AUDIT_BACKUP false Enable pre-mutation snapshots for DML changes
--audit-reads AUDIT_READS false Include read-scope tool calls in audit log
--audit-redact AUDIT_REDACT false Redact sensitive arguments in audit log
--audit-log-max-size AUDIT_LOG_MAX_SIZE - Max file size before rotation (bytes)
--audit-backup-data AUDIT_BACKUP_DATA false Include sample data in pre-mutation snaps
--audit-backup-max-size AUDIT_BACKUP_MAX_SIZE - Max table size in bytes for data capture
--pool-size MYSQL_POOL_SIZE 10 Maximum connection pool size
--pool-timeout MYSQL_POOL_TIMEOUT 10000 Connection pool timeout in ms
--pool-queue-limit MYSQL_POOL_QUEUE_LIMIT 0 Connection pool queue limit
[ENV Only] CODEMODE_MAX_RESULT_SIZE 102400 Max Code Mode result payload in bytes (default 100KB, up to 100MB limit)
[ENV Only] CODEMODE_ISOLATION isolate Sandbox mode (only native isolate is supported)
[ENV Only] CODEMODE_TIMEOUT_MS 30000 Code mode execution timeout in ms
[ENV Only] REDIS_URL - Redis connection URL (used for rate limiting)
[ENV Only] MCP_RATE_LIMIT_MAX 100 Custom HTTP request rate limit
[ENV Only] CODEMODE_RATE_LIMIT_MAX 60 Custom Code Mode execution rate limit
[ENV Only] MCP_MAX_BODY_SIZE 1048576 Maximum request body size in bytes
[ENV Only] MCP_REQUEST_TIMEOUT 120000 Global request timeout in ms
[ENV Only] MCP_HEADERS_TIMEOUT 66000 Global headers timeout in ms
[ENV Only] MCP_KEEPALIVE_TIMEOUT 65000 Keep-alive timeout in ms
[ENV Only] MCP_IDLE_TIMEOUT 300000 Idle session timeout in ms
[ENV Only] MCP_ABSOLUTE_TIMEOUT 3600000 Absolute session timeout in ms
[ENV Only] MCP_REAPER_INTERVAL 60000 Session reaper interval in ms

Note

The --stateless configuration lacks an environment variable equivalent. Standard MYSQL_* environment variables are natively parsed. Note that the --mysql CLI flag only overrides connection-specific variables (Host, Port, User, Password, DB), not all MYSQL_* variables.


Secure Deployments with OAuth 2.0

HTTP Transport supports OAuth 2.0 / OIDC authentication for enterprise deployments:

npx -y @neverinfamous/mysql-mcp \
  --transport http \
  --port 3000 \
  --allowed-io-roots /data \
  --mysql mysql://user:password@localhost:3306/database \
  --oauth-enabled \
  --oauth-issuer http://localhost:8080/realms/mysql-mcp \
  --oauth-audience mysql-mcp-client

See the OAuth page for complete setup instructions.

Connect Your Clients

Using MCP Inspector

Test your HTTP server with MCP Inspector:

# Start the server
npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3000 --allowed-io-roots /data --mysql mysql://...
# In another terminal, connect Inspector
npx -y @modelcontextprotocol/inspector http://localhost:3000/sse

Health Check

curl http://localhost:3000/health
# {"status":"healthy","timestamp":"<ISO-8601-Timestamp>"}

Deploy with Docker

Basic Deployment

# Run container with port mapping
docker run -d \
  --name mysql-mcp-server \
  -p 3000:3000 \
  -e MYSQL_HOST=host.docker.internal \
  -e MYSQL_USER=user \
  -e MYSQL_PASSWORD=password \
  -e MYSQL_DATABASE=database \
  -e REDIS_URL=redis://host.docker.internal:6379 \
  -e MCP_RATE_LIMIT_MAX=100 \
  -e CODEMODE_RATE_LIMIT_MAX=60 \
  -v mcp-data:/data \
  writenotenow/mysql-mcp:latest \
  --transport http \
  --server-host 0.0.0.0 \
  --port 3000 \
  --allowed-io-roots /data

Docker Compose

The following is a minimal excerpt for deploying the HTTP transport via docker-compose.yml. See the Observability & Telemetry page for the full Datadog test ecosystem. It is located in test-server/infrastructure.

# Note: Always include a healthcheck when deploying HTTP transport
services:
  mysql-mcp:
    image: writenotenow/mysql-mcp:latest
    ports:
      - "3000:3000"
    command:
      - --transport
      - http
      - --server-host
      - "0.0.0.0"
      - --port
      - "3000"
      - --allowed-io-roots
      - /data
      - --mysql
      - mysql://user:password@mysql:3306/database
    environment:
      - MYSQL_POOL_SIZE=20
      - REDIS_URL=redis://redis:6379
      - MCP_RATE_LIMIT_MAX=100
      - CODEMODE_RATE_LIMIT_MAX=60
    depends_on:
      - mysql
      - redis
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    volumes:
      - mcp-data:/data
  mysql:
    image: mysql:lts
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: database
    volumes:
      - mysql-data:/var/lib/mysql
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
volumes:
  mysql-data:
  mcp-data:

Scale in the Cloud

AWS ECS / Fargate

  1. Push Docker image to ECR
  2. Create ECS task definition with port 3000 exposed
  3. Configure ALB to route traffic to the container
  4. Set environment variables for MySQL connection

Google Cloud Run

# Build and push to GCR
gcloud builds submit --tag gcr.io/PROJECT_ID/mysql-mcp

# Deploy to Cloud Run
gcloud run deploy mysql-mcp \
  --image gcr.io/PROJECT_ID/mysql-mcp \
  --port 3000 \
  --set-env-vars MYSQL_HOST=...,MYSQL_USER=...,MYSQL_PASSWORD=... \
  --args="--transport","http","--server-host","0.0.0.0","--port","3000","--allowed-io-roots","/data","--stateless"

Azure Container Instances

az container create \
  --resource-group myResourceGroup \
  --name mysql-mcp \
  --image writenotenow/mysql-mcp:latest \
  --ports 3000 \
  --environment-variables \
    MYSQL_HOST=... \
    MYSQL_USER=... \
    MYSQL_PASSWORD=... \
  --command-line "node dist/cli.js --transport http --server-host 0.0.0.0 --port 3000 --allowed-io-roots /data --stateless"

Troubleshoot Issues

Connection Refused

Problem: Client cannot connect to the server

Solutions:

  • Verify server is running: curl http://localhost:3000/health
  • Check firewall rules allow port 3000
  • Ensure --server-host 0.0.0.0 if connecting from another machine
  • Check Docker port mapping: -p 3000:3000

406 Not Acceptable (Streamable HTTP)

Problem: POST /mcp returns 406

Solution: Include the required Accept header:

Accept: application/json, text/event-stream

The server requires clients to accept JSON and SSE formats.

Session Not Found

Problem: Requests return 400 Bad Request or 404 Not Found with session errors

Solutions:

  • Send the Mcp-Session-Id header from the initialize response
  • Cross-protocol guard prevents mixing SSE sessions and /mcp
  • Sessions expire when clients disconnect

OAuth Authentication Failures

Problem: Requests return 401 Unauthorized

Solutions:

  • Verify OAuth issuer URL is correct
  • Check token audience matches --oauth-audience
  • Ensure JWKS URI is accessible from the server
  • See OAuth troubleshooting section

Explore Related Topics

MySQL MCP Documentation

Value Proposition Enforce strict execution boundaries and maximize LLM context efficiency for secure, autonomous database interactions. Read the full value proposition

🏠 Home


Launch Your Setup


Connect Ecosystem Tools


Security & Compliance


Scale Your Operations


Explore External Links

Clone this wiki locally