10 -- Discovery: Docker

Prerequisite: 01 -- HTTP Gateway You will need: Running Hangar, Docker or Podman Time: 10 minutes Adds: Auto-discover MCP servers from Docker container labels

The Problem

You have MCP servers running as Docker containers. You don't want to manually update config.yaml every time a container starts or stops. You want Hangar to detect them automatically.

The Config

# config.yaml -- Recipe 10: Docker Discovery
discovery:                               # NEW: discovery configuration
  enabled: true                          # NEW: enable auto-discovery
  refresh_interval_s: 30                 # NEW: scan every 30 seconds
  auto_register: false                   # NEW: require manual approval

  sources:                               # NEW: discovery sources
    - type: docker                       # NEW: Docker source
      mode: additive                     # NEW: only add, never remove

Try It

  1. Start an MCP server container with labels:

    docker run -d --name my-mcp-server \
      -l mcp.hangar.enabled=true \
      -l mcp.hangar.name=docker-math \
      -l mcp.hangar.mode=http \
      -l mcp.hangar.port=8080 \
      my-mcp-server:latest
  2. Start Hangar:

    mcp-hangar serve --http --host 127.0.0.1 --port 8000
  3. Scan. A source scans on its own every discovery.refresh_interval_s, on the instance holding the management lease (see 25) -- the log line is discovery_cycle_complete. To do it now, read the source's id and ask:

    curl -s http://localhost:8000/api/discovery/sources    # each source carries an `id`
    curl -X POST http://localhost:8000/api/discovery/sources/<id>/scan

    Since 2.5.0. The id of a source declared in config.yaml is derived from its type, so it is the same after a restart and safe to keep in a script. Earlier releases listed configured sources without an id and answered 404 on this route for every id you could obtain.

    Preview. Triggering a scan and the other source-management routes (register/update/delete/enable/disable) ship in 2.5.0 as Preview, not GA — the behaviour may still change. Those responses carry the header X-Hangar-Preview: discovery-source-management. Listing sources and the approval workflow are stable.

  4. Check pending MCP servers:

    curl http://localhost:8000/api/discovery/pending
    {"pending": [{"name": "docker-math", "source_type": "docker", "mode": "remote",
                  "connection_info": {"endpoint": "http://172.17.0.3:8080/mcp"},
                  "metadata": {}, "fingerprint": "...", "discovered_at": "...",
                  "last_seen_at": "...", "ttl_seconds": 300, "is_expired": false}]}

    The key is source_type, not source.

  5. Approve the MCP server:

    curl -X POST http://localhost:8000/api/discovery/approve/docker-math
  6. Verify it's registered:

    mcp-hangar status
    docker-math    COLD
    

What Just Happened

The Docker discovery source connects to the Docker socket and lists containers with mcp.hangar.enabled=true labels. In additive mode, it only adds new MCP servers -- never removes existing ones. With auto_register: false, discovered MCP servers go to a pending queue for manual approval. Omit the key and they are registered on discovery: the default is true.

Set auto_register: true if you trust all labeled containers and want zero-touch registration.

With More Than One Hangar

Since 2.5.0, discovery runs only on the replica holding the management lease, and each replica reads its own Docker socket -- so the containers it can see are the ones on its node. Give every replica the discovery configuration, not just one: the holder can be any of them. See 11 -- Kubernetes discovery for the same rule stated in full, and 25 -- Running More Than One Replica.

Key Config Reference

KeyTypeDefaultDescription
discovery.enabledboolfalseEnable auto-discovery
discovery.refresh_interval_sint30Seconds between scans
discovery.auto_registerbooltrueRegister a discovered server without approval. The default registers -- set it to false, as this recipe does, if you want the pending queue
discovery.sources[].typestring--docker, filesystem, kubernetes, entrypoint
discovery.sources[].modestring--additive (add only) or authoritative (add and remove)

Docker Labels

LabelRequiredDefaultDescription
mcp.hangar.enabledYes--Must be "true"
mcp.hangar.nameNoContainer nameMCP Server name
mcp.hangar.modeNocontainerMCP Server mode
mcp.hangar.portNo8080MCP Server port
mcp.hangar.groupNo--Auto-add to group

What's Next

Docker discovery works for local and CI environments. For Kubernetes, you need annotation-based discovery.

--> 11 -- Discovery: Kubernetes