Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Architectural Proposal: Separate Gateway and Registry Containers #170

Open
Assignees
aarora79
Labels
architectureArchitecture and design decisionsenhancementNew feature or request
Milestone
@aarora79

Description

@aarora79

Architectural Proposal: Separate Gateway and Registry Containers

Overview

This document provides a detailed plan for separating the NGINX gateway and Registry application into two separate containers. This will enable organizations to use their own API gateways while still leveraging the MCP Gateway Registry infrastructure.

Problem Statement

Currently, NGINX and the Registry application are bundled in the SAME container (docker/Dockerfile.registry). This tight coupling prevents organizations from:

  1. Using their own existing API gateway (AWS API Gateway, Kong, Apigee, Traefik, etc.)
  2. Deploying registry-only without unused NGINX overhead
  3. Scaling gateway and registry independently
  4. Integrating with existing enterprise infrastructure

Current Architecture

What Exists Today

ONE container contains BOTH NGINX and Registry:

docker/Dockerfile.registry builds:┌─────────────────────────────────────────┐│  Registry Container (ONE big container) │├─────────────────────────────────────────┤│                                         ││  1. NGINX Server (running on 80/443)   ││     • nginx binary                      ││     • nginx.conf file                   ││     • Routes traffic                    ││                                         ││  2. FastAPI App (running on 7860)      ││     • registry/main.py                  ││     • Serves Web UI                     ││     • Manages servers                   ││                                         ││  3. Python Code that manages NGINX     ││     • registry/core/nginx_service.py   ││     • Generates nginx.conf dynamically ││     • Reloads NGINX when servers change││                                         │└─────────────────────────────────────────┘

The Problem: Who Controls NGINX Config?

Currently: The Registry Python code (registry/core/nginx_service.py) generates and controls NGINX configuration:

  • When you add/remove/toggle a server in the registry Web UI
  • Registry Python code rewritesnginx.conf file
  • Registry Python code reloads NGINX
  • This violates separation of concerns - the registry shouldn't manage the gateway

Architecture Diagram

                              CLIENTS   Human Developer (Web Browser)    AI Agent/Assistant (MCP Protocol)              │                                      │              │  HTTPS (Web UI)    MCP (Streamable HTTP/SSE)              └──────────────┬───────────────────────┘                             │                             ▼         ╔═══════════════════════════════════════════════════╗         ║  REGISTRY CONTAINER (ONE CONTAINER - PROBLEM!)   ║         ╠═══════════════════════════════════════════════════╣         ║                                                   ║         ║  NGINX (80/443)                                   ║         ║    • SSL/TLS termination                          ║         ║    • Reverse proxy                                ║         ║    • Auth validation                              ║         ║    │                                              ║         ║    │ Internal communication                       ║         ║    ▼                                              ║         ║  FastAPI (7860)                                   ║         ║    • Web UI                                       ║         ║    • Server management                            ║         ║    • Tool discovery                               ║         ║                                                   ║         ╚═══════════════════════════════════════════════════╝                 │           │              │                 │           │              │                 ▼           ▼              ▼         Auth Server   MCP Servers   Metrics Service           (8888)      (8001, etc.)      (8890)

Proposed Architecture

The Solution: Two Separate Containers

Split the single container into TWO containers:

┌──────────────────────────────────┐│  Gateway Container (NEW)         │├──────────────────────────────────┤│                                  ││  1. NGINX Server (80/443)        ││     • nginx binary               ││     • nginx.conf file            ││     • Routes traffic             ││                                  ││  2. Python Code                  ││     • gateway/config_generator.py││     • Watches registry API       ││     • Generates nginx.conf       ││     • Reloads NGINX              ││                                  │└──────────────────────────────────┘┌──────────────────────────────────┐│  Registry Container (CLEANED UP) │├──────────────────────────────────┤│                                  ││  1. FastAPI App ONLY (7860)      ││     • registry/main.py           ││     • Serves Web UI              ││     • Manages servers            ││     • NO NGINX code!             ││                                  │└──────────────────────────────────┘

Who Controls NGINX Config Now?

After separation:

  • Gateway container controls its own NGINX configuration
  • Registry container just provides API endpoints - doesn't touch NGINX at all
  • Gateway watches registry API and updates its own config when it detects changes

How Does Gateway Know About Server Changes?

Gateway usespolling (checking registry API periodically):

# Inside gateway container, gateway/main.py runs:whileTrue:# Poll registry APIservers=requests.get("http://registry:7860/api/servers").json()# Regenerate NGINX config if servers changedgenerate_nginx_config(servers)# Reload NGINXreload_nginx()# Wait 30 seconds before checking againsleep(30)

Why polling? Simple, reliable, and doesn't require registry to know about gateway.

Architecture Diagram

                              CLIENTS   Human Developer (Web Browser)    AI Agent/Assistant (MCP Protocol)              │                                      │              │  HTTPS (Web UI)    MCP (Streamable HTTP/SSE)              └──────────────┬───────────────────────┘                             │                             ▼         ┌────────────────────────────────────────────────┐         │  GATEWAY CONTAINER (NEW - Separate)            │         ├────────────────────────────────────────────────┤         │                                                │         │  NGINX (80/443)                                │         │    • SSL/TLS termination                       │         │    • Reverse proxy                             │         │    • Auth validation                           │         │                                                │         └──────┬────────────────┬───────────────┬────────┘                │                │               │                │                │               │                ▼                ▼               ▼         ┌────────────┐   ┌──────────────┐   ┌─────────────┐         │ Auth Server│   │  Registry    │   │ MCP Servers │         │  (8888)    │   │  (7860)      │   │  (8001...)  │         │            │   │              │   └─────────────┘         │            │   │ FastAPI only │         │            │   │  • Web UI    │         │            │   │  • REST APIs │         │            │   │  • Discovery │         └────────────┘   └──────────────┘

Benefits

Why Separate?

  1. Enable Enterprise Adoption

    • Organizations can use their existing AWS API Gateway, Kong, Apigee, etc.
    • Deploy only the registry stack (registry + auth + metrics + keycloak)
    • Skip our NGINX gateway entirely if they have their own
  2. Independent Scaling

    • Scale gateway and registry separately based on load
    • Deploy multiple gateway containers in different regions → single centralized registry
  3. Resource Efficiency

    • Don't run unused NGINX if customer has their own gateway
    • Smaller container images (each container has only what it needs)
  4. Better Architecture

    • Single responsibility principle - each container does one thing
    • All other services already separated (auth-server, metrics-service, keycloak)
    • This completes the microservices architecture

What Stays the Same

  • ✅ Default deployment still includes NGINX gateway (no breaking changes)
  • ✅ Same reverse proxy pattern (protocol independence preserved)
  • ✅ Same auth flow, same routing, same functionality
  • ✅ Existing users can continue as before
  • ✅ Just split into two containers instead of one

Deployment Modes

Mode 1: Full Mode (Gateway + Registry) - DEFAULT

What starts:

  • ✅ Gateway container (NGINX + Python)
  • ✅ Registry container (FastAPI)
  • ✅ Auth-server
  • ✅ Metrics-service
  • ✅ Keycloak
  • ✅ MCP servers (fininfo, mcpgw, currenttime, etc.)

What the Gateway does:

  1. Serves as reverse proxy toRegistry Web UI (port 7860)
  2. Serves as reverse proxy toMCP servers (ports 8001, 8003, etc.)
  3. Serves as reverse proxy toAuth server (port 8888)
  4. SSL/TLS termination
  5. Auth validation for all requests

User Access:

  • User goes tohttps://your-domain.com → NGINX gateway → proxies to registry:7860 → Web UI
  • AI agent callshttps://your-domain.com/fininfo/ → NGINX gateway → proxies to fininfo-server:8001

Who uses this: Most users who want everything to work out of the box


Mode 2: Registry-Only Mode (No Gateway)

What starts:

  • ❌ NO Gateway container
  • ✅ Registry container (FastAPI) - DIRECTLY exposes port 7860
  • ✅ Auth-server
  • ✅ Metrics-service
  • ✅ Keycloak
  • ✅ MCP servers (customer's own gateway routes to these)

What the Registry does:

  • FastAPI serves Web UI directly on port 7860 (no NGINX in front)
  • Provides REST APIs on port 7860
  • Doesn't know about gateway at all

User Access:

  • User goes tohttp://registry-host:7860 → DIRECTLY to FastAPI Web UI (no NGINX)
  • OR customer sets up their own gateway:
    • Customer's gateway (AWS API GW, Kong, etc.) → proxies to registry:7860
    • Customer's gateway → proxies to fininfo-server:8001, mcpgw-server:8003, etc.

Who uses this: Enterprises with existing API gateway infrastructure (AWS API Gateway, Kong, Apigee, etc.)


Mode 3: Standalone Registry (No Gateway, No MCP Servers)

What starts:

  • ❌ NO Gateway container
  • ✅ Registry container (FastAPI) - port 7860
  • ✅ Auth-server
  • ✅ Metrics-service
  • ✅ Keycloak
  • ❌ NO MCP servers (or they run somewhere else)

What the Registry does:

  • Just provides Web UI for managing server definitions
  • Server definitions point to MCP servers running elsewhere
  • Pure management/discovery layer

User Access:

  • User goes tohttp://registry-host:7860 directly to manage servers
  • MCP servers run on other hosts, registry just stores their URLs

Who uses this: Organizations that want centralized registry but run MCP servers on different infrastructure


Key Question: Does Registry Container Include the Web UI Frontend?

YES! The registry container includes the Web UI:

registry/├── main.py              # FastAPI serves the Web UI└── ...frontend/build/          # React Web UI (HTML/CSS/JS)├── index.html├── static/└── ...

Inregistry/main.py:

# Serve React static filesFRONTEND_BUILD_PATH=Path(__file__).parent.parent/"frontend"/"build"ifFRONTEND_BUILD_PATH.exists():# Serve static assetsapp.mount("/static",StaticFiles(directory=FRONTEND_BUILD_PATH/"static"),name="static")# Serve React app for all other routes (SPA)@app.get("/{full_path:path}")asyncdefserve_react_app(full_path:str):returnFileResponse(FRONTEND_BUILD_PATH/"index.html")

So the FastAPI app SERVES the Web UI directly - no NGINX needed for the UI!


What Does Gateway NGINX Actually Do?

The gateway NGINX provides:

  1. SSL/TLS termination - HTTPS instead of HTTP
  2. Single entry point - One domain for everything
  3. Auth validation - Validates ALL requests before they reach backends
  4. Routing - Routes to registry, auth, MCP servers based on path
  5. Protocol handling - SSE, WebSocket support for MCP protocol

But if you don't need these features:

  • You can access registry directly on port 7860 (HTTP, no SSL)
  • You can use your own gateway for SSL/auth/routing

Summary

ModeGateway ContainerRegistry Serves Web UIMCP ServersUse Case
Full✅ Runs (NGINX)✅ Via gateway proxy✅ LocalDefault deployment
Registry-Only❌ Not started✅ Direct (port 7860)✅ Customer routes to themEnterprise with own gateway
Standalone Registry❌ Not started✅ Direct (port 7860)❌ Run elsewhereCentralized management only

Bottom line: The registry container ALWAYS serves the Web UI (via FastAPI). The gateway is OPTIONAL and just adds SSL/auth/routing on top.

New Project Structure

Directory Layout

mcp-gateway-registry/├── gateway/                          # NEW FOLDER: All gateway code│   ├── config_generator.py          # Generates nginx.conf (moved from registry/)│   ├── config.py                     # Gateway-specific settings│   ├── templates/│   │   ├── nginx_http_https.conf    # NGINX template files│   │   └── nginx_http_only.conf│   ├── main.py                       # Python service that watches registry│   └── requirements.txt              # Python deps (requests, etc.)│├── registry/                         # CLEANED UP: No gateway code│   ├── core/│   │   └── nginx_service.py         # DELETE THIS FILE│   ├── main.py                       # REMOVE nginx operations (lines 115-120)│   └── ...                           # Everything else stays same│├── docker/│   ├── Dockerfile.gateway            # NEW: Builds gateway container│   │                                 # Contains: NGINX + Python + gateway/ code│   ├── Dockerfile.registry-only      # NEW: Builds registry container│   │                                 # Contains: ONLY Python + registry/ code│   ├── start-gateway.sh              # NEW: Starts both NGINX and Python in gateway│   └── Dockerfile.registry           # KEEP for backward compatibility (optional)│└── docker-compose.yml                # UPDATED: Two separate services

Implementation Plan

Phase 1: Create Gateway Folder and Move Code

Step 1.1: Create gateway folder structure

mkdir -p gateway/templates

Step 1.2: Move NGINX-related code

# Move nginx_service.py to gateway folder (and rename it)mv registry/core/nginx_service.py gateway/config_generator.py# Move NGINX templates to gateway foldermv docker/nginx_rev_proxy_http_and_https.conf gateway/templates/mv docker/nginx_rev_proxy_http_only.conf gateway/templates/

Step 1.3: Create gateway/main.py

This file polls the registry API and updates NGINX config:

#!/usr/bin/env python3"""Gateway Configuration ServiceWatches registry for server changes and updates NGINX config"""importasyncioimportloggingimporthttpxfrom .config_generatorimportNginxConfigGeneratorlogging.basicConfig(level=logging.INFO,format="%(asctime)s,p%(process)s,{%(filename)s:%(lineno)d},%(levelname)s,%(message)s",)logger=logging.getLogger(__name__)# Registry API URLREGISTRY_API_URL="http://registry:7860/api/servers"# Poll interval in secondsPOLL_INTERVAL=30asyncdefwatch_registry():"""Poll registry API for server changes and regenerate NGINX config"""nginx_generator=NginxConfigGenerator()whileTrue:try:logger.info(f"Polling registry API:{REGISTRY_API_URL}")# Get server list from registryasyncwithhttpx.AsyncClient()asclient:response=awaitclient.get(REGISTRY_API_URL)response.raise_for_status()servers_data=response.json()# Extract enabled serversenabled_servers= {path:infoforpath,infoinservers_data.items()ifinfo.get("enabled",False)            }logger.info(f"Found{len(enabled_servers)} enabled servers")# Generate NGINX configsuccess=awaitnginx_generator.generate_config_async(enabled_servers)ifsuccess:logger.info("NGINX configuration updated successfully")else:logger.error("Failed to update NGINX configuration")exceptExceptionase:logger.error(f"Error polling registry:{e}",exc_info=True)# Wait before next pollawaitasyncio.sleep(POLL_INTERVAL)if__name__=="__main__":logger.info("Starting Gateway Configuration Service")logger.info(f"Polling interval:{POLL_INTERVAL} seconds")asyncio.run(watch_registry())

Step 1.4: Create gateway/init.py

Make gateway a proper Python module:

"""Gateway configuration service for MCP Gateway Registry"""

Step 1.5: Create gateway/requirements.txt

httpx>=0.24.0

Step 1.6: Create gateway/config.py

This file handles gateway settings and reads from environment variables:

importosclassGatewaySettings:"""Gateway configuration settings"""# Registry API endpoint to poll for server listregistry_api_url:str=os.getenv("REGISTRY_API_URL","http://registry:7860/api/servers"    )# How often to poll registry (seconds)poll_interval:int=int(os.getenv("POLL_INTERVAL","30"))# NGINX config pathsnginx_config_dir:str="/etc/nginx"nginx_templates_dir:str="/etc/nginx/templates"settings=GatewaySettings()

Step 1.7: Update gateway/main.py to use environment variables

Change the hardcoded values to use settings:

importosfromgateway.configimportsettings# Use settings instead of hardcoded valuesREGISTRY_API_URL=settings.registry_api_urlPOLL_INTERVAL=settings.poll_interval

Step 1.8: Update gateway/config_generator.py

After moving from registry/ to gateway/, update the file:

  1. Update imports: Change any cross-module imports
# OLD (when in registry/core/):fromregistry.constantsimportNGINX_CONFIG_DIR# NEW (now in gateway/):fromgateway.configimportsettings
  1. Update config paths: Make sure NGINX config generation points to correct locations
# Use gateway settings for pathsconfig_path=f"{settings.nginx_config_dir}/nginx.conf"template_path=f"{settings.nginx_templates_dir}/nginx_http_https.conf"
  1. Handle missing templates gracefully: Add error handling
importosfrompathlibimportPathdef_load_template(self,template_name:str)->str:"""Load NGINX template file"""template_path=Path(settings.nginx_templates_dir)/template_nameifnottemplate_path.exists():logger.error(f"Template not found:{template_path}")raiseFileNotFoundError(f"NGINX template not found:{template_name}")withopen(template_path,'r')asf:returnf.read()

Step 1.9: Add error handling and startup logic

Updategateway/main.py to handle errors gracefully:

asyncdefwatch_registry():"""Poll registry API for server changes and regenerate NGINX config"""nginx_generator=NginxConfigGenerator()# Wait for registry to be available before startingawait_wait_for_registry()whileTrue:try:logger.info(f"Polling registry API:{REGISTRY_API_URL}")# Get server list from registryasyncwithhttpx.AsyncClient(timeout=10.0)asclient:response=awaitclient.get(REGISTRY_API_URL)response.raise_for_status()servers_data=response.json()# Extract enabled serversenabled_servers= {path:infoforpath,infoinservers_data.items()ifinfo.get("enabled",False)            }logger.info(f"Found{len(enabled_servers)} enabled servers")# Generate NGINX configsuccess=awaitnginx_generator.generate_config_async(enabled_servers)ifsuccess:logger.info("NGINX configuration updated successfully")else:logger.error("Failed to update NGINX configuration")excepthttpx.HTTPStatusErrorase:logger.error(f"HTTP error polling registry:{e.response.status_code}")excepthttpx.RequestErrorase:logger.error(f"Network error polling registry:{e}")exceptExceptionase:logger.error(f"Unexpected error:{e}",exc_info=True)# Wait before next pollawaitasyncio.sleep(POLL_INTERVAL)asyncdef_wait_for_registry():"""Wait for registry to become available before starting main loop"""logger.info("Waiting for registry to become available...")max_retries=30retry_delay=2forattemptinrange(max_retries):try:asyncwithhttpx.AsyncClient(timeout=5.0)asclient:response=awaitclient.get(REGISTRY_API_URL)ifresponse.status_code==200:logger.info("Registry is available!")returnexceptExceptionase:logger.debug(f"Registry not ready (attempt{attempt+1}/{max_retries}):{e}")awaitasyncio.sleep(retry_delay)logger.warning("Registry did not become available, continuing anyway...")

Step 1.10: Update gateway/config_generator.py to reload NGINX

Add method to reload NGINX after config generation:

importsubprocessimportlogginglogger=logging.getLogger(__name__)classNginxConfigGenerator:# ... existing code ...defreload_nginx(self)->bool:"""Reload NGINX to apply new configuration"""try:# Test config firstresult=subprocess.run(                ["nginx","-t"],capture_output=True,text=True,check=False            )ifresult.returncode!=0:logger.error(f"NGINX config test failed:{result.stderr}")returnFalse# Reload NGINXresult=subprocess.run(                ["nginx","-s","reload"],capture_output=True,text=True,check=False            )ifresult.returncode==0:logger.info("NGINX reloaded successfully")returnTrueelse:logger.error(f"NGINX reload failed:{result.stderr}")returnFalseexceptExceptionase:logger.error(f"Error reloading NGINX:{e}",exc_info=True)returnFalse

Phase 2: Clean Up Registry Code

Step 2.1: Remove NGINX code from registry/main.py

Find and delete these lines (around line 115-120):

# DELETE THIS BLOCK:logger.info("🌐 Generating initial Nginx configuration...")enabled_servers= {path:server_service.get_server_info(path)forpathinserver_service.get_enabled_services()}awaitnginx_service.generate_config_async(enabled_servers)

Step 2.2: Remove nginx_service import

Find and delete this import fromregistry/main.py:

# DELETE THIS LINE:fromregistry.core.nginx_serviceimportnginx_service

Step 2.3: Delete registry/core/nginx_service.py

This file has been moved to gateway/, so delete the old copy:

rm registry/core/nginx_service.py

Step 2.4: Remove NGINX reload calls from server toggle operations

Find where servers are enabled/disabled (likely inregistry/api/server_routes.py) and remove NGINX reload calls.

Search for calls like:

awaitnginx_service.generate_config_async(...)

And delete them. The gateway will pick up changes automatically via polling.

Phase 3: Create Dockerfiles

Step 3.1: Create docker/Dockerfile.gateway

This builds the gateway container with NGINX + Python:

FROM nginx:alpine# Install Python in the nginx containerRUN apk add --no-cache python3 py3-pip# Copy gateway Python codeCOPY gateway/ /app/gateway/COPY gateway/requirements.txt /app/RUN pip3 install --no-cache-dir -r /app/requirements.txt# Copy NGINX config templatesCOPY gateway/templates/ /etc/nginx/templates/# Copy SSL certificates (if they exist)COPY ssl/ /etc/ssl/ 2>/dev/null || trueEXPOSE 80 443# Start both NGINX and the Python config generatorCOPY docker/start-gateway.sh /start-gateway.shRUN chmod +x /start-gateway.shCMD ["/start-gateway.sh"]

Step 3.2: Create initial NGINX configuration

Creategateway/templates/nginx_initial.conf - used before registry is available:

events{worker_connections1024;}http{    # Basic settingssendfile on;tcp_nopush on;tcp_nodelay on;keepalive_timeout65;types_hash_max_size2048;    # Loggingaccess_log /var/log/nginx/access.log;error_log /var/log/nginx/error.log;    # Basic server - will be replaced by dynamic configserver{listen80 default_server;server_name _;location /{return503"Gateway initializing, please wait...";add_header Content-Type text/plain;}location /health{return200"OK";add_header Content-Type text/plain;}}}

Step 3.3: Create docker/start-gateway.sh

This script initializes NGINX config then starts both NGINX and Python watcher:

#!/bin/shset -eecho"Initializing Gateway..."# Create necessary directoriesmkdir -p /etc/nginx/conf.dmkdir -p /var/log/nginx# Copy initial NGINX config if main config doesn't existif [!-f /etc/nginx/nginx.conf ];thenecho"Creating initial NGINX configuration..."    cp /app/gateway/templates/nginx_initial.conf /etc/nginx/nginx.conffi# Test NGINX configurationecho"Testing NGINX configuration..."nginx -t# Start NGINX in backgroundecho"Starting NGINX..."nginx -g'daemon off;'&NGINX_PID=$!# Give NGINX a moment to startsleep 2# Start Python config generator (watches registry)echo"Starting Gateway Configuration Service..."cd /apppython3 -m gateway.main&PYTHON_PID=$!# Function to handle shutdownshutdown() {echo"Shutting down..."kill$NGINX_PID2>/dev/null||truekill$PYTHON_PID2>/dev/null||trueexit 0}# Trap SIGTERM and SIGINTtrap shutdown SIGTERM SIGINT# Wait for both processeswait

Step 3.3: Create docker/Dockerfile.registry-only

This builds the registry container with ONLY FastAPI (no NGINX):

FROM python:3.11-slim# Copy requirements and installCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt# Copy ONLY registry application code (no gateway code)COPY registry/ /app/registry/COPY auth_server/ /app/auth_server/COPY frontend/build/ /app/frontend/build/WORKDIR /app# Only FastAPI - NO NGINX!EXPOSE 7860CMD ["uvicorn","registry.main:app","--host","0.0.0.0","--port","7860"]

Key Differences Between Dockerfiles:

Dockerfile.gatewayDockerfile.registry-only
Based onnginx:alpineBased onpython:3.11-slim
Has NGINX serverNO NGINX
Has Python +gateway/ codeHas Python +registry/ code
Runs 2 processes (NGINX + Python)Runs 1 process (FastAPI only)
Exposes ports 80, 443Exposes port 7860 only
Containsgateway/config_generator.pyNO gateway code at all
Watches registry APIDoesn't watch anything

Phase 4: Update docker-compose.yml

Step 4.1: Split registry into two services

Updatedocker-compose.yml to have separate gateway and registry services:

services:# NEW: Separate gateway servicegateway:build:context:.dockerfile:docker/Dockerfile.gatewaycontainer_name:mcp-gatewayports:      -"80:80"      -"443:443"environment:      -REGISTRY_API_URL=http://registry:7860/api/servers      -POLL_INTERVAL=30volumes:      -${HOME}/mcp-gateway/ssl:/etc/ssl:ro      -gateway-logs:/var/log/nginxdepends_on:registry:condition:service_healthyauth-server:condition:service_startedhealthcheck:test:["CMD", "curl", "-f", "http://localhost/health"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stoppednetworks:      -mcp-network# UPDATED: Registry without NGINXregistry:build:context:.dockerfile:docker/Dockerfile.registry-onlycontainer_name:mcp-registryports:      -"7860:7860"environment:      -AUTH_SERVER_URL=http://auth-server:8888      -METRICS_SERVICE_URL=http://metrics-service:8890volumes:      -${HOME}/mcp-gateway/servers:/app/registry/servers      -${HOME}/mcp-gateway/logs/registry:/app/logsdepends_on:      -auth-server      -metrics-service      -keycloakhealthcheck:test:["CMD", "curl", "-f", "http://localhost:7860/health"]interval:30stimeout:10sretries:3start_period:10srestart:unless-stoppednetworks:      -mcp-network# All other services stay the same (auth-server, metrics-service, keycloak, mcp servers)auth-server:# ... (unchanged)metrics-service:# ... (unchanged)keycloak:# ... (unchanged)# MCP servers...fininfo-server:# ... (unchanged)networks:mcp-network:driver:bridge

Phase 5: Create Registry-Only Deployment

Step 5.1: Create docker-compose-registry-only.yml

For customers who want to use their own gateway:

services:# NO gateway service - customer provides their ownregistry:build:context:.dockerfile:docker/Dockerfile.registry-onlyports:      -"7860:7860"environment:      -AUTH_SERVER_URL=http://auth-server:8888      -METRICS_SERVICE_URL=http://metrics-service:8890      -TRUST_PROXY_HEADERS=true# Trust X-Forwarded-* headers from customer gatewayvolumes:      -${HOME}/mcp-gateway/servers:/app/registry/serversdepends_on:      -auth-server      -metrics-service      -keycloakrestart:unless-stoppednetworks:      -mcp-networkauth-server:# Same as main docker-compose.yml# ...metrics-service:# Same as main docker-compose.yml# ...keycloak:# Same as main docker-compose.yml# ...# MCP servers (customer gateway will route to these)fininfo-server:# Same as main docker-compose.yml# ...networks:mcp-network:driver:bridge

Phase 6: Testing

Step 6.1: Test Combined Mode (gateway + registry)

# Build and start both containersdocker-compose builddocker-compose up# Verify:# 1. Gateway container is running with both NGINX and Python# 2. Registry container is running with only FastAPI# 3. Web UI accessible at http://localhost# 4. Add/remove servers and verify NGINX config updates (check logs)

Step 6.2: Test Registry-Only Mode

# Start registry-only stackdocker-compose -f docker-compose-registry-only.yml up# Set up a custom NGINX on host machine pointing to registry:7860# Verify registry works without our gateway

Step 6.3: Verify No Breaking Changes

  • Existing users should be able to rundocker-compose up without changes
  • All functionality should work exactly as before
  • Check logs for any errors

Phase 7: Documentation

Step 7.1: Update README.md

Add sections explaining both deployment modes:

  • Combined deployment (default)
  • Registry-only deployment

Step 7.2: Create Custom Gateway Integration Guide

Document requirements for organizations using their own gateway.

Step 7.3: Add deployment examples

Include examples for:

  • AWS API Gateway integration
  • Kong integration
  • Custom NGINX configuration

Container Details

Gateway Container - What's Inside

Contains:

  • ✅ NGINX server (binary)
  • ✅ NGINX config file (nginx.conf)
  • ✅ Python 3
  • gateway/ folder code
  • gateway/main.py - polls registry API every 30 seconds
  • gateway/config_generator.py - generates nginx.conf from server list
  • ✅ SSL certificates (if provided)

Responsibilities:

  1. NGINX listens on ports 80/443
  2. Python script pollshttp://registry:7860/api/servers every 30 seconds
  3. When servers change, regenerates nginx.conf
  4. Reloads NGINX with new config
  5. Routes traffic to auth-server, registry, MCP servers

Does NOT contain:

  • ❌ No registry code
  • ❌ No FastAPI
  • ❌ No web UI
  • ❌ No server management logic

Registry Container - What's Inside

Contains:

  • ✅ Python 3
  • ✅ FastAPI application
  • registry/ folder code
  • ✅ Web UI (HTML/CSS/JS)
  • ✅ Server management logic
  • ✅ FAISS search index
  • ✅ Health monitoring

Responsibilities:

  1. Runs FastAPI on port 7860
  2. Serves web UI
  3. Provides API endpoints:
    • GET /api/servers - list all servers
    • POST /api/servers - add server
    • PUT /api/servers/{id} - update server
    • DELETE /api/servers/{id} - remove server
  4. Manages server state (enabled/disabled)
  5. Health checks MCP servers
  6. Tool discovery with FAISS

Does NOT contain:

  • ❌ No NGINX
  • ❌ No nginx.conf
  • ❌ No NGINX management code
  • ❌ Doesn't know about gateway at all

Communication Flow

How the system works after separation:

1. User adds server in Web UI   ↓2. POST to registry:7860/api/servers   ↓3. Registry saves server to servers.json   ↓4. Registry returns 200 OK   ↓5. (30 seconds pass)   ↓6. Gateway polls registry API   ↓7. Gateway sees new server in response   ↓8. Gateway regenerates nginx.conf   ↓9. Gateway reloads NGINX   ↓10. New server is now routable through gateway

Key Point: Registry and Gateway don't directly communicate. Gateway just polls registry's public API. This is a clean separation.

Custom Gateway Integration

For organizations that want to use their own gateway instead of ours, document these requirements:

Requirements for Custom Gateway

  1. Call auth server for validation

    For each incoming request:  1. Call GET http://auth-server:8888/auth/validate  2. Pass Authorization header from original request  3. If 200 OK: forward X-User and X-Scopes headers to backend  4. If 401/403: reject the request
  2. Route traffic to appropriate backends

    / → http://registry:7860 (Web UI and REST APIs)/fininfo/ → http://fininfo-server:8001/mcpgw/ → http://mcpgw-server:8003/currenttime/ → http://currenttime-server:8000/atlassian/ → http://atlassian-server:8005
  3. Support required protocols

    • HTTP/HTTPS
    • Server-Sent Events (SSE) for MCP protocol
    • WebSocket for real-time health updates
    • Long-running connections
  4. Forward required headers

    • X-Forwarded-For
    • X-Forwarded-Proto
    • X-Real-IP
    • Authorization (to auth-server)
    • X-User andX-Scopes (from auth-server to backends)

Implementation Phases

PhaseTask
1Create gateway folder and move code
2Clean up registry code
3Create Dockerfiles
4Update docker-compose.yml
5Create registry-only compose file
6Testing (combined + registry-only)
7Documentation

Success Criteria

  • Gateway container runs independently with NGINX + Python watcher
  • Registry container runs independently with only FastAPI
  • Gateway polls registry API every 30 seconds
  • NGINX config regenerates when servers are added/removed
  • Combined deployment works (docker-compose up)
  • Registry-only deployment works (docker-compose-registry-only.yml)
  • Registry supports HTTPS in standalone mode (with SSL certificates)
  • No breaking changes for existing users
  • Documentation updated with both deployment modes
  • All tests pass
  • Security scans pass (bandit)

HTTPS Configuration for Registry-Only Mode

CRITICAL: Registry Must Support HTTPS Without Gateway

When running in registry-only mode (without the gateway), the registry must be able to serve HTTPS directly. This is essential for production deployments.

Implementation: FastAPI with SSL/TLS Support

Step 1: Update registry/main.py to support SSL

Add SSL configuration at the end ofregistry/main.py:

importsslimportosfrompathlibimportPath# SSL ConfigurationSSL_ENABLED=os.getenv("SSL_ENABLED","false").lower()=="true"SSL_CERTFILE=os.getenv("SSL_CERTFILE","/etc/ssl/cert.pem")SSL_KEYFILE=os.getenv("SSL_KEYFILE","/etc/ssl/key.pem")if__name__=="__main__":importuvicornifSSL_ENABLED:# Verify SSL files existcert_path=Path(SSL_CERTFILE)key_path=Path(SSL_KEYFILE)ifnotcert_path.exists():logger.error(f"SSL certificate not found:{SSL_CERTFILE}")raiseFileNotFoundError(f"SSL certificate not found:{SSL_CERTFILE}")ifnotkey_path.exists():logger.error(f"SSL key not found:{SSL_KEYFILE}")raiseFileNotFoundError(f"SSL key not found:{SSL_KEYFILE}")logger.info("Starting registry with HTTPS enabled")logger.info(f"SSL Certificate:{SSL_CERTFILE}")uvicorn.run("registry.main:app",host="0.0.0.0",port=7860,ssl_certfile=SSL_CERTFILE,ssl_keyfile=SSL_KEYFILE,ssl_keyfile_password=os.getenv("SSL_KEYFILE_PASSWORD")        )else:logger.info("Starting registry with HTTP only (no SSL)")uvicorn.run("registry.main:app",host="0.0.0.0",port=7860        )

Step 2: Update Dockerfile.registry-only to support SSL certificates

FROM python:3.11-slim# Install dependenciesCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt# Copy application codeCOPY registry/ /app/registry/COPY auth_server/ /app/auth_server/COPY frontend/build/ /app/frontend/build/WORKDIR /app# Expose HTTP and HTTPS portsEXPOSE 7860# Set Python pathENV PYTHONPATH=/app# Run registry with SSL supportCMD ["python","-m","registry.main"]

Step 3: Update docker-compose-registry-only.yml with SSL configuration

services:registry:build:context:.dockerfile:docker/Dockerfile.registry-onlycontainer_name:mcp-registryports:      -"7860:7860"# HTTPS when SSL_ENABLED=true, HTTP otherwiseenvironment:      -AUTH_SERVER_URL=http://auth-server:8888      -METRICS_SERVICE_URL=http://metrics-service:8890      -TRUST_PROXY_HEADERS=true# SSL Configuration for standalone HTTPS      -SSL_ENABLED=true      -SSL_CERTFILE=/etc/ssl/cert.pem      -SSL_KEYFILE=/etc/ssl/key.pem# Optional: SSL key password# - SSL_KEYFILE_PASSWORD=${SSL_KEYFILE_PASSWORD}volumes:      -${HOME}/mcp-gateway/servers:/app/registry/servers      -${HOME}/mcp-gateway/logs/registry:/app/logs# Mount SSL certificates for HTTPS support      -${HOME}/mcp-gateway/ssl/cert.pem:/etc/ssl/cert.pem:ro      -${HOME}/mcp-gateway/ssl/key.pem:/etc/ssl/key.pem:rodepends_on:      -auth-server      -metrics-service      -keycloakhealthcheck:# Use HTTPS for health check when SSL is enabledtest:["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('https://localhost:7860/health' if ${SSL_ENABLED:-false} else 'http://localhost:7860/health')"]interval:30stimeout:10sretries:3start_period:10srestart:unless-stoppednetworks:      -mcp-network

Step 4: Generate SSL certificates for testing

Create a helper scriptscripts/generate-ssl-certs.sh:

#!/bin/bashset -eSCRIPT_DIR="$(cd"$( dirname"${BASH_SOURCE[0]}")"&&pwd)"SSL_DIR="${HOME}/mcp-gateway/ssl"echo"Creating SSL directory..."mkdir -p"$SSL_DIR"echo"Generating self-signed SSL certificate..."openssl req -x509 -newkey rsa:4096 -nodes \  -keyout"$SSL_DIR/key.pem" \  -out"$SSL_DIR/cert.pem" \  -days 365 \  -subj"/C=US/ST=State/L=City/O=Organization/CN=localhost"echo"Setting secure permissions..."chmod 600"$SSL_DIR/key.pem"chmod 644"$SSL_DIR/cert.pem"echo"SSL certificates generated successfully:"echo"  Certificate:$SSL_DIR/cert.pem"echo"  Private Key:$SSL_DIR/key.pem"echo""echo"For production, replace these with certificates from a trusted CA."

Step 5: Access Registry with HTTPS

With SSL enabled:

# Generate certs (first time only)bash scripts/generate-ssl-certs.sh# Start registry-only mode with SSLdocker-compose -f docker-compose-registry-only.yml up -d# Access via HTTPScurl -k https://localhost:7860/health# Or in browseropen https://localhost:7860

SSL Configuration Summary

ModeSSL/TLSPortAccess URLCertificate Location
Gateway (default)✅ Gateway handles443https://your-domain.comMounted in gateway container
Registry-Only (HTTP)❌ Disabled7860http://registry-host:7860Not needed
Registry-Only (HTTPS)✅ Registry handles7860https://registry-host:7860Mounted in registry container

Build and Deployment Scripts

Create Build Scripts for ECR Deployment

Step 1: Create scripts/build-and-push-gateway.sh

#!/bin/bash# Build and push gateway container to Amazon ECRset -eSCRIPT_DIR="$(cd"$( dirname"${BASH_SOURCE[0]}")"&&pwd)"PARENT_DIR="$(dirname"$SCRIPT_DIR")"# ConfigurationAWS_REGION="${AWS_REGION:-us-east-1}"ECR_REPO_NAME="mcp-gateway"AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)ECR_REPO_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME"echo"Building and pushing Gateway container..."echo"Region:$AWS_REGION"echo"Repository:$ECR_REPO_NAME"# Login to Amazon ECRecho"Logging in to Amazon ECR..."aws ecr get-login-password --region$AWS_REGION| \  docker login --username AWS --password-stdin \"$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"# Create repository if it doesn't existecho"Creating ECR repository if it doesn't exist..."aws ecr describe-repositories --repository-names"$ECR_REPO_NAME" --region"$AWS_REGION"2>/dev/null|| \  aws ecr create-repository --repository-name"$ECR_REPO_NAME" --region"$AWS_REGION"# Build the Docker imageecho"Building Gateway Docker image..."docker build -f"$PARENT_DIR/docker/Dockerfile.gateway" -t"$ECR_REPO_NAME""$PARENT_DIR"# Tag the imageecho"Tagging image..."docker tag"$ECR_REPO_NAME":latest"$ECR_REPO_URI":latest# Push the image to ECRecho"Pushing image to ECR..."docker push"$ECR_REPO_URI":latestecho"Successfully built and pushed gateway image:"echo"$ECR_REPO_URI:latest"# Save the container URI to a file for referenceecho"$ECR_REPO_URI:latest">"$SCRIPT_DIR/.gateway_container_uri"echo"Container URI saved to$SCRIPT_DIR/.gateway_container_uri"

Step 2: Create scripts/build-and-push-registry.sh

#!/bin/bash# Build and push registry container to Amazon ECRset -eSCRIPT_DIR="$(cd"$( dirname"${BASH_SOURCE[0]}")"&&pwd)"PARENT_DIR="$(dirname"$SCRIPT_DIR")"# ConfigurationAWS_REGION="${AWS_REGION:-us-east-1}"ECR_REPO_NAME="mcp-registry"AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)ECR_REPO_URI="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME"echo"Building and pushing Registry container..."echo"Region:$AWS_REGION"echo"Repository:$ECR_REPO_NAME"# Login to Amazon ECRecho"Logging in to Amazon ECR..."aws ecr get-login-password --region$AWS_REGION| \  docker login --username AWS --password-stdin \"$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"# Create repository if it doesn't existecho"Creating ECR repository if it doesn't exist..."aws ecr describe-repositories --repository-names"$ECR_REPO_NAME" --region"$AWS_REGION"2>/dev/null|| \  aws ecr create-repository --repository-name"$ECR_REPO_NAME" --region"$AWS_REGION"# Build the Docker imageecho"Building Registry Docker image..."docker build -f"$PARENT_DIR/docker/Dockerfile.registry-only" -t"$ECR_REPO_NAME""$PARENT_DIR"# Tag the imageecho"Tagging image..."docker tag"$ECR_REPO_NAME":latest"$ECR_REPO_URI":latest# Push the image to ECRecho"Pushing image to ECR..."docker push"$ECR_REPO_URI":latestecho"Successfully built and pushed registry image:"echo"$ECR_REPO_URI:latest"# Save the container URI to a file for referenceecho"$ECR_REPO_URI:latest">"$SCRIPT_DIR/.registry_container_uri"echo"Container URI saved to$SCRIPT_DIR/.registry_container_uri"

Step 3: Make scripts executable

chmod +x scripts/build-and-push-gateway.shchmod +x scripts/build-and-push-registry.shchmod +x scripts/generate-ssl-certs.sh

Security and Code Quality

Phase 8: Security Validation

Add security scanning to the implementation plan:

Step 8.1: Run Bandit security scanner on gateway code

# Scan new gateway code for security issuesuv run bandit -r gateway/# Generate reportuv run bandit -r gateway/ -f json -o reports/bandit-gateway.json

Step 8.2: Scan registry code

# Scan registry codeuv run bandit -r registry/# Generate reportuv run bandit -r registry/ -f json -o reports/bandit-registry.json

Step 8.3: Docker image security scanning

# Scan gateway imagedocker scan docker/Dockerfile.gateway# Scan registry imagedocker scan docker/Dockerfile.registry-only

Secrets Management Best Practices

Never commit these files:

  • SSL certificates (*.pem,*.crt,*.key)
  • Environment files with credentials (.env)
  • Database credentials
  • API keys

Add to .gitignore:

# SSL Certificates*.pem*.crt*.key*.p12# Environment files.env.env.local.env.production# Container URIs (from build scripts)scripts/.gateway_container_uriscripts/.registry_container_uri# Security reportsreports/

Testing Strategy

Unit Tests

Create tests/gateway/test_config_generator.py

importpytestfromunittest.mockimportMock,patchfromgateway.config_generatorimportNginxConfigGeneratorclassTestNginxConfigGenerator:"""Tests for NGINX config generation"""deftest_generate_config_with_enabled_servers(self):"""Test config generation with enabled servers"""generator=NginxConfigGenerator()servers= {"fininfo": {"enabled":True,"port":8001,"protocol":"http"            }        }config=generator._generate_config(servers)assert"fininfo"inconfigassert"8001"inconfigdeftest_generate_config_empty_servers(self):"""Test config generation with no servers"""generator=NginxConfigGenerator()config=generator._generate_config({})assertconfigisnotNoneassertlen(config)>0@patch('subprocess.run')deftest_reload_nginx_success(self,mock_run):"""Test successful NGINX reload"""mock_run.return_value=Mock(returncode=0,stderr="")generator=NginxConfigGenerator()result=generator.reload_nginx()assertresultisTrueassertmock_run.call_count==2# test + reload@patch('subprocess.run')deftest_reload_nginx_config_test_fails(self,mock_run):"""Test NGINX reload when config test fails"""mock_run.return_value=Mock(returncode=1,stderr="config error")generator=NginxConfigGenerator()result=generator.reload_nginx()assertresultisFalse

Create tests/registry/test_ssl_config.py

importpytestimportosfrompathlibimportPathclassTestRegistrySSLConfig:"""Tests for registry SSL configuration"""deftest_ssl_enabled_environment_variable(self):"""Test SSL enabled via environment variable"""os.environ["SSL_ENABLED"]="true"# Import after setting env varfromregistry.mainimportSSL_ENABLEDassertSSL_ENABLEDisTruedeftest_ssl_disabled_by_default(self):"""Test SSL disabled by default"""os.environ.pop("SSL_ENABLED",None)fromregistry.mainimportSSL_ENABLEDassertSSL_ENABLEDisFalsedeftest_ssl_cert_paths_configurable(self):"""Test SSL cert paths are configurable"""test_cert="/custom/path/cert.pem"test_key="/custom/path/key.pem"os.environ["SSL_CERTFILE"]=test_certos.environ["SSL_KEYFILE"]=test_keyfromregistry.mainimportSSL_CERTFILE,SSL_KEYFILEassertSSL_CERTFILE==test_certassertSSL_KEYFILE==test_key

Integration Tests

Create tests/integration/test_deployment_modes.py

importpytestimporthttpximportasyncioimportdocker@pytest.mark.asyncioclassTestDeploymentModes:"""Integration tests for different deployment modes"""asyncdeftest_full_mode_gateway_and_registry(self):"""Test full mode with both gateway and registry"""asyncwithhttpx.AsyncClient(verify=False)asclient:# Gateway should be accessiblegateway_response=awaitclient.get("http://localhost/health")assertgateway_response.status_code==200# Registry should be accessible through gatewayregistry_response=awaitclient.get("http://localhost/api/servers")assertregistry_response.status_code==200asyncdeftest_registry_only_mode_http(self):"""Test registry-only mode with HTTP"""asyncwithhttpx.AsyncClient()asclient:response=awaitclient.get("http://localhost:7860/health")assertresponse.status_code==200asyncdeftest_registry_only_mode_https(self):"""Test registry-only mode with HTTPS"""asyncwithhttpx.AsyncClient(verify=False)asclient:response=awaitclient.get("https://localhost:7860/health")assertresponse.status_code==200asyncdeftest_gateway_polls_registry(self):"""Test that gateway successfully polls registry"""# Add a server via registry APIasyncwithhttpx.AsyncClient()asclient:# Add test serverresponse=awaitclient.post("http://localhost:7860/api/servers",json={"name":"test-server","port":9999,"enabled":True                }            )assertresponse.status_code==200# Wait for gateway to pollawaitasyncio.sleep(35)# Check gateway config updated# (Would need to check NGINX config or test routing)

Environment Variables Reference

Complete Environment Variable Documentation

VariableContainerDefaultDescription
REGISTRY_API_URLgatewayhttp://registry:7860/api/serversRegistry API endpoint to poll
POLL_INTERVALgateway30How often gateway polls registry (seconds)
SSL_ENABLEDgateway, registryfalseEnable HTTPS/SSL
SSL_CERTFILEgateway, registry/etc/ssl/cert.pemPath to SSL certificate
SSL_KEYFILEgateway, registry/etc/ssl/key.pemPath to SSL private key
SSL_KEYFILE_PASSWORDgateway, registry(none)Password for encrypted SSL key
AUTH_SERVER_URLregistryhttp://auth-server:8888Authentication server URL
METRICS_SERVICE_URLregistryhttp://metrics-service:8890Metrics service URL
TRUST_PROXY_HEADERSregistryfalseTrust X-Forwarded-* headers from proxy
LOG_LEVELgateway, registryINFOLogging level (DEBUG, INFO, WARNING, ERROR)

Operational Details

Logging

Gateway logs:

  • NGINX access:/var/log/nginx/access.log
  • NGINX error:/var/log/nginx/error.log
  • Python service:docker logs mcp-gateway

Registry logs:

  • Application:docker logs mcp-registry
  • Mounted volume:~/mcp-gateway/logs/registry/

View live logs:

# Gateway logs (both NGINX and Python)docker logs -f mcp-gateway# Registry logsdocker logs -f mcp-registry# All servicesdocker-compose logs -f# Specific servicedocker-compose logs -f gatewaydocker-compose logs -f registry

Monitoring and Health Checks

Health check endpoints:

# Gateway healthcurl http://localhost/health# Registry health (direct)curl http://localhost:7860/health# Registry health (via gateway)curl http://localhost/api/health

Metrics to monitor:

  • Gateway poll frequency (should be ~30s)
  • NGINX reload success rate
  • Registry API response times
  • Container restart count
  • SSL certificate expiration dates

Container resource limits (production):

services:gateway:deploy:resources:limits:cpus:'1.0'memory:512Mreservations:cpus:'0.5'memory:256Mregistry:deploy:resources:limits:cpus:'2.0'memory:2Greservations:cpus:'1.0'memory:1G

README.md Updates

Add Deployment Modes Section

Add this section to the main README.md:

##Deployment ModesMCP Gateway Registry supports three deployment modes to fit different infrastructure needs.###Mode 1: Full Stack with Gateway (Default)**What you get:**- NGINX gateway with SSL/TLS termination- Registry with Web UI- Authentication server (Keycloak integration)- Metrics service- All MCP servers**Quick start:**```bash# Clone repositorygit clone https://github.com/your-org/mcp-gateway-registry.gitcd mcp-gateway-registry# Start all servicesdocker-compose up -d# Access Web UIopen https://localhost

Best for: Most users who want a complete, ready-to-use solution


Mode 2: Registry-Only (Bring Your Own Gateway)

What you get:

  • Registry with Web UI (HTTP or HTTPS)
  • Authentication server
  • Metrics service
  • MCP servers (you route to them via your gateway)

Quick start:

# Generate SSL certificates (for HTTPS)bash scripts/generate-ssl-certs.sh# Start registry stackdocker-compose -f docker-compose-registry-only.yml up -d# Access via HTTPScurl -k https://localhost:7860/health

Configure your gateway to route traffic:

  • /http://registry:7860 (orhttps://registry:7860 if SSL enabled)
  • /fininfo/http://fininfo-server:8001
  • /mcpgw/http://mcpgw-server:8003
  • etc.

Best for: Enterprises with existing API gateways (AWS API Gateway, Kong, Traefik, etc.)


Mode 3: Standalone Registry

What you get:

  • Registry with Web UI only
  • MCP servers run elsewhere (you manage them separately)

Quick start:

# Edit docker-compose-registry-only.yml# Remove or comment out MCP server services# Start registry onlydocker-compose -f docker-compose-registry-only.yml up registry auth-server keycloak

Best for: Organizations that want centralized server management but run MCP servers on different infrastructure


SSL/HTTPS Configuration

Full Mode (with Gateway)

Gateway handles all SSL/TLS:

# Place your SSL certificatesmkdir -p~/mcp-gateway/sslcp your-cert.pem~/mcp-gateway/ssl/cert.pemcp your-key.pem~/mcp-gateway/ssl/key.pem# Start servicesdocker-compose up -d# Access via HTTPS (gateway)open https://your-domain.com

Registry-Only Mode with HTTPS

Registry serves HTTPS directly:

# Generate self-signed cert (testing only)bash scripts/generate-ssl-certs.sh# Or use your own certificatesmkdir -p~/mcp-gateway/sslcp your-cert.pem~/mcp-gateway/ssl/cert.pemcp your-key.pem~/mcp-gateway/ssl/key.pem# Ensure SSL_ENABLED=true in docker-compose-registry-only.yml# Start servicesdocker-compose -f docker-compose-registry-only.yml up -d# Access via HTTPS (registry direct)curl -k https://localhost:7860/health

Production SSL certificates:

For production, use certificates from a trusted CA:

  • Let's Encrypt (free)
  • AWS Certificate Manager
  • Your organization's CA

Custom Gateway Integration Examples

AWS API Gateway

# API Gateway HTTP API configurationResources:MCPRegistryIntegration:Type:AWS::ApiGatewayV2::IntegrationProperties:ApiId:!Ref HttpApiIntegrationType:HTTP_PROXYIntegrationUri:https://registry-host:7860IntegrationMethod:ANYPayloadFormatVersion:'1.0'TlsConfig:ServerNameToVerify:registry-host

Kong

# kong.ymlservices:  -name:mcp-registryurl:https://registry:7860routes:      -name:registry-routepaths:          -/plugins:      -name:jwt      -name:rate-limitingconfig:minute:100

Traefik

# docker-compose.yml with Traefikservices:registry:labels:      -"traefik.enable=true"      -"traefik.http.routers.registry.rule=Host(`registry.example.com`)"      -"traefik.http.routers.registry.tls=true"      -"traefik.http.services.registry.loadbalancer.server.port=7860"      -"traefik.http.services.registry.loadbalancer.server.scheme=https"
## Migration Guide### Migrating from Monolithic to Separated Architecture**For existing deployments:**#### Step 1: Backup Current State```bash# Stop current containersdocker-compose down# Backup configuration and datacp -r ~/mcp-gateway ~/mcp-gateway-backup-$(date +%Y%m%d)# Backup current git stategit branch backup-before-separation

Step 2: Pull Latest Changes

# Update to latest versiongit pull origin main# Review changesgit log --oneline -10

Step 3: Rebuild Containers

# Clean rebuild (removes old images)docker-compose build --no-cache# Or rebuild specific servicesdocker-compose build gatewaydocker-compose build registry

Step 4: Update Configuration

# If using custom SSL certificates, ensure they're in the right locationls -la~/mcp-gateway/ssl/# Verify docker-compose.yml has correct volume mountsgrep -A 5"volumes:" docker-compose.yml

Step 5: Start New Architecture

# Start all servicesdocker-compose up -d# Watch logs for any errorsdocker-compose logs -f

Step 6: Verify Services

# Check containers are runningdocker ps| grep mcp-# Expected output:# mcp-gateway (new - separate container)# mcp-registry (new - separate container)# mcp-auth-server# mcp-metrics-service# mcp-keycloak# mcp-fininfo-server# etc.# Test gateway healthcurl http://localhost/health# Test registry health (direct)curl http://localhost:7860/health# Test Web UIopen https://localhost

Step 7: Verify Functionality

# Add a test server via Web UI# Toggle server on/off# Check gateway logs to confirm config regenerationdocker logs mcp-gateway| grep"NGINX configuration updated"

Rollback Plan

If issues occur:

# Stop new architecturedocker-compose down# Checkout previous versiongit checkout backup-before-separation# Restore backup if neededrm -rf~/mcp-gatewaymv~/mcp-gateway-backup-YYYYMMDD~/mcp-gateway# Start old versiondocker-compose up -d

Expected Changes

What changes:

  • Two containers instead of one (gateway + registry)
  • Gateway polls registry every 30 seconds (slight delay in config updates)
  • Separate health endpoints

What stays the same:

  • Web UI functionality
  • API endpoints
  • MCP server routing
  • Authentication flow
  • All features

Conclusion

This separation provides:

  • Clean architecture - each container has single responsibility
  • Enterprise adoption - organizations can use their own gateways
  • No breaking changes - existing users continue unchanged
  • Better scalability - independent scaling of gateway and registry

The separation completes the microservices architecture (auth, metrics, keycloak already separated) and enables broader enterprise adoption while maintaining all existing benefits.

Metadata

Metadata

Assignees

Labels

architectureArchitecture and design decisionsenhancementNew feature or request

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp