Handle
Connecting…
Back to Workshop
Module 06 ~15 min

Wire Agent ↔ MCP Server

Connect the AI agent and MCP server with Docker Compose so they can discover and call each other over the internal network.

What You'll Build

Service Wiring

Docker Compose connecting agent + MCP server

Internal Network

Services communicate via Docker DNS

End-to-End Test

Verify the full pipeline with curl

Language:

Step by Step

Step 6a

Create docker-compose.yml

The compose file defines both services on a shared network. The agent references the MCP server by its Docker DNS name mcp-server. Each language uses the same compose structure but points to different build contexts.

docker-compose.yml YAML
services:
  mcp-server:
    build: ./services/mcp-server
    ports:
      - "8000:8000"
    environment:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 10s
      retries: 3

  agent:
    build: ./services/agent
    ports:
      - "8001:8001"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - OPENAI_MODEL=${OPENAI_MODEL:-gemini-3.5-flash-lite}
      - MCP_SERVER_URL=http://mcp-server:8000
    depends_on:
      mcp-server:
        condition: service_healthy

Key insight: The agent uses MCP_SERVER_URL=http://mcp-server:8000. Docker Compose creates a shared network where services can reach each other by name. The depends_on with service_healthy ensures the MCP server is ready before the agent starts.

Step 6b

Create the .env File

Store your API keys in a .env file. Docker Compose reads this file automatically and injects the variables into your containers.

.env Bash
# Required: your Gemini API key
OPENAI_API_KEY=your-gemini-api-key-here
OPENAI_MODEL=gemini-3.5-flash-lite

# The weather tool needs no key: yr.no and Nominatim are free.

# Optional: another OpenAI-compatible provider (OpenAI, Ollama, ...)
# OPENAI_BASE_URL=http://ollama:11434/v1

Important: Never commit your .env file to git. Add it to .gitignore to keep your secrets safe.

Step 6c

Start the Stack

Build and start both services. Docker Compose will create the network, build the images, and start the containers in the correct order.

Terminal Bash
# Build and start all services
docker compose up --build -d

# Watch the logs to verify startup
docker compose logs -f

# Check service health
docker compose ps

You should see both services running and healthy:

Expected output Bash
NAME          STATUS                   PORTS
mcp-server    Up 10 seconds (healthy)  0.0.0.0:8000->8000/tcp
agent         Up 5 seconds             0.0.0.0:8001->8001/tcp
Step 6d

Test the MCP Server Directly

Before testing the full pipeline, verify the MCP server is responding correctly on its own.

Terminal Bash
# Health check
curl http://localhost:8000/health

# List available tools
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

# Call the weather tool directly
curl -X POST http://localhost:8000/message \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: get_weather_forecast" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_weather_forecast","arguments":{"location":"Oslo"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
Step 6e

End-to-End Test

Now test the full pipeline: your query goes to the agent, which calls the LLM, which decides to use a tool, the agent calls the MCP server, and returns the final answer.

Terminal Bash
# Query the agent (full pipeline: Agent → LLM → MCP → LLM → Response)
curl -X POST http://localhost:8001/query \
  -H "Content-Type: application/json" \
  -d '{"query": "What is the weather in Oslo?"}'

You should get a natural-language response about the weather in Oslo, something like:

Expected response JSON
{
  "response": "The current weather in Oslo is 8°C with partly cloudy skies..."
}

Congratulations! If you see a weather response, the full pipeline is working: Agent discovered tools from MCP, sent your query to the LLM, the LLM called the weather tool, and the agent returned the result. The MCP protocol is doing its job.

Step 6f

Troubleshooting

Common issues and how to fix them:

Terminal Bash
# Agent can't reach MCP server? Check the network
docker compose exec agent curl http://mcp-server:8000/health

# See detailed logs for a specific service
docker compose logs agent --tail=50
docker compose logs mcp-server --tail=50

# Rebuild everything from scratch
docker compose down
docker compose up --build -d

# Check environment variables are injected
docker compose exec agent env | grep OPENAI

Common pitfall: If the agent starts before the MCP server is healthy, tool discovery will fail. The depends_on with service_healthy condition prevents this, but make sure your MCP server has a working health check endpoint.

What You've Built

Docker Compose orchestration wiring agent and MCP server
Secure environment variable management with .env
Health checks ensuring correct startup order
End-to-end verified: User → Agent → LLM → MCP → Weather API

How was this module?

Your feedback helps us improve the workshop.

Submitting as anonymous

Your handle is sent with this feedback; leave it blank in the header and the submission stays anonymous. Please keep personal data out of the comment too — no name, e-mail address or employer, yours or anyone else's.

Build the Agent Add a Custom Tool