diff --git a/README.md b/README.md index 31d1ee068..2d04d6c19 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ We provide official SDKs to integrate and interact with the TN network: Learn how to query data through AI Clients: [TRUF.NETWORK MCP](./docs/mcp-server.md) +For production deployments behind reverse proxies: [MCP Reverse Proxy Configuration](./docs/mcp-reverse-proxy.md) + ## Development Guide For contributors and developers building on top of TN: diff --git a/docs/examples/mcp-reverse-proxy/Caddyfile.example b/docs/examples/mcp-reverse-proxy/Caddyfile.example new file mode 100644 index 000000000..92c40f2f3 --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/Caddyfile.example @@ -0,0 +1,123 @@ +# Caddy configuration example for TRUF.NETWORK MCP Server with SSE transport +# +# Caddy automatically handles many SSE requirements, making configuration simpler +# compared to other reverse proxies. +# +# Usage: +# 1. Copy this file to your Caddy configuration directory +# 2. Update domain name and backend address +# 3. Test: caddy validate --config Caddyfile +# 4. Reload: caddy reload --config Caddyfile + +# Production HTTPS configuration with automatic TLS +mcp.your-domain.com { + # MCP Server SSE endpoint + reverse_proxy /sse/* localhost:8000 { + # Caddy handles SSE automatically, but these settings ensure optimal performance + flush_interval -1 + + # Headers + header_up Host {http.reverse_proxy.upstream.hostport} + header_up X-Real-IP {http.request.remote} + header_up X-Forwarded-Proto {http.request.scheme} + header_up X-Forwarded-Port {http.request.port} + + # Optional: Health checks + health_uri /health + health_interval 30s + health_timeout 5s + } + + # Security headers + header { + # Security headers + X-Frame-Options DENY + X-Content-Type-Options nosniff + X-XSS-Protection "1; mode=block" + Strict-Transport-Security "max-age=31536000; includeSubDomains" + Referrer-Policy "strict-origin-when-cross-origin" + + # CORS headers for browser-based clients (optional) + Access-Control-Allow-Origin "*" + Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization" + Access-Control-Allow-Methods "GET, POST, OPTIONS" + } + + # Handle preflight requests + @options method OPTIONS + respond @options 204 + + # Rate limiting (optional; requires a Caddy plugin such as mholt/caddy-ratelimit) + # rate_limit { + # zone static + # key {remote} + # events 10 + # window 1m + # } + + # Access control (optional) - restrict to trusted networks + # @denied not remote_ip 192.168.1.0/24 10.0.0.0/8 + # respond @denied 403 + + # Logging + log { + output file /var/log/caddy/mcp-access.log + format json + } + + # Error handling + handle_errors { + respond "MCP Server Error: {http.error.status_code}" {http.error.status_code} + } +} + +# Development configuration (HTTP only) +# mcp-dev.your-domain.com:80 { +# reverse_proxy /sse/* localhost:8000 { +# flush_interval -1 +# header_up Host {http.reverse_proxy.upstream.hostport} +# } +# } + +# Advanced configuration with load balancing (for high availability) +# mcp-ha.your-domain.com { +# reverse_proxy /sse/* { +# to localhost:8000 +# to localhost:8001 +# to localhost:8002 +# +# lb_policy least_conn +# health_uri /health +# health_interval 10s +# +# flush_interval -1 +# header_up Host {http.reverse_proxy.upstream.hostport} +# } +# } + +# Global options (place at the top of your Caddyfile) +{ + # Email for Let's Encrypt (update with your email) + email admin@your-domain.com + + # Optional: Enable admin API + admin localhost:2019 + + # Logging + log { + level INFO + } + + # ACME CA for certificates (default is Let's Encrypt production) + # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory # staging +} + +# Example with custom TLS certificates +# mcp-custom-tls.your-domain.com { +# tls /path/to/cert.pem /path/to/key.pem +# +# reverse_proxy /sse/* localhost:8000 { +# flush_interval -1 +# header_up Host {http.reverse_proxy.upstream.hostport} +# } +# } \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/Caddyfile.windows.example b/docs/examples/mcp-reverse-proxy/Caddyfile.windows.example new file mode 100644 index 000000000..d9fa36dfb --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/Caddyfile.windows.example @@ -0,0 +1,54 @@ +# Caddyfile for Windows MCP Server Testing +# This configuration is optimized for Windows development and testing +# with IPv4 addressing and local development considerations +# Different OS may require different configurations + +# Basic HTTP configuration for local testing +:8080 { + # Route all SSE traffic to the MCP server + # Using IPv4 (127.0.0.1) to avoid Windows IPv6/IPv4 issues + @sse path /sse /sse/* + reverse_proxy @sse 127.0.0.1:8000 { + # Optional for SSE; Caddy streams text/event-stream by default. + # flush_interval -1 + # Rely on Caddy defaults for Host/X-Forwarded-*. + } + + # Optional: Health check endpoint + reverse_proxy /health 127.0.0.1:8000 + + # Enable access logging for debugging + log { + output file caddy-access.log + format single_field common_log + } +} + +# Production HTTPS configuration with domain +# Replace your-domain.com with your actual domain +# your-domain.com { +# reverse_proxy /sse* 127.0.0.1:8000 { +# flush_interval -1 +# header_up Host {http.reverse_proxy.upstream.hostport} +# header_up Connection {>Connection} +# header_up Cache-Control {>Cache-Control} +# header_up X-Real-IP {http.request.remote} +# header_up X-Forwarded-For {http.request.remote} +# header_up X-Forwarded-Proto https +# } +# +# # Security headers for production +# header { +# X-Frame-Options DENY +# X-Content-Type-Options nosniff +# X-XSS-Protection "1; mode=block" +# Strict-Transport-Security "max-age=31536000; includeSubDomains" +# } +# } + +# Usage Instructions: +# 1. Save this file as "Caddyfile" (no extension) +# 2. Start MCP server: postgres-mcp --transport=sse --sse-host=127.0.0.1 --sse-port=8000 +# 3. Start Caddy: caddy run +# 4. Test: curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8080/sse +# 5. Configure Claude Desktop with: npx mcp-remote http://127.0.0.1:8080/sse \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/README.md b/docs/examples/mcp-reverse-proxy/README.md new file mode 100644 index 000000000..43a20a389 --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/README.md @@ -0,0 +1,294 @@ +# MCP Server Reverse Proxy Configuration Examples + +This directory contains ready-to-use configuration examples for deploying the TRUF.NETWORK MCP server with SSE (Server-Sent Events) transport behind various reverse proxy servers. + +๐Ÿ“– **Main Documentation**: See [MCP Server Reverse Proxy Configuration](../../mcp-reverse-proxy.md) for detailed setup instructions and explanations. + +## ๐Ÿ“ Files Overview + +| File | Description | +|------|-------------| +| `nginx.conf.example` | Complete nginx configuration with HTTPS, security headers, and SSE optimization | +| `Caddyfile.example` | Caddy configuration with automatic HTTPS and built-in SSE support | +| `Caddyfile.windows.example` | Windows-specific Caddy configuration with IPv4 addressing | +| `traefik.yml.example` | Traefik static/dynamic configuration with Docker Compose integration | +| `docker-compose.sse.yaml` | Complete Docker Compose setup with multiple reverse proxy options | +| `claude-desktop-config.example` | Claude Desktop configuration examples | +| `README.md` | This documentation file | + +## ๐Ÿš€ Quick Start + +### Option 1: Using Docker Compose (Recommended) + +1. **Copy the Docker Compose file:** + ```bash + cp docker-compose.sse.yaml docker-compose.yml + ``` + +2. **Create environment file:** + ```bash + cat > .env << EOF + # TRUF.NETWORK uses kwildb/postgres image which auto-creates kwild user/database + # Note: No password needed - uses POSTGRES_HOST_AUTH_METHOD=trust + DOMAIN=mcp.your-domain.com + ACME_EMAIL=admin@your-domain.com + EOF + ``` + +3. **Start with your preferred reverse proxy:** + ```bash + # Option A: Nginx + docker compose --profile nginx up -d + + # Option B: Caddy (automatic HTTPS) + docker compose --profile caddy up -d + + # Option C: Traefik (container orchestration) + docker compose --profile traefik up -d + ``` + +### Option 2: Windows Native Testing + +**๐Ÿ“‹ Complete Windows testing guide:** [Windows MCP Testing](../../windows-mcp-testing.md) + +1. **Copy Windows-specific files:** + ```powershell + cp Caddyfile.windows.example Caddyfile + cp claude-desktop-config.windows.example claude-config.json + ``` + +2. **Start MCP server with IPv4 addressing:** + ```powershell + $env:DATABASE_URI = "postgresql://kwild@127.0.0.1:5432/kwild" + postgres-mcp --transport=sse --sse-host=127.0.0.1 --sse-port=8000 + ``` + +3. **Start Caddy reverse proxy:** + ```powershell + caddy run + ``` + +4. **Test both endpoints:** + ```powershell + curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8000/sse + curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8080/sse + ``` + +### Option 3: Manual Configuration + +1. **Choose your reverse proxy configuration file** +2. **Update domain names and paths for your environment** +3. **Deploy according to your reverse proxy's documentation** + +## ๐Ÿ”ง Configuration Requirements + +### Critical SSE Settings + +All reverse proxy configurations **MUST** include these settings for SSE to work: + +```nginx +# Nginx example +proxy_set_header Connection ''; +proxy_http_version 1.1; +proxy_buffering off; +proxy_cache off; +chunked_transfer_encoding off; +``` + +```caddyfile +# Caddy example (mostly automatic) +reverse_proxy /sse/* localhost:8000 { + flush_interval -1 +} +``` + +### Why These Settings Matter + +- **`proxy_buffering off`**: Most critical - prevents response buffering that breaks streaming +- **`proxy_http_version 1.1`**: Required for persistent connections and chunked encoding +- **`Connection ''`**: Clears connection header to prevent pooling issues +- **`proxy_cache off`**: SSE responses must never be cached + +## ๐ŸŒ Supported Reverse Proxies + +### Nginx โœ… +- **Complexity**: Medium +- **SSE Support**: Excellent with proper configuration +- **HTTPS**: Manual certificate management +- **Best for**: High-traffic production environments + +### Caddy โœ… +- **Complexity**: Low +- **SSE Support**: Built-in, minimal configuration needed +- **HTTPS**: Automatic with Let's Encrypt +- **Best for**: Simple deployments, automatic HTTPS + +### Traefik โœ… +- **Complexity**: Medium-High +- **SSE Support**: Good with proper middleware +- **HTTPS**: Automatic with multiple providers +- **Best for**: Container orchestration, microservices + +### HAProxy โœ… +- **Complexity**: High +- **SSE Support**: Good with timeout configuration +- **HTTPS**: Requires separate termination +- **Best for**: Load balancing, high availability + +### Apache โš ๏ธ +- **Complexity**: High +- **SSE Support**: Possible but requires extensive configuration +- **HTTPS**: Manual configuration +- **Best for**: Legacy environments + +## ๐Ÿงช Testing Your Setup + +### 1. Test SSE Endpoint + +```bash +# Basic connectivity test +curl -v http://your-domain.com/sse + +# SSE-specific test +curl -H "Accept: text/event-stream" \ + -H "Cache-Control: no-cache" \ + http://your-domain.com/sse +``` + +### 2. Test MCP Client + +Configure your MCP client: + +```json +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "url": "http://your-domain.com/sse" + } + } +} +``` + +### 3. Verify Streaming + +Look for immediate response headers (no buffering): + +```bash +curl -v -N -H "Accept: text/event-stream" http://your-domain.com/sse +``` + +## ๐Ÿ”’ Security Considerations + +### Production Checklist + +- [ ] Enable HTTPS with valid certificates +- [ ] Implement rate limiting +- [ ] Configure access control (IP whitelist, authentication) +- [ ] Set up security headers +- [ ] Enable logging and monitoring +- [ ] Regular security updates +- [ ] Network segmentation (private networks) + +### Example Security Headers + +```nginx +add_header X-Frame-Options DENY; +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; +``` + +## ๐Ÿšจ Common Issues + +### Issue: SSE Connection Drops +**Solution**: Increase proxy timeouts +```nginx +proxy_read_timeout 300s; +proxy_send_timeout 300s; +``` + +### Issue: No Streaming (Buffered Response) +**Solution**: Verify `proxy_buffering off` is set + +### Issue: CORS Errors +**Solution**: Add CORS headers for browser-based clients +```nginx +add_header 'Access-Control-Allow-Origin' '*'; +``` + +### Issue: SSL/TLS Problems +**Solution**: Check certificate paths and forwarded headers + +## ๐Ÿ“Š Monitoring + +### Nginx Access Logs +```bash +tail -f /var/log/nginx/access.log | grep "/sse" +``` + +### MCP Server Logs +```bash +docker logs -f your-mcp-container +``` + +### Health Checks +Add health check endpoints to your configuration: +```nginx +location /health { + proxy_pass http://localhost:8000/health; + access_log off; +} +``` + +## ๐Ÿ“š Additional Resources + +- [MCP Server Documentation](../../mcp-server.md) +- [Complete Reverse Proxy Guide](../../mcp-reverse-proxy.md) +- [Node Operator Guide](../../node-operator-guide.md) +- [TRUF.NETWORK Documentation](https://docs.truf.network) + +## ๐Ÿ†˜ Support + +If you encounter issues: + +1. Check the [troubleshooting section](../../mcp-reverse-proxy.md#common-issues-and-solutions) +2. Verify your configuration against the examples +3. Test with curl commands provided above +4. Check logs for error messages +5. Open an issue on the [TRUF.NETWORK repository](https://github.com/trufnetwork/node) + +## ๐Ÿค Contributing + +Have a working configuration for another reverse proxy? Please contribute: + +1. Create a new configuration file +2. Test thoroughly +3. Document any special requirements +4. Submit a pull request + +--- + +**โšก Quick Deployment Commands:** + +```bash +# Clone and setup +git clone https://github.com/trufnetwork/node.git +cd node/docs/examples/mcp-reverse-proxy + +# Configure environment (kwildb/postgres auto-creates kwild user/database) +cat > .env << EOF +DOMAIN=mcp.your-domain.com +ACME_EMAIL=admin@your-domain.com +EOF + +# Deploy with Caddy (easiest) +docker compose --profile caddy up -d + +# Check status +docker compose ps +docker compose logs -f mcp-server + +# Test +curl -H "Accept: text/event-stream" http://your-domain.com/sse +``` \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/claude-desktop-config.example b/docs/examples/mcp-reverse-proxy/claude-desktop-config.example new file mode 100644 index 000000000..9282ea376 --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/claude-desktop-config.example @@ -0,0 +1,97 @@ +# Claude Desktop Configuration Examples +# Location: %APPDATA%\Claude\claude_desktop_config.json (Different OS might vary) + +# Example 1: Direct SSE connection (through mcp-remote proxy) +# This connects directly to the MCP server on port 8000 +{ + "mcpServers": { + "truf-direct": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8000/sse" + ] + } + } +} + +# Example 2: Reverse proxy connection (through Caddy) +# This connects through the reverse proxy on port 8080 +{ + "mcpServers": { + "truf-proxy": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8080/sse" + ] + } + } +} + +# Example 3: Production HTTPS connection +# This connects to a production server with HTTPS +{ + "mcpServers": { + "truf-production": { + "command": "npx", + "args": [ + "mcp-remote", + "https://mcp.your-domain.com/sse" + ] + } + } +} + +# Example 4: Multiple server configuration +# You can configure multiple servers, but only enable one at a time to avoid conflicts +{ + "mcpServers": { + "truf-local-direct": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8000/sse" + ] + }, + "truf-local-proxy": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8080/sse" + ] + } + } +} + +# IMPORTANT NOTES FOR WINDOWS: +# +# 1. Always use IPv4 addresses (127.0.0.1) instead of localhost on Windows +# to avoid IPv6 resolution issues with SSH tunnels +# +# 2. Claude Desktop requires the "command" field for all MCP servers +# Direct SSE configuration with "type": "sse" is NOT supported +# +# 3. You must use npx mcp-remote as a proxy for SSE connections +# Make sure Node.js and npx are installed and in your PATH +# +# 4. Only enable one server at a time. Having multiple active servers +# can cause conflicts in Claude Desktop +# +# 5. After editing this file, completely restart Claude Desktop +# (close all windows and restart the application) +# +# 6. To test if your configuration works: +# - Open Claude Desktop +# - Start a new conversation +# - Ask: "Can you list the available MCP tools?" +# - You should see tools like list_schemas, execute_sql, etc. + +# TROUBLESHOOTING: +# +# If Claude Desktop shows no MCP tools: +# - Check that MCP server is running (postgres-mcp --transport=sse ...) +# - Verify the URL is accessible (curl.exe -H "Accept: text/event-stream" URL) +# - Ensure no other MCP servers are configured in the same file +# - Check Claude Desktop logs in the application menu +# - Try restarting Claude Desktop completely \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/docker-compose.sse.yaml b/docs/examples/mcp-reverse-proxy/docker-compose.sse.yaml new file mode 100644 index 000000000..441c9d807 --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/docker-compose.sse.yaml @@ -0,0 +1,254 @@ +# Docker Compose example for TRUF.NETWORK MCP Server with SSE transport +# +# This file demonstrates how to deploy the MCP server with SSE transport +# using Docker Compose, with examples for different reverse proxy setups. +# +# Usage: +# 1. Copy this file and customize environment variables +# 2. Create .env file with your settings +# 3. Run: docker compose -f docker-compose.sse.yaml up -d + +version: '3.8' + +services: + # PostgreSQL Database (Kwil-configured image for TRUF.NETWORK) + postgres: + image: kwildb/postgres:latest + container_name: tn-postgres + restart: unless-stopped + environment: + POSTGRES_HOST_AUTH_METHOD: trust + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init-db:/docker-entrypoint-initdb.d:ro # Optional: init scripts + networks: + - internal + ports: + - "127.0.0.1:5432:5432" # Bind to localhost only for security + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-kwild} -d ${POSTGRES_DB:-kwild}"] + interval: 10s + timeout: 5s + retries: 5 + + # TRUF.NETWORK MCP Server with SSE transport + mcp-server: + image: trufnetwork/postgres-mcp:latest + container_name: truf-mcp-server + restart: unless-stopped + command: [ + "--transport=sse", + "--sse-host=0.0.0.0", + "--sse-port=8000", + "--access-mode=restricted" # Use 'unrestricted' for development + ] + environment: + DATABASE_URI: postgresql://kwild@postgres:5432/kwild + networks: + - internal + - proxy # For reverse proxy access + ports: + - "${MCP_PORT:-8000}:8000" # Exposed for reverse proxy + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + volumes: + - ./logs/mcp:/var/log/mcp:rw # Optional: log persistence + + # Example: Nginx reverse proxy + nginx: + image: nginx:alpine + container_name: truf-nginx + restart: unless-stopped + ports: + - "${NGINX_HTTP_PORT:-80}:80" + - "${NGINX_HTTPS_PORT:-443}:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./certs:/etc/nginx/certs:ro # SSL certificates + - ./logs/nginx:/var/log/nginx:rw + networks: + - proxy + depends_on: + - mcp-server + profiles: + - nginx + + # Example: Caddy reverse proxy (alternative to nginx) + caddy: + image: caddy:2-alpine + container_name: truf-caddy + restart: unless-stopped + ports: + - "${CADDY_HTTP_PORT:-80}:80" + - "${CADDY_HTTPS_PORT:-443}:443" + volumes: + - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ./caddy/data:/data + - ./caddy/config:/config + - ./logs/caddy:/var/log/caddy:rw + networks: + - proxy + depends_on: + - mcp-server + environment: + DOMAIN: ${DOMAIN:-localhost} + profiles: + - caddy + + # Example: Traefik reverse proxy (alternative to nginx/caddy) + traefik: + image: traefik:v3.0 + container_name: truf-traefik + restart: unless-stopped + command: + - --api.dashboard=true + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --certificatesresolvers.letsencrypt.acme.httpchallenge=true + - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web + - --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL:-admin@your-domain.com} + - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json + ports: + - "${TRAEFIK_HTTP_PORT:-80}:80" + - "${TRAEFIK_HTTPS_PORT:-443}:443" + - "${TRAEFIK_DASHBOARD_PORT:-8080}:8080" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik/letsencrypt:/letsencrypt + - ./logs/traefik:/var/log/traefik:rw + networks: + - proxy + profiles: + - traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN:-localhost}`)" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=admin:$$2y$$10$$K4qQe/9v4V4eJn5l5Q8K5.9Zl6J8Z9n5Q8K5.9Zl6J8Z9n5Q8K5.9" # admin:password + + # MCP Server with Traefik labels (when using traefik profile) + mcp-server-traefik: + extends: + service: mcp-server + labels: + - "traefik.enable=true" + - "traefik.http.routers.mcp.rule=Host(`mcp.${DOMAIN:-localhost}`) && PathPrefix(`/sse`)" + - "traefik.http.routers.mcp.entrypoints=websecure" + - "traefik.http.routers.mcp.tls.certresolver=letsencrypt" + - "traefik.http.services.mcp.loadbalancer.server.port=8000" + # SSE-specific middleware + - "traefik.http.routers.mcp.middlewares=mcp-sse" + - "traefik.http.middlewares.mcp-sse.headers.customrequestheaders.Connection=" + profiles: + - traefik + + # Monitoring: Grafana (optional) + grafana: + image: grafana/grafana:latest + container_name: truf-grafana + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} + volumes: + - grafana_data:/var/lib/grafana + networks: + - internal + - proxy + ports: + - "${GRAFANA_PORT:-3000}:3000" + profiles: + - monitoring + + # Monitoring: Prometheus (optional) + prometheus: + image: prom/prometheus:latest + container_name: truf-prometheus + restart: unless-stopped + volumes: + - ./prometheus:/etc/prometheus:ro + - prometheus_data:/prometheus + networks: + - internal + ports: + - "${PROMETHEUS_PORT:-9090}:9090" + profiles: + - monitoring + +networks: + internal: + driver: bridge + internal: true + proxy: + driver: bridge + +volumes: + postgres_data: + grafana_data: + prometheus_data: + +# Example environment file (.env) +# Copy to .env and customize: +# +# # Database settings (TRUF.NETWORK uses kwildb/postgres image with trust auth) +# # The kwildb/postgres image automatically creates kwild user and database +# # Note: No password needed due to POSTGRES_HOST_AUTH_METHOD=trust +# +# # MCP Server settings +# MCP_PORT=8000 +# +# # Reverse proxy settings +# DOMAIN=mcp.your-domain.com +# NGINX_HTTP_PORT=80 +# NGINX_HTTPS_PORT=443 +# +# # SSL/TLS +# ACME_EMAIL=admin@your-domain.com +# +# # Monitoring (optional) +# GRAFANA_PASSWORD=secure-grafana-password +# PROMETHEUS_PORT=9090 +# GRAFANA_PORT=3000 + +--- +# Minimal production setup (nginx + mcp-server + postgres) +# Run with: docker compose -f docker-compose.sse.yaml --profile nginx up -d + +# Development setup (direct access, no reverse proxy) +# Run with: docker compose -f docker-compose.sse.yaml up mcp-server postgres -d + +# Full setup with Caddy and monitoring +# Run with: docker compose -f docker-compose.sse.yaml --profile caddy --profile monitoring up -d + +# Usage Examples: +# +# 1. Start with nginx reverse proxy: +# docker compose -f docker-compose.sse.yaml --profile nginx up -d +# +# 2. Start with Caddy (automatic HTTPS): +# docker compose -f docker-compose.sse.yaml --profile caddy up -d +# +# 3. Start with Traefik (container orchestration): +# docker compose -f docker-compose.sse.yaml --profile traefik up -d +# +# 4. Development mode (no reverse proxy): +# docker compose -f docker-compose.sse.yaml up mcp-server postgres -d +# +# 5. View logs: +# docker compose -f docker-compose.sse.yaml logs -f mcp-server +# +# 6. Stop services: +# docker compose -f docker-compose.sse.yaml down +# +# 7. Full cleanup: +# docker compose -f docker-compose.sse.yaml down -v --remove-orphans \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/nginx.conf.example b/docs/examples/mcp-reverse-proxy/nginx.conf.example new file mode 100644 index 000000000..145584d1d --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/nginx.conf.example @@ -0,0 +1,117 @@ +# Nginx configuration example for TRUF.NETWORK MCP Server with SSE transport +# +# This configuration provides a production-ready setup for proxying the MCP server +# with proper SSE (Server-Sent Events) support. +# +# Usage: +# 1. Copy this file to /etc/nginx/sites-available/mcp-server +# 2. Update server_name and paths for your environment +# 3. Enable: ln -s /etc/nginx/sites-available/mcp-server /etc/nginx/sites-enabled/ +# 4. Test: nginx -t +# 5. Reload: systemctl reload nginx + +# Rate limiting (add to http block in nginx.conf) +# limit_req_zone $binary_remote_addr zone=mcp_sse:10m rate=5r/m; + +server { + listen 443 ssl http2; + server_name mcp.your-domain.com; + + # SSL Configuration - Update paths for your certificates + ssl_certificate /etc/letsencrypt/live/mcp.your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/mcp.your-domain.com/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Security Headers + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header Referrer-Policy "strict-origin-when-cross-origin"; + + # Rate Limiting + # limit_req zone=mcp_sse burst=10 nodelay; + + # MCP Server SSE Endpoint - CRITICAL SSE CONFIGURATION + location /sse { + # Backend server (adjust IP/port as needed) + proxy_pass http://127.0.0.1:8000/sse; + + # ESSENTIAL SSE SETTINGS - DO NOT MODIFY + proxy_set_header Connection ''; + proxy_http_version 1.1; + proxy_buffering off; + proxy_cache off; + chunked_transfer_encoding off; + + # Extended timeouts for long-lived connections + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_connect_timeout 60s; + + # Standard proxy headers + 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 https; + proxy_set_header X-Forwarded-Port 443; + + # CORS headers for browser-based clients (optional) + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; + + # Handle preflight requests + if ($request_method = 'OPTIONS') { + return 204; + } + + # Access control - restrict to trusted networks (adjust as needed) + # allow 192.168.1.0/24; + # allow 10.0.0.0/8; + # deny all; + + # Logging + access_log /var/log/nginx/mcp-sse.access.log; + error_log /var/log/nginx/mcp-sse.error.log; + } + + # Health check endpoint (optional) + location /health { + proxy_pass http://127.0.0.1:8000/health; + proxy_set_header Host $host; + access_log off; + } + + # Deny all other locations + location / { + return 404; + } +} + +# HTTP to HTTPS redirect +server { + listen 80; + server_name mcp.your-domain.com; + return 301 https://$host$request_uri; +} + +# Example basic HTTP configuration (for development only) +# server { +# listen 80; +# server_name mcp-dev.your-domain.com; +# +# location /sse { +# proxy_pass http://127.0.0.1:8000/sse; +# proxy_set_header Connection ''; +# proxy_http_version 1.1; +# proxy_buffering off; +# proxy_cache off; +# chunked_transfer_encoding off; +# proxy_set_header Host $host; +# } +# } \ No newline at end of file diff --git a/docs/examples/mcp-reverse-proxy/traefik.yml.example b/docs/examples/mcp-reverse-proxy/traefik.yml.example new file mode 100644 index 000000000..7daad3632 --- /dev/null +++ b/docs/examples/mcp-reverse-proxy/traefik.yml.example @@ -0,0 +1,313 @@ +# Traefik configuration example for TRUF.NETWORK MCP Server with SSE transport +# +# This file shows both static configuration (traefik.yml) and dynamic configuration +# examples for deploying the MCP server with proper SSE support. +# +# Usage: +# 1. Use this as your traefik.yml static configuration +# 2. Apply the Docker Compose labels or file-based dynamic config +# 3. Ensure the MCP server is accessible from Traefik + +# Static Configuration (traefik.yml) +global: + checkNewVersion: false + sendAnonymousUsage: false + +api: + dashboard: true + insecure: false # Set to true for development + +entryPoints: + web: + address: ":80" + # Redirect HTTP to HTTPS + http: + redirections: + entrypoint: + to: websecure + scheme: https + permanent: true + + websecure: + address: ":443" + +providers: + # Docker provider for container discovery + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + network: traefik + + # File provider for additional configuration + file: + directory: /etc/traefik/dynamic + watch: true + +certificatesResolvers: + letsencrypt: + acme: + email: admin@your-domain.com + storage: /letsencrypt/acme.json + httpChallenge: + entryPoint: web + # Alternative: DNS challenge + # dnsChallenge: + # provider: cloudflare + # resolvers: + # - "1.1.1.1:53" + +# Logging +log: + level: INFO + filePath: /var/log/traefik/traefik.log + +accessLog: + filePath: /var/log/traefik/access.log + +--- +# Dynamic Configuration Example 1: Docker Compose Labels +# Add these labels to your MCP server container in docker-compose.yml + +version: '3.8' +services: + mcp-server: + image: trufnetwork/postgres-mcp:latest + command: ["--transport=sse", "--sse-host=0.0.0.0", "--access-mode=restricted"] + environment: + - DATABASE_URI=postgresql://kwild@postgres:5432/kwild + networks: + - traefik + labels: + # Enable Traefik for this service + - "traefik.enable=true" + + # Router configuration + - "traefik.http.routers.mcp-server.rule=Host(`mcp.your-domain.com`) && PathPrefix(`/sse`)" + - "traefik.http.routers.mcp-server.entrypoints=websecure" + - "traefik.http.routers.mcp-server.tls=true" + - "traefik.http.routers.mcp-server.tls.certresolver=letsencrypt" + + # Service configuration + - "traefik.http.services.mcp-server.loadbalancer.server.port=8000" + + # SSE-specific middleware + - "traefik.http.routers.mcp-server.middlewares=mcp-sse-headers,mcp-security,mcp-ratelimit" + + # SSE Headers Middleware - CRITICAL for SSE functionality + - "traefik.http.middlewares.mcp-sse-headers.headers.customrequestheaders.Connection=" + - "traefik.http.middlewares.mcp-sse-headers.headers.customrequestheaders.Cache-Control=no-cache" + + # Security Headers Middleware + - "traefik.http.middlewares.mcp-security.headers.frameDeny=true" + - "traefik.http.middlewares.mcp-security.headers.contentTypeNosniff=true" + - "traefik.http.middlewares.mcp-security.headers.browserXssFilter=true" + - "traefik.http.middlewares.mcp-security.headers.stsSeconds=31536000" + - "traefik.http.middlewares.mcp-security.headers.stsIncludeSubdomains=true" + + # CORS Headers (optional for browser clients) + - "traefik.http.middlewares.mcp-security.headers.accesscontrolalloworiginlist=*" + - "traefik.http.middlewares.mcp-security.headers.accesscontrolallowheaders=Origin,X-Requested-With,Content-Type,Accept,Authorization" + - "traefik.http.middlewares.mcp-security.headers.accesscontrolallowmethods=GET,POST,OPTIONS" + + # Rate Limiting Middleware + - "traefik.http.middlewares.mcp-ratelimit.ratelimit.average=10" + - "traefik.http.middlewares.mcp-ratelimit.ratelimit.burst=20" + - "traefik.http.middlewares.mcp-ratelimit.ratelimit.period=1m" + + # IP Whitelist (optional) + # - "traefik.http.middlewares.mcp-ipwhitelist.ipwhitelist.sourcerange=192.168.1.0/24,10.0.0.0/8" + # - "traefik.http.routers.mcp-server.middlewares=mcp-sse-headers,mcp-security,mcp-ratelimit,mcp-ipwhitelist" + + traefik: + image: traefik:v3.0 + container_name: traefik + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "8080:8080" # Dashboard (secure in production) + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik.yml:/etc/traefik/traefik.yml:ro + - ./dynamic:/etc/traefik/dynamic:ro + - ./letsencrypt:/letsencrypt + - ./logs:/var/log/traefik + networks: + - traefik + environment: + - CF_API_EMAIL=your-email@example.com # For Cloudflare DNS challenge + - CF_DNS_API_TOKEN=your-cloudflare-token # For Cloudflare DNS challenge + +networks: + traefik: + external: true + +--- +# Dynamic Configuration Example 2: File-based configuration +# Save as /etc/traefik/dynamic/mcp-server.yml + +http: + routers: + mcp-server: + rule: "Host(`mcp.your-domain.com`) && PathPrefix(`/sse`)" + entryPoints: + - websecure + middlewares: + - mcp-sse-headers + - mcp-security + - mcp-ratelimit + service: mcp-server + tls: + certResolver: letsencrypt + + mcp-server-insecure: + rule: "Host(`mcp.your-domain.com`) && PathPrefix(`/sse`)" + entryPoints: + - web + middlewares: + - redirect-to-https + service: mcp-server + + middlewares: + # CRITICAL: SSE Headers Middleware + mcp-sse-headers: + headers: + customRequestHeaders: + Connection: "" + Cache-Control: "no-cache" + + # Security Headers + mcp-security: + headers: + frameDeny: true + contentTypeNosniff: true + browserXssFilter: true + stsSeconds: 31536000 + stsIncludeSubdomains: true + referrerPolicy: "strict-origin-when-cross-origin" + # CORS (optional) + accessControlAllowOriginList: + - "*" + accessControlAllowHeaders: + - "Origin" + - "X-Requested-With" + - "Content-Type" + - "Accept" + - "Authorization" + accessControlAllowMethods: + - "GET" + - "POST" + - "OPTIONS" + + # Rate Limiting + mcp-ratelimit: + rateLimit: + average: 10 + burst: 20 + period: "1m" + + # IP Whitelist (optional) + mcp-ipwhitelist: + ipWhiteList: + sourceRange: + - "192.168.1.0/24" + - "10.0.0.0/8" + + # HTTPS Redirect + redirect-to-https: + redirectScheme: + scheme: https + permanent: true + + services: + mcp-server: + loadBalancer: + servers: + - url: "http://mcp-server:8000" + healthCheck: + path: /health + interval: 30s + timeout: 5s + +--- +# Production Docker Compose with Traefik +# Complete production setup + +version: '3.8' + +services: + traefik: + image: traefik:v3.0 + container_name: traefik + restart: unless-stopped + command: + - "--configFile=/etc/traefik/traefik.yml" + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./traefik:/etc/traefik:ro + - ./letsencrypt:/letsencrypt + - traefik-logs:/var/log/traefik + networks: + - traefik + environment: + - CF_API_EMAIL=${CF_API_EMAIL} + - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} + labels: + # Dashboard + - "traefik.enable=true" + - "traefik.http.routers.traefik.rule=Host(`traefik.your-domain.com`)" + - "traefik.http.routers.traefik.entrypoints=websecure" + - "traefik.http.routers.traefik.tls=true" + - "traefik.http.routers.traefik.tls.certresolver=letsencrypt" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.middlewares=dashboard-auth" + - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$YOUR_HASHED_PASSWORD_HERE" # Generate with: htpasswd -nb admin your-secure-password + + postgres: + image: kwildb/postgres:latest + restart: unless-stopped + environment: + - POSTGRES_HOST_AUTH_METHOD=trust + volumes: + - postgres-data:/var/lib/postgresql/data + networks: + - internal + + mcp-server: + image: trufnetwork/postgres-mcp:latest + restart: unless-stopped + command: ["--transport=sse", "--sse-host=0.0.0.0", "--access-mode=restricted"] + environment: + - DATABASE_URI=postgresql://kwild@postgres:5432/kwild + networks: + - traefik + - internal + depends_on: + - postgres + labels: + - "traefik.enable=true" + - "traefik.http.routers.mcp.rule=Host(`mcp.your-domain.com`) && PathPrefix(`/sse`)" + - "traefik.http.routers.mcp.entrypoints=websecure" + - "traefik.http.routers.mcp.tls=true" + - "traefik.http.routers.mcp.tls.certresolver=letsencrypt" + - "traefik.http.routers.mcp.middlewares=mcp-sse,mcp-security,mcp-ratelimit" + - "traefik.http.services.mcp.loadbalancer.server.port=8000" + + # SSE Configuration + - "traefik.http.middlewares.mcp-sse.headers.customrequestheaders.Connection=" + - "traefik.http.middlewares.mcp-security.headers.frameDeny=true" + - "traefik.http.middlewares.mcp-security.headers.contentTypeNosniff=true" + - "traefik.http.middlewares.mcp-ratelimit.ratelimit.average=10" + +networks: + traefik: + external: true + internal: + internal: true + +volumes: + postgres-data: + traefik-logs: \ No newline at end of file diff --git a/docs/mcp-reverse-proxy.md b/docs/mcp-reverse-proxy.md new file mode 100644 index 000000000..5a9d0accc --- /dev/null +++ b/docs/mcp-reverse-proxy.md @@ -0,0 +1,653 @@ +# MCP Server Reverse Proxy Configuration + +This guide explains how to configure reverse proxy servers for TRUF.NETWORK's MCP (Model Context Protocol) server when using SSE (Server-Sent Events) transport. + +๐Ÿ’ก **Quick Start**: Ready-to-use configuration files and Docker Compose setups are available in [`examples/mcp-reverse-proxy/`](./examples/mcp-reverse-proxy/). + +## Overview + +The TRUF.NETWORK MCP server supports two transport modes: +- **stdio**: Direct process communication (default, used by Claude Desktop) +- **sse**: Server-Sent Events over HTTP (for remote connections and multi-client support) + +When deploying the MCP server with SSE transport behind a reverse proxy, specific configuration is required to ensure proper streaming behavior. + +## Why Reverse Proxy Configuration Matters + +SSE (Server-Sent Events) is a streaming technology that maintains persistent HTTP connections to deliver real-time data. Unlike regular HTTP requests, SSE requires: + +1. **No buffering** - Data must be streamed immediately, not buffered +2. **Persistent connections** - Long-lived connections that don't timeout prematurely +3. **Proper headers** - Specific HTTP headers for event streaming +4. **HTTP/1.1 support** - Streaming requires HTTP/1.1 features + +## Critical Configuration Requirements + +All reverse proxy configurations must include these essential settings: + +### 1. Disable Proxy Buffering +**Most Important**: Buffering breaks real-time streaming +- Response data must pass through immediately +- No waiting for response completion + +### 2. HTTP/1.1 Support +- Required for streaming connections +- Enables proper chunked transfer encoding + +### 3. Connection Header Management +- Clear or properly manage Connection headers +- Prevent connection pooling interference + +### 4. Disable Caching +- SSE responses should never be cached +- Each event stream is unique and live + +## Server-Specific Configurations + +### Nginx Configuration + +Create a server block with SSE-specific settings: + +```nginx +server { + listen 80; + server_name your-domain.com; + + location /sse { + proxy_pass http://localhost:8000/sse; + + # Essential SSE settings + proxy_set_header Connection ''; + proxy_http_version 1.1; + proxy_buffering off; + proxy_cache off; + chunked_transfer_encoding off; + + # Standard proxy headers + 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; + } +} +``` + +#### HTTPS Configuration + +```nginx +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /path/to/cert.pem; + ssl_certificate_key /path/to/key.pem; + + location /sse { + proxy_pass http://localhost:8000/sse; + + # Essential SSE settings + proxy_set_header Connection ''; + proxy_http_version 1.1; + proxy_buffering off; + proxy_cache off; + chunked_transfer_encoding off; + + # Headers for HTTPS + 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 https; + } +} +``` + +### Caddy Configuration + +Caddy has built-in SSE support with minimal configuration: + +```caddyfile +your-domain.com { + reverse_proxy /sse/* localhost:8000 { + # Caddy handles SSE automatically + # Optional: Add custom headers if needed + header_up Host {http.reverse_proxy.upstream.hostport} + } +} +``` + +#### Advanced Caddy Configuration + +```caddyfile +your-domain.com { + reverse_proxy /sse/* localhost:8000 { + # Disable buffering for SSE + flush_interval -1 + + # Custom headers + header_up Host {http.reverse_proxy.upstream.hostport} + header_up X-Real-IP {http.request.remote} + header_up X-Forwarded-Proto {http.request.scheme} + } +} +``` + +### Traefik Configuration + +#### Docker Compose with Traefik + +```yaml +version: '3.8' +services: + mcp-server: + image: trufnetwork/postgres-mcp:latest + command: ["--transport=sse", "--sse-host=0.0.0.0", "--access-mode=restricted"] + environment: + - DATABASE_URI=postgresql://kwild:password@postgres:5432/kwild + labels: + - "traefik.enable=true" + - "traefik.http.routers.mcp.rule=Host(`your-domain.com`) && PathPrefix(`/sse`)" + - "traefik.http.services.mcp.loadbalancer.server.port=8000" + # SSE-specific labels + - "traefik.http.middlewares.sse-headers.headers.customrequestheaders.Connection=" + - "traefik.http.routers.mcp.middlewares=sse-headers" + + traefik: + image: traefik:v3.0 + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock +``` + +#### Traefik Static Configuration (traefik.yml) + +```yaml +api: + dashboard: true + +entryPoints: + web: + address: ":80" + websecure: + address: ":443" + +providers: + docker: + exposedByDefault: false + +# Optional: Enable HTTPS with Let's Encrypt +certificatesResolvers: + letsencrypt: + acme: + email: admin@your-domain.com + storage: /letsencrypt/acme.json + httpChallenge: + entryPoint: web +``` + +### HAProxy Configuration + +``` +global + daemon + log stdout local0 + +defaults + mode http + timeout connect 5000ms + timeout client 60000ms + timeout server 60000ms + + # Important for SSE + timeout client-fin 30s + timeout server-fin 30s + +frontend mcp_frontend + bind *:80 + acl is_sse path_beg /sse + use_backend mcp_sse if is_sse + +backend mcp_sse + # SSE requires HTTP/1.1 + http-request set-header Connection "" + http-request set-header Host %[req.hdr(host)] + + # Disable buffering for streaming + no option httpchk + option http-server-close + + server mcp1 localhost:8000 check +``` + +### Apache HTTP Server Configuration + +```apache + + ServerName your-domain.com + + + ProxyPass http://localhost:8000/sse + ProxyPassReverse http://localhost:8000/sse + + # Essential for SSE + ProxyPreserveHost On + ProxyRequests Off + + # Disable buffering + SetEnv proxy-nokeepalive 1 + SetEnv proxy-initial-not-pooled 1 + + # Headers + ProxyPassReverse / + ProxyPassReverseRewrite / + + + +# Enable required modules +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_http_module modules/mod_proxy_http.so +LoadModule headers_module modules/mod_headers.so +``` + +## MCP Server SSE Configuration + +**Important**: TRUF.NETWORK uses the Kwil PostgreSQL Docker image (`kwildb/postgres`) which is configured with `POSTGRES_HOST_AUTH_METHOD=trust` and automatically creates a `kwild` database with a `kwild` user. This matches the standard node setup described in the [Node Operator Guide](./node-operator-guide.md). + +### Starting the MCP Server with SSE + +#### Using Docker +```bash +docker run -p 8000:8000 \ + -e DATABASE_URI=postgresql://kwild@localhost:5432/kwild \ + trufnetwork/postgres-mcp \ + --access-mode=restricted \ + --transport=sse \ + --sse-host=0.0.0.0 \ + --sse-port=8000 +``` + +#### Using Python directly +```bash +# Install the TRUF.NETWORK postgres-mcp +git clone https://github.com/trufnetwork/postgres-mcp.git +cd postgres-mcp +./install.sh + +# Run with SSE transport +DATABASE_URI=postgresql://kwild@localhost:5432/kwild \ +postgres-mcp --transport=sse --sse-host=0.0.0.0 --sse-port=8000 --access-mode=restricted +``` + +## Client Configuration + +### MCP Client Configuration for SSE + +Update your MCP client configuration to use the SSE endpoint: + +#### Cursor (mcp.json) +```json +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "url": "http://your-domain.com/sse" + } + } +} +``` + +#### Cline (cline_mcp_settings.json) +```json +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "url": "http://your-domain.com/sse" + } + } +} +``` + +#### Windsurf (mcp_config.json) +```json +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "serverUrl": "http://your-domain.com/sse" + } + } +} +``` + +#### Claude Desktop (claude_desktop_config.json) +**โš ๏ธ Important**: Claude Desktop doesn't support direct SSE connections. You must use `mcp-remote` proxy tool. + +```json +{ + "mcpServers": { + "truf-postgres": { + "command": "npx", + "args": [ + "mcp-remote", + "http://your-domain.com/sse" + ] + } + } +} +``` + +**Configuration Location:** +- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` +- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +- **Linux**: `~/.config/Claude/claude_desktop_config.json` + +## Testing and Validation + +### 1. Test SSE Endpoint Availability + +```bash +# Test basic connectivity +curl -v http://your-domain.com/sse + +# Test with SSE headers +curl -H "Accept: text/event-stream" \ + -H "Cache-Control: no-cache" \ + http://your-domain.com/sse +``` + +### 2. Verify Streaming Behavior + +Check that responses stream immediately without buffering: + +```bash +# Should show immediate response headers +curl -v -N -H "Accept: text/event-stream" http://your-domain.com/sse +``` + +### 3. Test MCP Client Connection + +1. Configure your MCP client with the SSE URL +2. Verify the client can connect and list tools +3. Test executing queries through the SSE transport + +### 4. Monitor Connection Health + +```bash +# Check nginx access logs for SSE requests +tail -f /var/log/nginx/access.log | grep "/sse" + +# Monitor MCP server logs +docker logs -f your-mcp-container +``` + +## Common Issues and Solutions + +### Issue: Connection Timeouts + +**Symptoms**: SSE connections drop after 30-60 seconds +**Solution**: Increase proxy timeouts + +```nginx +# Nginx +location /sse { + proxy_read_timeout 300s; + proxy_send_timeout 300s; + # ... other settings +} +``` + +```caddyfile +# Caddy +reverse_proxy /sse/* localhost:8000 { + timeout 5m +} +``` + +### Issue: Responses Not Streaming + +**Symptoms**: No data until connection closes +**Solution**: Ensure buffering is disabled + +```nginx +# Nginx - Most critical setting +proxy_buffering off; + +# Also verify chunked transfer encoding +chunked_transfer_encoding off; +``` + +### Issue: CORS Errors in Browser + +**Symptoms**: Browser-based clients can't connect +**Solution**: Add CORS headers + +```nginx +# Nginx +location /sse { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + + # Handle preflight requests + if ($request_method = 'OPTIONS') { + return 204; + } + + # ... other proxy settings +} +``` + +### Issue: SSL/TLS Problems + +**Symptoms**: HTTPS connections fail +**Solution**: Verify certificate configuration and headers + +```nginx +# Ensure proper forwarded headers for HTTPS +proxy_set_header X-Forwarded-Proto https; +proxy_set_header X-Forwarded-Port 443; +``` + +### Issue: Database Connection Errors + +**Symptoms**: MCP tools fail with database errors +**Solution**: Verify DATABASE_URI and network connectivity + +```bash +# Test database connectivity from MCP server (TRUF.NETWORK uses kwild user/database) +docker exec truf-mcp-server psql postgresql://kwild@localhost:5432/kwild -c "SELECT version();" + +# Or connect to the TRUF.NETWORK postgres container directly +docker exec -it tn-postgres psql -U kwild -d kwild + +### Issue: Windows IPv4/IPv6 Addressing Problems + +**Symptoms**: Database connection timeouts on Windows, especially with SSH tunnels +**Solution**: Use explicit IPv4 addresses instead of localhost + +```powershell +# Wrong: May resolve to IPv6 (::1) which breaks SSH tunnels +$env:DATABASE_URI = "postgresql://kwild@localhost:5432/kwild" + +# Correct: Force IPv4 addressing +$env:DATABASE_URI = "postgresql://kwild@127.0.0.1:5432/kwild" + +# Start MCP server with IPv4 +postgres-mcp --transport=sse --sse-host=127.0.0.1 --sse-port=8000 + +# Test connectivity +Test-NetConnection -ComputerName localhost -Port 5432 +``` + +### Issue: Claude Desktop Direct SSE Configuration + +**Symptoms**: Claude Desktop shows "Required" errors for missing "command" field +**Solution**: Claude Desktop requires `mcp-remote` proxy for SSE connections + +```json +# Wrong: Direct SSE configuration not supported +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "url": "http://localhost:8000/sse" + } + } +} + +# Correct: Use mcp-remote proxy +{ + "mcpServers": { + "truf-postgres": { + "command": "npx", + "args": ["mcp-remote", "http://127.0.0.1:8000/sse"] + } + } +} +``` + +## Security Considerations + +### 1. Access Control + +```nginx +# Restrict access by IP +location /sse { + allow 192.168.1.0/24; + allow 10.0.0.0/8; + deny all; + + # ... proxy settings +} +``` + +### 2. Rate Limiting + +```nginx +# Nginx rate limiting +http { + limit_req_zone $binary_remote_addr zone=sse:10m rate=10r/m; + + server { + location /sse { + limit_req zone=sse burst=5; + # ... proxy settings + } + } +} +``` + +### 3. Authentication + +Consider implementing authentication at the reverse proxy level: + +```nginx +# Basic auth example +location /sse { + auth_basic "MCP Server Access"; + auth_basic_user_file /etc/nginx/.htpasswd; + + # ... proxy settings +} +``` + +### 4. Network Security + +- Use HTTPS in production +- Implement firewall rules +- Use VPNs or private networks when possible +- Regular security updates for proxy software + +## Production Deployment Example + +Here's a complete production-ready nginx configuration: + +```nginx +# /etc/nginx/sites-available/mcp-server +server { + listen 443 ssl http2; + server_name mcp.your-domain.com; + + # SSL Configuration + ssl_certificate /etc/letsencrypt/live/mcp.your-domain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/mcp.your-domain.com/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + # Security headers + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Rate limiting + limit_req zone=sse burst=10 nodelay; + + # MCP SSE endpoint + location /sse { + # Essential SSE configuration + proxy_pass http://127.0.0.1:8000/sse; + proxy_set_header Connection ''; + proxy_http_version 1.1; + proxy_buffering off; + proxy_cache off; + chunked_transfer_encoding off; + + # Timeouts for long-lived connections + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_connect_timeout 60s; + + # Headers + 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 https; + proxy_set_header X-Forwarded-Port 443; + + # Access control (adjust as needed) + allow 192.168.1.0/24; + deny all; + } + + # Health check endpoint + location /health { + proxy_pass http://127.0.0.1:8000/health; + access_log off; + } +} + +# Redirect HTTP to HTTPS +server { + listen 80; + server_name mcp.your-domain.com; + return 301 https://$host$request_uri; +} + +# Rate limiting zone definition (place in http block) +# limit_req_zone $binary_remote_addr zone=sse:10m rate=5r/m; +``` + +## Configuration Examples + +Ready-to-use configuration files and Docker Compose setups are available in the [`examples/mcp-reverse-proxy/`](./examples/mcp-reverse-proxy/) directory, including: + +- **nginx.conf.example** - Production-ready nginx configuration +- **Caddyfile.example** - Caddy configuration with automatic HTTPS +- **traefik.yml.example** - Traefik static/dynamic configuration +- **docker-compose.sse.yaml** - Complete Docker Compose setup with multiple reverse proxy options + +## Next Steps + +After implementing reverse proxy configuration: + +1. **Monitor Performance**: Track connection counts, response times, and error rates +2. **Set Up Logging**: Configure detailed logging for debugging and monitoring +3. **Implement Monitoring**: Use tools like Grafana/Prometheus for metrics +4. **Plan Scaling**: Consider load balancing for high-traffic scenarios +5. **Security Audits**: Regular security reviews and updates + +For additional support with MCP server deployment, consult the [Node Operator Guide](./node-operator-guide.md) and [MCP Server Documentation](./mcp-server.md). \ No newline at end of file diff --git a/docs/mcp-server.md b/docs/mcp-server.md index cfebbcbd9..208c744ca 100644 --- a/docs/mcp-server.md +++ b/docs/mcp-server.md @@ -41,10 +41,10 @@ python3 -m pipx ensurepath **Windows:** -```bash +```powershell # Install Python 3.12+ from https://www.python.org/downloads/ -# or use chocolatey: -choco install python --version=3.12.0 +# or use winget: +winget install Python.Python.3.12 # Install pipx python -m pip install --user pipx @@ -81,6 +81,66 @@ cd postgres-mcp - `get_index` - Get stream index data - `get_index_change` - Calculate percentage changes +## Deployment Options + +The TRUF.NETWORK MCP server supports two transport modes: + +### 1. Stdio Transport (Default) +Used by Claude Desktop and other local MCP clients that launch processes directly. + +### 2. SSE Transport (Server-Sent Events) +For remote connections, multi-client support, and production deployments. Requires additional configuration when deployed behind reverse proxies. + +**Starting with SSE Transport:** +```bash +# Using Docker (matches TRUF.NETWORK PostgreSQL setup) +docker run -p 8000:8000 \ + -e DATABASE_URI=postgresql://kwild@localhost:5432/kwild \ + trufnetwork/postgres-mcp \ + --access-mode=restricted \ + --transport=sse \ + --sse-host=0.0.0.0 +``` + +**MCP Client Configuration for SSE:** + +**Note**: Claude Desktop doesn't support direct SSE. Use `mcp-remote` proxy: +```json +{ + "mcpServers": { + "truf-postgres": { + "command": "npx", + "args": ["mcp-remote", "http://localhost:8000/sse"] + } + } +} +``` + +**Other MCP clients (Cursor, Cline, Windsurf) with direct SSE support:** +```json +{ + "mcpServers": { + "truf-postgres": { + "type": "sse", + "url": "http://localhost:8000/sse" + } + } +} +``` + +### Reverse Proxy Configuration + +When deploying the MCP server behind a reverse proxy (nginx, Caddy, Traefik, etc.), special configuration is required for SSE transport to work correctly. + +**๐Ÿ“‹ See the complete reverse proxy setup guide:** [MCP Server Reverse Proxy Configuration](./mcp-reverse-proxy.md) + +The guide covers: +- Nginx, Caddy, Traefik, HAProxy, and Apache configurations +- Essential SSE streaming requirements +- Security considerations +- Testing and troubleshooting +- Production deployment examples + ## Troubleshooting **Connection issues:** @@ -92,7 +152,17 @@ cd postgres-mcp **Claude integration:** - Restart Claude Desktop after installation -- Check config file: `~/Library/Application Support/Claude/claude_desktop_config.json` +- Check config file locations: + - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` + - **Linux**: `~/.config/Claude/claude_desktop_config.json` + +**Windows-specific issues:** + +- **IPv4/IPv6 addressing**: Use `127.0.0.1` instead of `localhost` for SSH tunnels + ```powershell + $env:DATABASE_URI = "postgresql://kwild@127.0.0.1:5432/kwild" + ``` ## Uninstall diff --git a/docs/windows-mcp-testing.md b/docs/windows-mcp-testing.md new file mode 100644 index 000000000..c7104384c --- /dev/null +++ b/docs/windows-mcp-testing.md @@ -0,0 +1,404 @@ +# Windows MCP Server + Reverse Proxy Testing Guide + +This guide will help you test the TRUF.NETWORK MCP server **natively on Windows** with reverse proxy configuration, connecting to your AWS database. + +## Prerequisites โœ… + +You already have: +- โœ… TRUF.NETWORK node running on AWS +- โœ… Windows machine +- โœ… Claude Desktop installed on Windows +- โœ… Database with TRUF.NETWORK data populated + +## Required Software + +### Install on Windows (Native): +1. **Python 3.12+** - Download from https://www.python.org/downloads/ +2. **pipx** - For isolated Python package installation +3. **Git** - For cloning the repository +4. **Caddy** - For reverse proxy testing (https://caddyserver.com/download) + +### Install Commands (Windows PowerShell as Administrator): + +```powershell +# Install Python 3.12+ (or download from python.org) +winget install Python.Python.3.12 + +# Install pipx +python -m pip install --user pipx +python -m pipx ensurepath + +# Install Git (if not already installed) +winget install Git.Git + +# Download Caddy from https://caddyserver.com/download +``` + +--- + +## Step 1: Set Up SSH Tunnel (if the node is being set up remotely from local machine) + +### **Windows PowerShell 1: SSH Port Forward** +**Keep this running throughout the entire test!** + +```powershell +ssh -i ~/.ssh/ -L 5432:localhost:5432 @ +``` + +**โœ… Test the tunnel works:** +```powershell +# In another PowerShell window +Test-NetConnection -ComputerName localhost -Port 5432 +# Should return: TcpTestSucceeded: True +``` + +--- + +## Step 2: Install MCP Server Natively on Windows + +### **Windows PowerShell 2: Clone and Install** + +```powershell +# Navigate to your development directory +cd C:\Users\$env:USERNAME + +# Clone the postgres-mcp repository +git clone https://github.com/trufnetwork/postgres-mcp.git +cd postgres-mcp + +# Run the install script +./install.sh +``` + +**During installation, you'll be prompted for:** +- Host: `localhost` (default) +- Port: `5432` (default) +- Database: `kwild` (default) +- Username: `kwild` (default) +- Password: (leave empty - uses trust authentication) + +**โœ… Expected Results:** +- MCP server installed with pipx +- Claude Desktop config created/updated at `%APPDATA%\Claude\claude_desktop_config.json` +- Database connection tested successfully + +--- + +## Step 3: Test Basic MCP Functionality (Stdio Transport) + +### **Windows PowerShell 3: Test MCP Server** + +```powershell +# Test the installed postgres-mcp command +$env:DATABASE_URI = "postgresql://kwild@localhost:5432/kwild" +postgres-mcp --help +``` + +### Test Claude Desktop Integration + +1. **Restart Claude Desktop completely** +2. **Open a new conversation** +3. **Test basic functionality:** + +``` +Can you list the available MCP tools? +``` + +``` +Can you show me what schemas are available in the database? +``` + +**โœ… Expected Results:** +- Claude shows MCP tools available +- Can query main schema and TRUF.NETWORK tables +- Database queries return real data + +--- + +## Step 4: Set Up Reverse Proxy Testing (SSE Transport) + +### **Important IPv4 Configuration Note** +โš ๏ธ **Windows networking may use IPv6 (::1) by default, but SSH tunnels typically work on IPv4 (127.0.0.1). Use IPv4 addresses explicitly to avoid connection timeouts.** + +### **Windows PowerShell 4: Start MCP Server with SSE** + +```powershell +# Start MCP server with SSE transport using IPv4 addressing (keep this running!) +$env:DATABASE_URI = "postgresql://kwild@127.0.0.1:5432/kwild" +postgres-mcp --access-mode=restricted --transport=sse --sse-host=127.0.0.1 --sse-port=8000 +``` + +**โœ… Expected Output:** +``` +Starting PostgreSQL MCP Server in RESTRICTED mode +Successfully connected to database and initialized connection pool +Starting SSE server on 127.0.0.1:8000 +``` + +**โŒ If you see database timeouts:** +- Ensure SSH tunnel is running +- Use IPv4 (127.0.0.1) instead of localhost +- Verify with `Test-NetConnection -ComputerName localhost -Port 5432` + +### **Windows PowerShell 5: Set Up Caddy Reverse Proxy** + +Create Caddyfile: +```powershell +# Create Caddyfile in current directory +@" +:8080 { + reverse_proxy /sse* 127.0.0.1:8000 { + flush_interval -1 + header_up Host {http.reverse_proxy.upstream.hostport} + header_up Connection {>Connection} + header_up Cache-Control {>Cache-Control} + } +} +"@ | Out-File -FilePath "Caddyfile" -Encoding UTF8 +``` + +Start Caddy: +```powershell +# Start Caddy (keep this running!) +caddy run +``` + +**โœ… Expected Output:** +- Caddy starts without errors +- Serving HTTP on :8080 + +--- + +## Step 5: Test Reverse Proxy Endpoints + +### **Windows PowerShell 6: Test Direct and Proxied Connections** + +**โš ๏ธ PowerShell Curl Note**: Use `curl.exe` or `Invoke-WebRequest` to avoid PowerShell's curl alias issues. + +```powershell +# Test direct connection to MCP server +curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8000/sse + +# Test through Caddy reverse proxy +curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8080/sse + +# Alternative using PowerShell native command +Invoke-WebRequest -Uri "http://127.0.0.1:8000/sse" -Headers @{"Accept"="text/event-stream"} +Invoke-WebRequest -Uri "http://127.0.0.1:8080/sse" -Headers @{"Accept"="text/event-stream"} +``` + +**โœ… Expected Results:** +- Both endpoints should respond with SSE headers +- No buffering or delays +- Similar response format + +--- + +## Step 6: Configure Claude Desktop for SSE Transport + +### **Edit Claude Desktop Config for SSE Testing** + +Edit: `%APPDATA%\Claude\claude_desktop_config.json` + +**โš ๏ธ Important**: Claude Desktop doesn't support direct SSE connections. You must use `mcp-remote` proxy tool. + +**Replace with this configuration to test the reverse proxy:** + +```json +{ + "mcpServers": { + "truf-proxy": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8080/sse" + ] + } + } +} +``` + +**๐Ÿ”„ Optional: Test Direct Connection** +To compare performance, you can also test the direct connection separately: +```json +{ + "mcpServers": { + "truf-direct": { + "command": "npx", + "args": [ + "mcp-remote", + "http://127.0.0.1:8000/sse" + ] + } + } +} +``` + +**โš ๏ธ Note:** Don't enable both direct and proxy connections simultaneously as this can cause conflicts. Test them one at a time. + +**Save the file and completely restart Claude Desktop!** + +--- + +## Step 7: Test SSE Transport in Claude Desktop + +### **Test Both Configurations** + +1. **Close Claude Desktop completely** +2. **Start Claude Desktop again** +3. **Open a new conversation** +4. **Test connection:** + +``` +Can you list the available MCP tools? +``` + +**โœ… Expected Results:** +- Claude should show MCP tools from the configured server +- Database queries work through the reverse proxy +- Performance should be similar to direct connection + +--- + +## Terminal Layout Summary + +**Windows PowerShell Windows:** +1. **PowerShell 1**: SSH tunnel to AWS (keep running) +2. **PowerShell 2**: Installation and testing (can close after setup) +3. **PowerShell 3**: Basic stdio testing (can close after testing) +4. **PowerShell 4**: MCP server with SSE transport (keep running) +5. **PowerShell 5**: Caddy reverse proxy (keep running) +6. **PowerShell 6**: Testing endpoints (can close after testing) + +**Windows Applications:** +- **Claude Desktop**: Configured for SSE transport testing + +--- + +## Troubleshooting + +### **Common Issues** + +**1. Python/pipx not found:** +```powershell +# Add Python to PATH +$env:PATH += ";C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312" +refreshenv +``` + +**2. postgres-mcp command not found:** +```powershell +# Add pipx binaries to PATH +$env:PATH += ";C:\Users\$env:USERNAME\AppData\Roaming\Python\Python312\Scripts" +refreshenv +``` + +**3. SSH tunnel disconnected:** +```powershell +# Check if tunnel is still active +netstat -an | findstr :5432 +``` + +**4. Database connection timeouts:** +```powershell +# Force IPv4 addressing - most common fix +$env:DATABASE_URI = "postgresql://kwild@127.0.0.1:5432/kwild" + +# Check connectivity +Test-NetConnection -ComputerName localhost -Port 5432 + +# Should return: TcpTestSucceeded: True +``` + +**5. PowerShell curl header errors:** +```powershell +# Wrong (PowerShell alias issue): +curl -H "Accept: text/event-stream" http://localhost:8000/sse + +# Correct options: +curl.exe -H "Accept: text/event-stream" http://127.0.0.1:8000/sse +Invoke-WebRequest -Uri "http://127.0.0.1:8000/sse" -Headers @{"Accept"="text/event-stream"} +``` + +**6. Caddy port conflict:** +```powershell +# Check what's using port 8080 +netstat -ano | findstr :8080 +``` + +**7. Claude Desktop SSE connection issues:** +- Verify both MCP server (port 8000) and Caddy (port 8080) are running +- Check Windows Firewall isn't blocking connections +- Test direct connection first to ensure MCP server works +- Don't enable both direct and proxy connections simultaneously +- Use `npx mcp-remote` instead of direct SSE configuration + +**8. Claude Desktop "command" field Required errors:** +- Claude Desktop requires the `command` field for all MCP server configurations +- Direct SSE configuration is not supported - must use `mcp-remote` proxy + +### **Debug Commands** + +```powershell +# Check running processes +Get-Process | Where-Object {$_.ProcessName -match "postgres-mcp|caddy"} + +# Test port connectivity +Test-NetConnection -ComputerName 127.0.0.1 -Port 8000 +Test-NetConnection -ComputerName 127.0.0.1 -Port 8080 + +# Check services listening +netstat -an | findstr :8000 +netstat -an | findstr :8080 +``` + +--- + +## Expected Results โœ… + +**โœ… Success indicators:** +- MCP server installs natively on Windows with pipx +- Basic stdio transport works with Claude Desktop +- SSE transport starts on port 8000 with IPv4 addressing +- Caddy reverse proxy runs on port 8080 +- Proxied endpoint (8080) responds correctly +- Claude Desktop connects via SSE transport through mcp-remote proxy +- Database queries work through the reverse proxy connection +- No performance degradation through reverse proxy +- Streaming behavior works properly (no buffering) + +**โŒ Failure indicators:** +- Installation errors with pipx or Python +- Claude Desktop shows no MCP tools +- SSE endpoints return errors or timeouts +- Database connection timeout errors (usually IPv6/IPv4 issue) +- Reverse proxy buffering causes delays +- PowerShell curl header syntax errors + +--- + +## Key Differences from Linux/Docker Deployment + +### Windows-Specific Considerations + +1. **IPv4/IPv6 Addressing**: Windows may default to IPv6 (`::1`) while SSH tunnels work on IPv4 (`127.0.0.1`). Always use explicit IPv4 addresses. + +2. **PowerShell curl Alias**: Use `curl.exe` or `Invoke-WebRequest` instead of PowerShell's `curl` alias to avoid header parameter issues. + +3. **PATH Management**: Python and pipx binaries may need manual PATH configuration. + +4. **Claude Desktop Config Location**: `%APPDATA%\Claude\claude_desktop_config.json` on Windows vs `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS. + +5. **mcp-remote Requirement**: Claude Desktop requires `npx mcp-remote` proxy for SSE connections - direct SSE configuration is not supported. + +### Production Deployment Considerations + +Once Windows testing works: + +1. **Deploy to production server** with proper domain names +2. **Add HTTPS certificates** +3. **Test with multiple Claude Desktop clients** simultaneously +4. **Benchmark performance** under load +5. **Document production deployment** procedures + +This testing validates the complete TRUF.NETWORK MCP server reverse proxy functionality on Windows! \ No newline at end of file