harbor

Examples

MCP Server Task

How to create a task with a sidecar MCP server using Docker Compose.

Multi-container tasks are useful for simulating external services. This example shows how to build a task where the agent must interact with an MCP server running as a sidecar service. The full source is in examples/tasks/hello-mcp.

Multi-container environments

Harbor tasks define their environments in the environment/ directory. Every implementation of the BaseEnvironment class defines which files are required in that environment directory. Most environments expect a single Dockerfile, which is insufficient for multi-container tasks.

The --env docker environment supports multi-container tasks by preferring a environment/docker-compose.yaml file if present.

Note that the DockerEnvironment class is currently the only environment that supports multi-container tasks. We are actively working on adding cloud support for multi-container tasks.

Task overview

In this example task, the agent is asked to connect to an MCP server, call a tool, and write the result to a file. The key concepts demonstrated are:

  • Using Docker Compose to run additional services alongside the agent container
  • Health checks to ensure the sidecar is ready before the agent starts
  • Verifying the agent's output with a simple pytest check

Directory structure

hello-mcp/
├── instruction.md
├── task.toml
├── environment/
│   ├── Dockerfile                 # Agent container
│   ├── docker-compose.yaml        # Compose override (adds MCP server)
│   └── mcp-server/
│       ├── Dockerfile             # MCP server container
│       └── server.py              # FastMCP server
├── solution/
│   └── solve.sh
└── tests/
    ├── test.sh
    └── test_outputs.py

Docker Compose sidecar

When a task needs additional services (databases, APIs, MCP servers, etc.), place a docker-compose.yaml in the environment/ directory. Harbor merges it with its base compose config automatically.

environment/docker-compose.yaml
services:
  main:
    depends_on:
      mcp-server:
        condition: service_healthy

  mcp-server:
    build:
      context: ./mcp-server
    expose:
      - "8000"
    healthcheck:
      test: ["CMD", "curl", "-Isf", "http://localhost:8000/sse"]
      interval: 2s
      timeout: 5s
      retries: 15
      start_period: 5s
  • main is the agent's container — Harbor configures it automatically. You only add overrides like depends_on.
  • Additional services (here mcp-server) are defined normally. They share a Docker network with main, so the agent can reach them by service name.

MCP server

The server uses FastMCP and exposes a single tool over SSE:

environment/mcp-server/server.py
from fastmcp import FastMCP

mcp = FastMCP("hello-mcp")

SECRET_VALUE = "harbor-mcp-secret-12345"

@mcp.tool()
def get_secret() -> str:
    """Returns a secret value."""
    return SECRET_VALUE

if __name__ == "__main__":
    mcp.run(transport="sse", host="0.0.0.0", port=8000)

Instruction

instruction.md
There is an MCP server running at `http://mcp-server:8000/sse`
that exposes a tool called `get_secret`.

1. Connect to the MCP server
2. Call the `get_secret` tool
3. Write the returned secret value to `/app/secret.txt`

Verification

The test script runs a pytest check that compares the file contents to the expected secret:

tests/test_outputs.py
SECRET_VALUE = "harbor-mcp-secret-12345"

def test_secret_file_exists():
    with open("/app/secret.txt") as f:
        content = f.read()
    assert content.strip() == SECRET_VALUE

Running it

# Verify with the oracle agent (runs solution/solve.sh)
harbor run -p examples/tasks/hello-mcp -a oracle

# Test with a real agent
harbor run -p examples/tasks/hello-mcp -a claude-code -m anthropic/claude-sonnet-4-5

Docker Compose & cloud providers

Docker Compose tasks currently only work with the local Docker environment (--env docker). Most cloud sandbox providers only support single-Dockerfile environments. We are actively working on multi-container support for cloud sandbox providers.

On this page