-
Notifications
You must be signed in to change notification settings - Fork 2
HTTP Transport
Value Proposition Empower your enterprise architecture with highly scalable, secure remote deployments. The HTTP transport provides zero-trust OAuth authentication and high-throughput connection pooling. It is purposefully engineered to drive multi-agent concurrency and mission-critical workloads. Read the full value proposition.
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 mode when:
- Deploying the server to a remote location (cloud, Docker container)
- Multiple AI clients need to connect to the same database
- You need OAuth authentication for enterprise security
- Running the server as a standalone network service
- Serverless/stateless deployments (
--stateless)
Use stdio mode (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 mode. HTTP mode is for advanced deployments.
Important
You must set --allowed-io-roots in HTTP transport mode to explicitly authorize filesystem access. The server will fail to start without it to ensure safe bounds for I/O operations.
# Local installation
npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3001 --allowed-io-roots /data --mysql mysql://user:password@localhost:3306/databaseQuick Start
# Run with port mapping
docker run -p 3001:3001 \
-e MYSQL_HOST=host.docker.internal \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=password \
-e MYSQL_DATABASE=database \
writenotenow/mysql-mcp:latest \
--transport http \
--server-host 0.0.0.0 \
--port 3001 \
--allowed-io-roots /dataProduction/Enterprise
# Run with port mapping, Redis, and distributed rate limiting
docker run -p 3001:3001 \
-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 3001 \
--allowed-io-roots /datanpx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3001 --allowed-io-roots /data --auth-token my-secret --mysql mysql://user:password@localhost:3306/databasenpx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3001 --stateless --allowed-io-roots /data --mysql mysql://user:password@localhost:3306/databaseThe HTTP transport supports two protocol versions, letting modern and legacy clients connect simultaneously.
-
/mcp(Streamable HTTP): The modern protocol using a single endpoint for all communication. -
/sse(Legacy SSE): The backward-compatible protocol establishing a long-lived Server-Sent Events connection.
Modern protocol — single endpoint, session-based:
| 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): GET /mcp returns 405 (Method Not Allowed) since streams are unsupported. 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: The Mcp-Session-Id header may be structurally required by strict MCP clients even though --stateless mode ignores it internally.)
Example — Initialize a session:
curl -X POST http://localhost:3001/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:3001/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 protocol — for 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:3001/sse
# Send a message (in another terminal, using the sessionId from the SSE stream)
curl -X POST "http://localhost:3001/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>"}}}'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 |
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. This 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 |
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 sessions from early termination.
We hardcode CORS to allow all origins (*). This permissiveness defaults to internal routing for seamless orchestrator integration. In production, CORS should be secured at the reverse proxy. We do not yet support restricting origins via CLI flags.
Enable trustProxy behind reverse proxies. This reads the client IP from X-Forwarded-For. It ensures rate limiting and logging use the real IP.
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.
-
/healthbypass — The server processes health checks before rate limiting. This ensures monitoring probes always succeed. -
Retry-Afterheader — Rate-limited responses include aRetry-Afterheader. It shows seconds until the window resets. -
Environment override — Set
MCP_RATE_LIMIT_MAXto customize the HTTP request limit. SetCODEMODE_RATE_LIMIT_MAXto customize the Code Mode execution limit.
Two layers enforce request body size:
- Content-Length header check — fast rejection for well-behaved clients
- Streaming byte tracking — catches missing headers and chunked encoding
For production deployments, use a structured .env file following fleet groupings:
# Server
MYSQLMCP_PORT=3001
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=productionNote
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
|
3001 |
HTTP server port |
--server-host |
MCP_HOST |
localhost |
Host to bind HTTP transport to |
--auth-token |
MCP_AUTH_TOKEN |
- |
Simple bearer token for HTTP auth |
--stateless |
- |
false |
Stateless HTTP mode (no sessions, no SSE) |
--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 |
false |
Enable prometheus metrics endpoint /metrics
|
--allowed-io-roots |
ALLOWED_IO_ROOTS |
- |
Explicitly authorize filesystem boundaries (Required for HTTP mode) |
--oauth-enabled, -o
|
OAUTH_ENABLED |
false |
Enable OAuth authentication |
--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 audit backups |
--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 |
30000 |
Connection pool timeout in ms |
--pool-queue-limit |
MYSQL_POOL_QUEUE_LIMIT |
0 |
Connection pool queue limit |
- |
CODEMODE_MAX_RESULT_SIZE |
102400 |
Max Code Mode result payload in bytes (default 100KB, up to 100MB limit) |
- |
REDIS_URL |
- |
Redis connection URL (used for rate limiting) |
- |
MCP_RATE_LIMIT_MAX |
100 |
Custom HTTP request rate limit |
- |
CODEMODE_RATE_LIMIT_MAX |
60 |
Custom Code Mode execution rate limit |
- |
MCP_MAX_BODY_SIZE |
1048576 | Maximum request body size in bytes |
- |
MCP_REQUEST_TIMEOUT |
120000 | Global request timeout in ms |
- |
MCP_HEADERS_TIMEOUT |
66000 |
Global headers timeout in ms |
- |
MCP_KEEPALIVE_TIMEOUT |
65000 |
Keep-alive timeout in ms |
- |
MCP_IDLE_TIMEOUT |
300000 |
Idle session timeout in ms |
- |
MCP_ABSOLUTE_TIMEOUT |
3600000 |
Absolute session timeout in ms |
- |
MCP_REAPER_INTERVAL |
60000 |
Session reaper interval in ms |
Note
The --stateless configuration lacks an environment variable equivalent.
HTTP mode supports OAuth authentication for enterprise deployments:
npx -y @neverinfamous/mysql-mcp \
--transport http \
--port 3001 \
--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-clientSee the OAuth page for complete setup instructions.
Test your HTTP server with MCP Inspector:
# Start the server
npx -y @neverinfamous/mysql-mcp --transport http --server-host 0.0.0.0 --port 3001 --allowed-io-roots /data --mysql mysql://...
# In another terminal, connect Inspector
npx -y @modelcontextprotocol/inspector http://localhost:3001/ssecurl http://localhost:3001/health
# {"status":"healthy","timestamp":"<ISO-8601-Timestamp>"}# Run container with port mapping
docker run -d \
--name mysql-mcp-server \
-p 3001:3001 \
-e MYSQL_HOST=host.docker.internal \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=password \
-e MYSQL_DATABASE=database \
writenotenow/mysql-mcp:latest \
--transport http \
--server-host 0.0.0.0 \
--port 3001 \
--allowed-io-roots /dataThe root repository includes a minimal docker-compose.yml that deploys the mysql-mcp HTTP transport alongside a basic observability stack containing prometheus and grafana services. For the full, enterprise-grade test ecosystem with Datadog, see the Observability & Telemetry page and test-server/infrastructure.
# Note: Always include a healthcheck when deploying HTTP transport
services:
mysql-mcp:
image: writenotenow/mysql-mcp:latest
ports:
- "3001:3001"
command:
- --transport
- http
- --server-host
- "0.0.0.0"
- --port
- "3001"
- --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
volumes:
- mcp-data:/data
mysql:
image: mysql:lts
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: database
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
mcp-data:- Push Docker image to ECR
- Create ECS task definition with port 3001 exposed
- Configure ALB to route traffic to the container
- Set environment variables for MySQL connection
# 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 3001 \
--set-env-vars MYSQL_HOST=...,MYSQL_USER=...,MYSQL_PASSWORD=... \
--command "--transport,http,--port,3001,--allowed-io-roots,/data,--stateless"az container create \
--resource-group myResourceGroup \
--name mysql-mcp \
--image writenotenow/mysql-mcp:latest \
--ports 3001 \
--environment-variables \
MYSQL_HOST=... \
MYSQL_USER=... \
MYSQL_PASSWORD=... \
--command-line "--transport http --server-host 0.0.0.0 --port 3001 --allowed-io-roots /data --stateless"Problem: Client cannot connect to the server
Solutions:
- Verify server is running:
curl http://localhost:3001/health - Check firewall rules allow port 3001
- Ensure
--server-host 0.0.0.0if connecting from another machine - Check Docker port mapping:
-p 3001:3001
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.
Problem: Requests return 400 Bad Request or 404 Not Found with session errors
Solutions:
- Send the
Mcp-Session-Idheader from theinitializeresponse - Cross-protocol guard prevents mixing SSE sessions and
/mcp - Sessions expire when clients disconnect
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
- Configuration - General configuration options
- OAuth - OAuth authentication setup
- MCP Inspector - Testing with MCP Inspector
- MCP Protocol Specification - Official MCP docs
Unlock autonomous database orchestration with an enterprise-grade MySQL MCP server. Featuring blazing-fast sandboxed Code Mode, uncompromising schema enforcement, and seamless ecosystem integrations to power secure, intelligent AI workflows.
- Installation
- Configuration
- Architecture
- HTTP Transport
- Tool Filtering
- Code Mode
- Tools
- Prompts
- Resources
- Observability & Telemetry