Skip to main content
Glama
SomeNewKid

McpSidecar

by SomeNewKid
README.md
# McpSidecar

McpSidecar is a Python command-line project for running an OpenAI Agents SDK
workload inside a hardened, disposable Docker sandbox with a colocated MCP
server sidecar.

The project started from the sibling SandboxAgent repository, but changes the
topology so the AI agent container is complemented by:

- a Squid proxy container that controls network egress
- an MCP server container that exposes local MCP tools and resources
- a Docker internal network that lets the agent call the MCP sidecar without
  exposing the sidecar directly to the host

> [!WARNING]
> This is an experimental sandboxing and MCP-sidecar project and should not be
> treated as a finished security model.

The current workload uses the OpenAI Agents SDK to answer a Microsoft Learn
code-sample question. The agent reads a local answer-format resource from the
MCP sidecar, calls a Microsoft Learn code-sample search tool through that same
sidecar, saves the final answer as `answer.txt`, and prints the answer to
stdout.

## Current Workflow

Run the project from the repository root:

```powershell
.\.venv\Scripts\python.exe -m sandbox_agent
```

The host-side command loads `src/sandbox_agent/sandbox_spec.toml`, generates a
Dockerfile and low-level Docker profile from that spec, builds or reuses a
hash-tagged Docker image for the agent, builds or reuses the MCP sidecar image,
creates a per-run Docker network, starts the Squid proxy container, starts the
MCP sidecar container, starts a disposable agent container, streams the agent's
stdout and stderr to the terminal, persists artifacts, and then removes the
disposable containers and network.

Inside the agent container, Sandbox Agent:

1. Creates an OpenAI Agents SDK agent.
2. Reads the MCP resource `mcp-sidecar://instructions/answer-format.md`.
3. Calls the MCP tool `microsoft_code_sample_search` with `language="csharp"`.
4. Answers how an instance of `HttpClient` should be obtained when using C# and
   .NET 8.
5. Saves the exact answer text to `/sandbox-output/answer.txt`.
6. Prints the same answer text to stdout.

Run artifacts are written under `.docker_sandbox/runs/run-*`.

## Sandbox Spec

The sandbox is driven by a declarative TOML file:

```toml
schema_version = 1
capabilities = [
  "network",
  "mcp_client",
  "openai_agents",
]
allowed_domains = [
  ".example.com",
  ".gov.uk",
  ".microsoft.com"
]
allowed_ip_addresses = []
```

The design rule is that capabilities soften the sandbox only when necessary.
Unknown keys and unsupported capability values fail closed.

`allowed_domains` and `allowed_ip_addresses` refine the `network` capability.
They do not enable networking by themselves.

Environment variables can be declared explicitly:

```toml
[[environment_variables]]
name = "API_BASE_URL"
value = "https://example.com"
```

Or copied from the host:

```toml
[[environment_variables]]
name = "OPENAI_API_KEY"
from_host = true
```

OpenAI-backed capabilities add `OPENAI_API_KEY` automatically where the sandbox
system knows the provider convention.

## Capabilities

The current workload uses these capabilities:

- `network`: enables the Squid egress gateway. Network access is default-deny
  unless domains or IP addresses are allowed by the resolved profile.
- `mcp_client`: installs the Python MCP client package in the agent image and
  requires the `network` capability.
- `openai_agents`: installs `openai-agents`, requires `network`, adds
  `.openai.com`, and forwards `OPENAI_API_KEY` from the host.

The MCP sidecar itself is started by the host-side Docker harness when running
the `sandbox_agent` workload with a network gateway.

## MCP Sidecar

The sidecar package can run directly:

```powershell
.\.venv\Scripts\python.exe -m mcp_sidecar --host 0.0.0.0 --port 8000
```

By default it uses streamable HTTP on port `8000`. The Docker harness starts it
on the internal Docker network with the alias `mcp-sidecar`, and passes this
connection string into the agent container:

```text
MCP_SIDECAR_URL=http://mcp-sidecar:8000/mcp
```

The sidecar currently exposes these MCP tools:

- `get_html_element_name`: returns a hard-coded HTML element name used by an
  earlier sample workload.
- `microsoft_docs_search`: proxies Microsoft Learn documentation search.
- `microsoft_docs_fetch`: proxies Microsoft Learn documentation fetch.
- `microsoft_code_sample_search`: proxies Microsoft Learn code sample search.

The sidecar currently exposes this MCP resource:

- `mcp-sidecar://instructions/answer-format.md`: a Markdown answer format that
  the agent must read before composing its final answer.

The Microsoft wrapper tools call the public Microsoft Learn MCP endpoint:

```text
https://learn.microsoft.com/api/mcp
```

When the sidecar is started by the Docker harness, it receives proxy environment
variables so its outbound HTTP and HTTPS traffic goes through the Squid gateway:

```text
HTTP_PROXY=http://egress-gateway:3128
HTTPS_PROXY=http://egress-gateway:3128
NO_PROXY=localhost,127.0.0.1,mcp-sidecar
```

The MCP sidecar Docker image uses `python:3.12-slim` and installs the `mcp`
package. The host runner bind-mounts `src/mcp_sidecar` into the sidecar
container as read-only source so sidecar Python changes are visible to the next
run without rebuilding the image.

## Run Artifacts

A successful run directory contains files similar to:

```text
.docker_sandbox/runs/run-YYYY-mm-dd-HH-MM-SS/
  Dockerfile
  answer.txt
  config.json
  gateway-logs.json
  gateway-start-results.json
  landlock-policy.json
  mcp-sidecar-logs.json
  mcp-sidecar-metadata.json
  mcp-sidecar-start-results.json
  mcp-sidecar-stderr.txt
  mcp-sidecar-stdout.txt
  mcp-sidecar-tool-calls.jsonl
  resolved-profile.json
  run-metadata.json
  sandbox-spec.json
  seccomp-profile.json
  squid.conf
  stderr.txt
  stdout.txt
```

`answer.txt` contains the final answer saved by the agent. `stdout.txt`
contains the same answer as printed by the in-container process. `stderr.txt`
should normally be empty.

`mcp-sidecar-tool-calls.jsonl` is the sidecar audit log. It records MCP resource
reads and tool calls, including arguments, success or failure, and a short
result preview. A successful current run should include a `resource` record for
`mcp-sidecar://instructions/answer-format.md` and a `tool` record for
`microsoft_code_sample_search`.

The repository also includes `ARCHITECTURE.png`, a static infographic showing
the current host, Docker network, Squid proxy, MCP sidecar, AI agent container,
Microsoft Learn endpoint, and artifact flow.

## Sandbox Probes

The project can run the copied SandboxTester probe suite against the generated
sandbox:

```powershell
.\.venv\Scripts\python.exe -m sandbox_agent --test-sandbox
```

By default, probe evidence is redacted from `report.json`. To serialize evidence
for troubleshooting, run:

```powershell
.\.venv\Scripts\python.exe -m sandbox_agent --test-sandbox --serialize-evidence
```

## Requirements

- Python 3.11.
- PowerShell on Windows.
- Docker Desktop with Linux containers enabled.
- `OPENAI_API_KEY` in the host environment for the OpenAI Agents SDK workload.
- Network access during image builds to download Python packages and Docker base
  images.
- Network access during runs to allowed model-provider and Microsoft Learn
  endpoints through the Squid gateway.

Docker image builds can take several minutes the first time an image is created.

## Setup

Create the virtual environment and install development dependencies:

```powershell
.\scripts\setup-dev.ps1
```

The setup script expects Python 3.11 at the path configured in
`scripts\setup-dev.ps1`.

## Development Checks

Run formatting, linting, type checking, and tests:

```powershell
.\scripts\check.ps1
```

This runs:

- `ruff format .`
- `ruff check .`
- `pyright`
- `pytest`

## Architecture

The project has four main packages:

- `sandbox_agent`: the in-container workload. It owns the OpenAI Agents SDK
  prompt, tool adapters, MCP client calls into the sidecar, and answer saving.
- `mcp_sidecar`: the MCP server container workload. It owns local MCP resources,
  local MCP tools, Microsoft Learn proxy tools, streamable HTTP server setup,
  and sidecar audit logging.
- `docker_sandbox`: the host/container harness copied and adapted from the
  sibling sandbox projects. It owns sandbox spec loading, Dockerfile generation,
  profile resolution, image creation, Docker local network creation, Squid
  gateway setup, MCP sidecar startup, disposable agent container execution,
  artifact persistence, and teardown.
- `sandbox_tester`: the copied probe suite used by `--test-sandbox` to produce
  `report.json` for comparing sandbox behavior over time.

The command path deliberately differs by location:

- On the host, `python -m sandbox_agent` delegates to `docker_sandbox`.
- Inside the container, `python -m sandbox_agent` runs the workload.

The in-container path is guarded by `SANDBOX_AGENT_CONTAINER=1` and expected
mounts such as `/sandbox-output`, `/sandbox-work`, and `/sandbox-source/src`.

The default Docker topology for the current workload is:

```text
Docker host
  docker_sandbox host runner
    |
    +-- Docker internal network
          |
          +-- sandbox-agent-* container
          |     MCP_SIDECAR_URL=http://mcp-sidecar:8000/mcp
          |
          +-- mcp-sidecar-* container
          |     HTTP_PROXY=http://egress-gateway:3128
          |
          +-- squid proxy container
                network alias: egress-gateway
```

The agent container does not call the Microsoft Learn MCP endpoint directly.
It calls the local MCP sidecar, and the sidecar performs the upstream Microsoft
Learn MCP call through Squid.

## Project Structure

```text
ARCHITECTURE.png               Static Docker topology infographic

src/sandbox_agent/
  __main__.py                  Package entry point for python -m sandbox_agent
  cli.py                       Host delegation and in-container workload entry point
  openai_agent.py              OpenAI Agents SDK workload
  openai_tools.py              OpenAI Agents SDK tool adapters
  sandbox_spec.toml            Declarative sandbox capability spec
  tools.py                     Neutral tools, MCP client calls, and answer saving

src/mcp_sidecar/
  __main__.py                  Package entry point for python -m mcp_sidecar
  audit.py                     JSONL audit logging for resources and tools
  cli.py                       MCP sidecar command-line entry point
  resources.py                 Local MCP resources
  server.py                    FastMCP server construction
  tools.py                     Local and Microsoft Learn proxy tools
  dockerfile/Dockerfile        MCP sidecar image definition

src/docker_sandbox/
  __main__.py                  Package entry point for python -m docker_sandbox
  cli.py                       Docker sandbox command-line orchestration
  container_factory.py         Docker image inspection and build
  container_guard.py           Runtime guard for in-container execution
  landlock_runner.py           Linux Landlock path-policy launcher
  models.py                    Docker orchestration dataclasses
  profiles.py                  Legacy named profile definitions
  run_results.py               Local run artifact persistence
  sandbox_container.py         Containers, network, sidecars, artifacts, teardown
  sandbox_spec.py              Spec validation and profile/Dockerfile generation
  dockerfile/remove_python_packaging.py
  dockerfile/runtime_sitecustomize.py

src/sandbox_tester/
  Probe definitions and report generation used by --test-sandbox

tests/
  test_mcp_sidecar.py
  test_mcp_sidecar_container.py
  test_mcp_sidecar_dockerfile.py
  test_sandbox_agent_tools.py
  test_sandbox_spec.py
  test_sandbox_tester_serialization.py
  test_smoke.py

scripts/
  setup-dev.ps1
  check.ps1
```

## Notes

McpSidecar is a learning and hardening exercise, not a security proof. The
container policy reduces accidental host exposure and makes required capability
softening visible, but Docker, Landlock, seccomp, Squid, MCP tool boundaries,
and Python runtime guards should not be interpreted as a complete isolation
guarantee.

Generated content can vary between runs because it is model-generated. Model API
calls may incur usage costs.

Run artifacts under `.docker_sandbox/runs` are ignored by Git.

The Microsoft Learn MCP endpoint is treated as an upstream third-party MCP
server. The local sidecar currently proxies selected tools from that endpoint;
future versions may add authenticated upstream MCP servers or host-local
resources.

## Third-Party Notices

This project uses third-party packages including `mcp`, `openai`,
`openai-agents`, and `pillow`. It also uses Docker images such as
`python:3.12-slim` for the MCP sidecar and `ubuntu/squid:latest` for the Squid
gateway. See each package and image license metadata for details.

## License

GNU General Public License v3.0. See the `LICENSE` file for details.