Skip to content

01 — HTTP Gateway

Prerequisite: None You will need: A running Streamable HTTP MCP server (test server provided below) Time: 5 minutes Adds: Single remote MCP server behind Hangar as control plane

The Problem

You have one MCP server today. Tomorrow you'll have five. You need a control plane before that happens. Right now, Claude Desktop connects directly to your MCP server — no visibility, no lifecycle management, no single point of configuration. When you add the second server, you're managing two configs. By the fifth, it's chaos.

Prerequisites

You need a running MCP server to point Hangar at. This repo ships a test server in examples/provider_math/ that runs on Streamable HTTP.

# Build the test MCP server (requires Docker)
docker build -t mcp-math:latest examples/provider_math/

# Start it on port 8080
docker run -d --name mcp-math -p 8080:8080 mcp-math:latest

Verify it is running:

curl -s http://localhost:8080/mcp -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' \
  -H "Content-Type: application/json" | head -c 80

You should see a JSON-RPC response. Keep the container running.

The Config

# config.yaml — Recipe 01: HTTP Gateway
mcp_servers:
  my-mcp:
    mode: remote
    endpoint: http://localhost:8080/mcp
    description: "My remote MCP server"
    http:
      connect_timeout: 10.0
      read_timeout: 30.0

Save this as ~/.config/mcp-hangar/config.yaml or pass it with --config.

Try It

  1. Test Hangar with the config (using stdin/stdout)
echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' | \
  mcp-hangar --config ~/.config/mcp-hangar/config.yaml serve
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{...},"serverInfo":{"name":"mcp-registry","version":"1.25.0"}}}

Hangar responds to MCP initialize. Press Ctrl+C to stop.

  1. Check MCP server status (create test script)
cat > /tmp/test-hangar.sh << 'EOF'
#!/bin/bash
(
  echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'
  sleep 0.5
  echo '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
  sleep 0.5
  echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"hangar_status","arguments":{}},"id":2}'
  sleep 2
) | mcp-hangar --config ~/.config/mcp-hangar/config.yaml serve 2>/dev/null | grep '"id":2'
EOF
chmod +x /tmp/test-hangar.sh
/tmp/test-hangar.sh
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"...\"id\": \"my-mcp\", \"indicator\": \"[COLD]\", \"state\": \"cold\"..."}]}}

MCP Server shows COLD state (not started yet).

  1. List tools to trigger cold start
cat > /tmp/test-list.sh << 'EOF'
#!/bin/bash
(
  echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'
  sleep 0.5
  echo '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
  sleep 0.5
  echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"hangar_list","arguments":{}},"id":2}'
  sleep 5
) | mcp-hangar --config ~/.config/mcp-hangar/config.yaml serve 2>/dev/null | grep '"id":2'
EOF
chmod +x /tmp/test-list.sh
/tmp/test-list.sh
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"...\"mcp_server\": \"my-mcp\", \"state\": \"ready\", \"mode\": \"remote\", \"tools_count\": 2..."}]}}

MCP Server transitioned to READY and discovered tools.

  1. Configure Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "hangar": {
      "command": "mcp-hangar",
      "args": ["serve", "--config", "~/.config/mcp-hangar/config.yaml"]
    }
  }
}

What Just Happened

Hangar loaded your MCP server configuration and started in stdio mode (JSON-RPC over stdin/stdout). When you sent the initialize handshake, Hangar responded with its capabilities. On the first hangar_list call, Hangar performed a cold start: it connected to the remote MCP server (examples/provider_math in this recipe), sent MCP initialize + tools/list to discover available tools, and registered them in its internal registry.

The test MCP server doesn't know Hangar exists — it sees standard MCP JSON-RPC requests. This is a transparent proxy pattern. Hangar adds nothing yet: no health checks, no circuit breaker, no authentication. That's the point — recipe 01 is the baseline.

Cleanup

When you are done with this recipe (or before starting recipe 02), stop the test container:

docker rm -f mcp-math

Key Config Reference

Key Type Default Description
mode string MCP Server mode. Use remote for HTTP/SSE MCP servers
endpoint string Full URL of the remote MCP server (including path)
description string "" Human-readable description shown in status
http.connect_timeout float 10.0 TCP connection timeout in seconds
http.read_timeout float 30.0 Response read timeout in seconds

What's Next

Your MCP server is proxied — but what happens when it goes down? Right now, Hangar sends requests into the void and forwards the error. You need visibility into MCP server health before failures surprise you.

02 — Health Checks