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.pyDocker 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.
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: 5smainis the agent's container — Harbor configures it automatically. You only add overrides likedepends_on.- Additional services (here
mcp-server) are defined normally. They share a Docker network withmain, so the agent can reach them by service name.
MCP server
The server uses FastMCP and exposes a single tool over SSE:
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
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:
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_VALUERunning 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-5Docker 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.