services:
  mcp-hangar:
    image: ghcr.io/mcp-hangar/mcp-hangar:latest
    container_name: mcp-hangar
    ports:
      - "8080:8080"
    # The image's default CMD is `serve --http --host 0.0.0.0 --port 8080`, and
    # this example configures no authentication -- so the gateway refuses to
    # start and the container exits 1:
    #
    #   Refusing to start HTTP on non-loopback without authentication.
    #   Use --unsafe-no-auth to override.
    #
    # That refusal is correct and stays: binding a wildcard address with no
    # authentication is the mistake worth failing closed on. It is also why this
    # compose file had never once started the gateway -- the flag was simply
    # missing, and `examples/**` has no CI to notice.
    #
    # Overridden here because this is a local first-run example on a published
    # loopback port. Do NOT copy this line into anything reachable by others;
    # see `examples/auth-keycloak/` for the shape with authentication on.
    command:
      - serve
      - --http
      - --host
      - "0.0.0.0"
      - --port
      - "8080"
      - --unsafe-no-auth
    environment:
      # Mounting the config is not enough to load it: without this the gateway
      # starts with `config_path: null` and ignores the file below entirely.
      - MCP_CONFIG=/etc/mcp-hangar/config.yaml
      - MCP_MODE=http
      - MCP_HTTP_PORT=8080
      - MCP_LOG_LEVEL=INFO
      - MCP_JSON_LOGS=true
    volumes:
      - ./config.yaml:/etc/mcp-hangar/config.yaml:ro
    depends_on:
      everything:
        condition: service_started
    healthcheck:
      # `python3`, not `curl`: the image is `python:3.14-slim` and ships neither
      # curl nor wget, so every `["CMD", "curl", ...]` healthcheck in this repo
      # reported the container unhealthy forever, whatever path it asked for.
      # `/health/ready` and not `/health`: the gateway serves `/health/live`,
      # `/health/ready` and `/health/startup`; a bare `/health` is a 404.
      test:
        - CMD
        - python3
        - -c
        - |
          import sys, urllib.request
          sys.exit(0 if urllib.request.urlopen("http://localhost:8080/health/ready", timeout=3).status == 200 else 1)
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s
    restart: unless-stopped

  # The provider: the official "everything" server over streamable HTTP.
  #
  # Run from the npm package on a stock node image, not from `mcp/everything`.
  # That image exists, but it was last rebuilt in 2025 and its build of the
  # server ignored the transport argument -- it came up on stdio and the
  # gateway got `Connection refused`. The package is released continuously
  # (`@modelcontextprotocol/server-everything`), and `npx` is how the docs
  # already recommend running an official server.
  #
  # `everything` and not one of the others because it is the only official
  # server that speaks HTTP; the rest are stdio-only, which a gateway in its
  # own container cannot attach to without a bridge beside it.
  everything:
    image: node:lts-alpine
    container_name: mcp-everything
    command: ["npx", "-y", "@modelcontextprotocol/server-everything", "streamableHttp"]
    environment:
      - PORT=3001
    # Not published on the host: the gateway reaches it over the compose
    # network, and nothing else needs to.
    expose:
      - "3001"
    restart: unless-stopped

  # Optional: Prometheus for metrics
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.console.templates=/usr/share/prometheus/consoles'
    restart: unless-stopped

  # Optional: Grafana for dashboards
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana-data:/var/lib/grafana
    restart: unless-stopped

volumes:
  grafana-data:
