How to Deploy MetaMCP Behind Nginx: Ensuring SSE Compatibility

Configure Nginx with keepalive_timeout 300, disable buffering via X-Accel-Buffering 'no', and clear the Connection header to stream MetaMCP's Server-Sent Events without interruption.

MetaMCP relies on Server-Sent Events (SSE) to stream real-time data through long-lived HTTP connections, which requires specific reverse-proxy tuning to avoid premature connection drops. When deploying the metatool-ai/metamcp repository behind Nginx, default buffering settings will block or delay the event stream, breaking the real-time experience for downstream clients. This guide provides the exact configuration directives and source file references needed to deploy MetaMCP behind Nginx while maintaining full SSE compatibility.

Why SSE Requires Special Nginx Configuration

MetaMCP uses SSE to maintain persistent connections between the backend and clients such as Open WebUI or Claude Desktop. In apps/backend/src/routers/public-metamcp/sse.ts, the application handles the /metamcp/:endpoint_name/sse route by streaming EventSource data over a single HTTP connection that can remain open for minutes or hours. Nginx's default behavior—buffering responses and closing idle connections after 75 seconds—directly conflicts with this architecture.

To support SSE properly, you must:

  • Extend connection timeouts to prevent Nginx from closing idle streams
  • Disable response buffering to ensure immediate delivery of each data: line
  • Preserve original request headers so MetaMCP can correctly build URLs and perform authentication

Critical Nginx Directives for MetaMCP SSE

The repository provides a working reference in nginx.conf.example. The following directives are essential for SSE compatibility:

Directive Purpose Source Location
keepalive_timeout 300; Extends idle timeout from the default 75s to 5 minutes (or longer) to support long-lived streams nginx.conf.example, line 31
proxy_set_header Connection ''; Removes the Connection: close header that would otherwise terminate the stream immediately nginx.conf.example, line 134
proxy_set_header Cache-Control 'no-cache'; Prevents intermediate caches from buffering the event stream nginx.conf.example, line 138
proxy_set_header X-Accel-Buffering 'no'; Disables Nginx's internal response buffering, ensuring real-time delivery nginx.conf.example, line 139
proxy_set_header Host $host; Preserves the original Host header for correct URL generation nginx.conf.example, lines 122-125

Complete Nginx Configuration for MetaMCP

Copy the following location block into your nginx.conf, adjusting the proxy_pass URL to match your MetaMCP backend address (default port 12008):

http {
    # Critical for long-lived SSE connections

    keepalive_timeout 300;

    server {
        listen 8080;
        server_name localhost;

        # Static frontend assets

        location / {
            root html;
            index index.html index.htm;
        }

        # MetaMCP SSE and API proxy

        location /metamcp/ {
            proxy_pass http://localhost:12008;
            proxy_http_version 1.1;
            
            # Preserve client information

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # SSE-specific headers (disable buffering)

            proxy_set_header Connection '';
            proxy_set_header Cache-Control 'no-cache';
            proxy_set_header X-Accel-Buffering 'no';
        }
    }
}

Key configuration details:

  • proxy_http_version 1.1 – Required for keepalive connections to the backend
  • proxy_pass http://localhost:12008 – Points to the MetaMCP backend container or process
  • Location path /metamcp/ – Matches the route defined in apps/backend/src/routers/public-metamcp/sse.ts

Docker Compose Integration

If running MetaMCP with Docker Compose, expose the backend port and mount your custom Nginx configuration:

services:
  metamcp-backend:
    build: .
    container_name: metamcp-backend
    expose:
      - "12008"
    environment:
      - NODE_ENV=production

  nginx:
    image: nginx:stable-alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8080:8080"
    depends_on:
      - metamcp-backend

Verifying SSE Compatibility Through Nginx

Test that Nginx correctly proxies the SSE stream without buffering by connecting directly to the /metamcp/:endpoint_name/sse endpoint:

curl -N -H "Accept: text/event-stream" http://localhost:8080/metamcp/my-endpoint/sse

The -N flag disables curl's buffering, allowing you to observe each data: line as the server emits it. If the configuration is correct, you will see a continuous stream of events; if buffering is misconfigured, the connection will hang or timeout without displaying data.

This verification confirms that the directives from nginx.conf.example are functioning correctly and that your MetaMCP instance can stream events reliably through the reverse proxy.

Summary

  • Extend timeouts: Set keepalive_timeout 300 (or higher) in the http block to prevent Nginx from closing idle SSE connections
  • Disable buffering: Use proxy_set_header X-Accel-Buffering 'no' and proxy_set_header Connection '' to ensure real-time event delivery
  • Preserve headers: Pass Host, X-Real-IP, and forwarding headers so MetaMCP can authenticate requests and generate correct URLs
  • Reference implementation: Consult apps/backend/src/routers/public-metamcp/sse.ts for the SSE endpoint logic and nginx.conf.example for the complete working configuration
  • Test with curl: Use curl -N to verify unbuffered streaming through your Nginx proxy before deploying to production

Frequently Asked Questions

What port does the MetaMCP backend run on by default?

The MetaMCP backend listens on port 12008 by default, as reflected in the proxy_pass directive within nginx.conf.example. When deploying with Docker, expose this port internally and map Nginx to communicate with http://metamcp-backend:12008 (or localhost:12008 for single-host deployments).

Why does SSE stop working when I add Nginx?

Nginx buffers responses by default to optimize throughput, which breaks SSE's real-time nature. Additionally, Nginx closes idle connections after 75 seconds by default. To fix this, you must disable buffering with X-Accel-Buffering 'no', clear the Connection header, and increase keepalive_timeout as shown in the configuration above.

Can I use this same configuration for the internal MCP proxy routes?

Yes. The SSE transport is also used for internal proxy routes implemented in apps/backend/src/routers/mcp-proxy/server.ts. The same Nginx location block and buffering directives apply to any MetaMCP endpoint that utilizes Server-Sent Events, ensuring consistent behavior across public and internal APIs.

How do I handle authentication headers when proxying through Nginx?

Pass the original Host header and client IP information using proxy_set_header Host $host, X-Real-IP $remote_addr, and X-Forwarded-For $proxy_add_x_forwarded_for. MetaMCP inspects these headers to validate requests and build absolute URLs, so preserving them is essential for correct authentication and routing behavior.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →