Skip to main content
Glama
joelmnz

Article Manager MCP Server

by joelmnz
nginx-subpath-deployment.md14.8 kB
# Nginx Subpath Deployment Guide This guide explains how to deploy the MCP Markdown Manager behind Nginx on a subpath (e.g., `/md`, `/articles`) using the runtime base path configuration feature. ## Overview The application supports runtime base path configuration, allowing the same built frontend assets to work with different deployment paths without rebuilding. This is achieved through: - **Runtime Configuration**: Base path is set via environment variables and injected into the frontend at request time - **Dynamic URL Generation**: All URLs (API calls, navigation, assets) are constructed using runtime configuration - **Deployment Flexibility**: Same Docker image works across different environments and paths ## Runtime Configuration Approach ### Key Benefits 1. **Build Once, Deploy Anywhere**: Same frontend assets work with any base path 2. **Container Portability**: Same Docker image for root path and subpath deployments 3. **Environment Flexibility**: Configure base path through environment variables only 4. **No Rebuild Required**: Change deployment path without rebuilding frontend ### How It Works ``` Environment Variables → Server Configuration → HTML Template Injection → Frontend Runtime Config → All URL Generation ``` 1. **Environment**: Set `BASE_URL` or `BASE_PATH` environment variable 2. **Server**: Parses and validates base path configuration 3. **Template**: Injects base path into HTML template at request time 4. **Frontend**: Reads injected configuration and uses for all URL generation 5. **URLs**: All navigation, API calls, and assets use runtime base path ## Environment Variable Configuration ### BASE_URL vs BASE_PATH **BASE_URL** (Recommended for production): ```bash # Full URL including protocol and domain BASE_URL=https://example.com/md BASE_URL=https://mysite.org/articles BASE_URL=https://docs.company.com/wiki ``` **BASE_PATH** (Path only): ```bash # Path portion only BASE_PATH=/md BASE_PATH=/articles BASE_PATH=/app/docs ``` **Priority**: `BASE_URL` takes precedence if both are set. The path portion is extracted from `BASE_URL`. ### Path Normalization The system automatically normalizes base paths: ```bash # These all become "/md" BASE_PATH=md BASE_PATH=/md BASE_PATH=md/ BASE_PATH=/md/ # These all become "/app/docs" BASE_PATH=app/docs BASE_PATH=/app/docs BASE_PATH=app/docs/ BASE_PATH=/app/docs/ ``` ### Default Behavior ```bash # Root path deployment (default) # BASE_URL= # BASE_PATH= # (or leave unset) ``` ## Nginx Configuration Examples ### Basic Subpath Configuration ```nginx server { listen 80; server_name example.com; # Serve MCP Markdown Manager on /md subpath location /md/ { # Remove /md prefix when forwarding to backend rewrite ^/md/(.*)$ /$1 break; proxy_pass http://localhost:5000; 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; # Important: Set base path for the application proxy_set_header X-Base-Path /md; } # Handle exact /md redirect location = /md { return 301 /md/; } } ``` ### Advanced Configuration with SSL ```nginx server { listen 443 ssl http2; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; # Serve MCP Markdown Manager on /articles subpath location /articles/ { # Remove /articles prefix when forwarding to backend rewrite ^/articles/(.*)$ /$1 break; proxy_pass http://localhost:5000; 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; proxy_set_header X-Base-Path /articles; # WebSocket support for potential future features proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Handle exact /articles redirect location = /articles { return 301 /articles/; } # Optional: Serve other applications on different paths location /api/ { proxy_pass http://localhost:3000; # ... other API configuration } } ``` ### Multiple Applications Configuration ```nginx server { listen 80; server_name myserver.local; # MCP Markdown Manager on /md location /md/ { rewrite ^/md/(.*)$ /$1 break; proxy_pass http://localhost:5000; 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; proxy_set_header X-Base-Path /md; } location = /md { return 301 /md/; } # Another app on /wiki location /wiki/ { rewrite ^/wiki/(.*)$ /$1 break; proxy_pass http://localhost:8080; # ... wiki app configuration } location = /wiki { return 301 /wiki/; } # Default app at root location / { proxy_pass http://localhost:3000; # ... default app configuration } } ``` ### Docker Compose with Nginx ```nginx # nginx.conf events { worker_connections 1024; } http { upstream mcp-markdown { server mcp-markdown-manager:5000; } server { listen 80; server_name localhost; location /md/ { rewrite ^/md/(.*)$ /$1 break; proxy_pass http://mcp-markdown; 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; proxy_set_header X-Base-Path /md; } location = /md { return 301 /md/; } } } ``` ## Docker Deployment Examples ### Docker Compose with Subpath Create `docker-compose.subpath.yml`: ```yaml version: '3.8' services: nginx: image: nginx:alpine container_name: nginx-proxy ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - mcp-markdown-manager restart: unless-stopped postgres: image: pgvector/pgvector:pg16 container_name: mcp-markdown-postgres environment: - POSTGRES_DB=article_manager - POSTGRES_USER=article_user - POSTGRES_PASSWORD=${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U article_user -d article_manager"] interval: 10s timeout: 5s retries: 5 mcp-markdown-manager: image: ghcr.io/joelmnz/mcp-markdown-manager:latest container_name: mcp-markdown-manager environment: - AUTH_TOKEN=${AUTH_TOKEN} - DATA_DIR=/data - PORT=5000 - NODE_ENV=production - MCP_SERVER_ENABLED=true # Runtime base path configuration - BASE_URL=http://localhost/md # Alternative: BASE_PATH=/md # Database configuration - DB_HOST=postgres - DB_PORT=5432 - DB_NAME=article_manager - DB_USER=article_user - DB_PASSWORD=${DB_PASSWORD} - DB_SSL=false volumes: - ./data:/data depends_on: postgres: condition: service_healthy restart: unless-stopped # Note: No external port mapping - accessed through nginx volumes: postgres_data: driver: local ``` ### Environment Configuration for Subpath Create `.env` file: ```bash # Authentication AUTH_TOKEN=your-secret-token-here DB_PASSWORD=your-database-password-here # Base path configuration for /md subpath BASE_URL=http://localhost/md # Alternative path-only configuration: # BASE_PATH=/md ``` ### Deployment Commands ```bash # Deploy with subpath configuration docker-compose -f docker-compose.subpath.yml up -d # Check logs docker-compose -f docker-compose.subpath.yml logs -f # Access application # http://localhost/md ``` ## Production Deployment Examples ### Production with SSL Create `docker-compose.production.yml`: ```yaml version: '3.8' services: nginx: image: nginx:alpine container_name: nginx-proxy ports: - "80:80" - "443:443" volumes: - ./nginx-ssl.conf:/etc/nginx/nginx.conf:ro - ./ssl:/etc/nginx/ssl:ro depends_on: - mcp-markdown-manager restart: unless-stopped mcp-markdown-manager: image: ghcr.io/joelmnz/mcp-markdown-manager:latest container_name: mcp-markdown-manager environment: - AUTH_TOKEN=${AUTH_TOKEN} - DATA_DIR=/data - PORT=5000 - NODE_ENV=production - MCP_SERVER_ENABLED=true # Production base URL with HTTPS - BASE_URL=https://example.com/md # Database configuration - DB_HOST=postgres - DB_PORT=5432 - DB_NAME=article_manager - DB_USER=article_user - DB_PASSWORD=${DB_PASSWORD} - DB_SSL=false volumes: - ./data:/data depends_on: postgres: condition: service_healthy restart: unless-stopped postgres: # ... same as above ``` ### Environment for Production ```bash # .env.production AUTH_TOKEN=production-secret-token DB_PASSWORD=production-database-password # Production base URL BASE_URL=https://example.com/md ``` ## Testing Subpath Deployment ### 1. Verify Base Path Configuration Check application startup logs: ```bash docker-compose logs mcp-markdown-manager | grep -i "base" ``` Expected output: ``` Base path configured: /md Base URL: https://example.com/md ``` ### 2. Test Frontend URLs Access the application and verify: - **Main page**: `https://example.com/md/` loads correctly - **Navigation**: All internal links maintain `/md` prefix - **API calls**: Browser network tab shows API calls to `/md/api/*` - **Assets**: CSS/JS files load from `/md/App.[hash].js` ### 3. Test API Endpoints ```bash # Test API with base path curl -H "Authorization: Bearer your-token" \ https://example.com/md/api/articles # Test MCP endpoint curl -H "Authorization: Bearer your-token" \ -H "Content-Type: application/json" \ -d '{"method":"tools/list"}' \ https://example.com/md/mcp ``` ### 4. Test Service Worker Check browser developer tools: 1. **Application tab** → **Service Workers** 2. Verify service worker is registered for `/md/` scope 3. **Cache Storage** should show cached resources with `/md/` prefix ## Troubleshooting ### Common Issues **1. Assets not loading (404 errors)** - Check nginx rewrite rule removes base path correctly - Verify `BASE_URL`/`BASE_PATH` environment variable is set - Check application logs for base path configuration **2. API calls failing** - Ensure nginx forwards API requests to backend - Verify `Authorization` header is passed through - Check that API calls include base path prefix **3. Navigation broken** - Verify frontend runtime configuration is loaded - Check browser console for JavaScript errors - Ensure all navigation uses runtime base path **4. Service worker issues** - Clear browser cache and service worker - Check service worker registration scope - Verify cached resources use correct base path ### Debug Commands ```bash # Check application configuration docker exec mcp-markdown-manager printenv | grep -E "(BASE_|URL)" # Test nginx configuration docker exec nginx-proxy nginx -t # Check application health curl http://localhost/md/health # View application logs docker-compose logs -f mcp-markdown-manager ``` ### Configuration Validation The application validates base path configuration on startup: ```bash # Valid configurations BASE_PATH=/md # ✓ Normalized to /md BASE_PATH=articles # ✓ Normalized to /articles BASE_URL=https://example.com/md # ✓ Path extracted: /md # Invalid configurations (fallback to root) BASE_PATH=../invalid # ✗ Invalid characters BASE_PATH=//double # ✗ Double slashes ``` ## Migration from Root Path If migrating from root path deployment to subpath: ### 1. Update Environment ```bash # Add base path configuration echo "BASE_URL=https://example.com/md" >> .env ``` ### 2. Update Nginx Configuration Add subpath location block and rewrite rules. ### 3. Update Bookmarks/Links Update any saved bookmarks or external links: - `https://example.com/` → `https://example.com/md/` ### 4. Test Deployment 1. Deploy with new configuration 2. Verify all functionality works 3. Update documentation/links ## Best Practices ### 1. Environment Configuration - Use `BASE_URL` for production (includes protocol/domain) - Use `BASE_PATH` for development/testing - Always include trailing slash in nginx location blocks - Set environment variables in docker-compose.yml ### 2. Nginx Configuration - Use rewrite rules to remove base path before forwarding - Set appropriate proxy headers - Include exact location redirects (e.g., `/md` → `/md/`) - Test configuration with `nginx -t` ### 3. Deployment - Test in development environment first - Use same Docker image for all environments - Monitor application logs during deployment - Verify all features work after deployment ### 4. Monitoring - Check application startup logs for base path configuration - Monitor nginx access logs for 404 errors - Test API endpoints and MCP functionality - Verify service worker registration and caching ## Security Considerations ### 1. Proxy Headers Always set security headers in nginx: ```nginx 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; ``` ### 2. SSL Configuration For production deployments: ```nginx # Force HTTPS if ($scheme != "https") { return 301 https://$host$request_uri; } # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; ``` ### 3. Access Control Restrict access if needed: ```nginx # IP-based access control location /md/ { allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; # ... proxy configuration } ``` This guide provides comprehensive instructions for deploying the MCP Markdown Manager on nginx subpaths using the runtime configuration approach, ensuring flexibility and maintainability across different deployment scenarios. </content>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/joelmnz/mcp-markdown-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server