name: airis-mcp-gateway services: # Docker MCP Gateway (internal, port 9390) # Provides the internal SSE transport. Only its built-in time server runs; # client-visible capabilities are defined by mcp-config.json. docker-mcp-gateway: container_name: airis-docker-mcp-gateway # Pinned by digest — docker/mcp-gateway does not publish versioned tags (issue #77). # Refresh: docker pull docker/mcp-gateway:latest && docker inspect --format='{{index .RepoDigests 0}}' docker/mcp-gateway:latest image: docker/mcp-gateway@sha256:f79574a3f86603ad65fd5c04f1251a2f2ecda56c1df76ed4418c1c11401c9432 command: - --transport=sse - --port=9390 - --watch - --servers=time environment: # Clear OrbStack-injected proxy vars. docker/mcp-gateway fetches its # catalog over HTTPS; the injected proxy host (proxyproxy.orb.internal) # does not resolve, which crash-loops the container. Same fix the api # service already carries — issue: it was applied there but not here. - HTTP_PROXY= - HTTPS_PROXY= - ALL_PROXY= - NO_PROXY= - http_proxy= - https_proxy= - all_proxy= - no_proxy= expose: - "9390" volumes: - /var/run/docker.sock:/var/run/docker.sock healthcheck: test: ["CMD-SHELL", "wget -qO /dev/null http://$(hostname -i):9390/health"] interval: 10s timeout: 3s retries: 5 restart: unless-stopped # FastAPI Proxy (external, port 9400) # Hybrid MCP Multiplexer: Docker Gateway + Process (uvx/npx) api: container_name: airis-mcp-gateway # Single-file workflow: with both `image` and `build` set, `docker compose up` # PULLS the prebuilt image by default (end-users who didn't clone the repo), # while contributors run `docker compose up -d --build` to build from source. # pull_policy: missing → use the cached/pulled image if present, otherwise pull. image: ghcr.io/agiletec-inc/airis-mcp-gateway:latest build: context: . dockerfile: Dockerfile pull_policy: missing ports: - "9400:8000" extra_hosts: - "host.docker.internal:host-gateway" dns: - 8.8.8.8 - 1.1.1.1 # Resource limits for container (prevents runaway processes) mem_limit: 2g cpus: 2 environment: - MCP_GATEWAY_URL=http://docker-mcp-gateway:9390 - MCP_CONFIG_PATH=/app/mcp-config.json # Dynamic MCP: exposes only meta-tools instead of all tools # reduces context token usage by ~98%. Set to "false" to expose all tools directly - DYNAMIC_MCP=${DYNAMIC_MCP:-true} # Meta-tools mode: "core" (3 tools, ~600 tokens) or "full" (7 tools, ~1400 tokens) - META_TOOLS_MODE=${META_TOOLS_MODE:-core} # Tool listing: "compact" (top 3 per server) or "full" (all tool names) - TOOL_LISTING_MODE=${TOOL_LISTING_MODE:-compact} # Clear OrbStack-injected proxy vars: NO_PROXY contains IPv6 CIDRs that # httpx cannot parse as URL patterns, causing /ready to 500. - HTTP_PROXY= - HTTPS_PROXY= - ALL_PROXY= - NO_PROXY= - http_proxy= - https_proxy= - all_proxy= - no_proxy= volumes: - ./mcp-config.json:/app/mcp-config.json:ro # Persistent data volumes (appuser home directory) - cache-uv:/home/appuser/.cache/uv # uvx package cache (faster restarts) - cache-npm:/home/appuser/.npm # npm package cache (faster restarts) depends_on: docker-mcp-gateway: condition: service_healthy healthcheck: # Use /ready instead of /health to ensure HOT servers are initialized # before accepting connections (fixes Claude Code slash command visibility) test: ["CMD-SHELL", "curl -sf http://localhost:8000/ready | grep -q '\"ready\":true' || exit 1"] interval: 5s timeout: 3s retries: 10 start_period: 30s # Allow time for HOT server initialization restart: unless-stopped volumes: cache-uv: # uvx package cache (persists uvx packages) cache-npm: # npm package cache (persists npx server packages)