bots-garden/mini-mepublic Fork
⑂ fix/tool-order
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
📦 Mini Me ab6a77c · on fix/tool-order · k33g · 24d ago
run-in-a-docker-sandbox.md · 182 lines · 14.0 KBmarkdown
Blame HistoryOpen raw

How to run mini-me in a Docker Sandbox

This guide shows how to run mm inside a Docker Sandbox, so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the sbx command, and a model server (Docker Model Runner or llama-server) running on your machine.

These are the v2 kits (spec.yaml). The same two kits also exist for the kit specification v3, under docker-sbx-v3/: see Run mini-me with the v3 sandbox kits.

Everything lives under docker-sbx/ in the repository:

Piece What it is
template/Dockerfile The template: Docker Sandboxes' shell image with mm built from this checkout, plus the two entry points below.
kits/mm-tui/spec.yaml A sandbox kit: sbx run on it opens mm's window mode on the workspace.
kits/mm-acp/spec.yaml A sandbox kit for an editor: created once with sbx create, the editor then runs mm -acp inside it.
build.sh Builds the template image, k33g/mm-sandbox:<version>; PUSH=1 builds it for linux/amd64 and linux/arm64 and pushes it.
sync-kits.sh, push-kits.sh Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts.

Use the published kits

Every release publishes the image and both kits to Docker Hub at mm's version — k33g/mm-sandbox:0.2.1, k33g/mm-tui:0.2.1, k33g/mm-acp:0.2.1 — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (sbx settings get kit.allowedSources shows docker.io/), and a reference with a / but no registry host is read as docker.io/…:

cd /path/to/your/project
sbx run k33g/mm-tui:0.2.1 . -- agent.dmr.yaml

for the window mode, and for an editor:

sbx create --name mm-acp k33g/mm-acp:0.2.1 . --kit-arg config=agent.dmr.yaml

then the editor registration of step 3 below. No --pull never: the kit names k33g/mm-sandbox:0.2.1 and sbx pulls it. sbx kit inspect k33g/mm-tui:0.2.1 shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm.

Steps

1. Build the template

From the repository root:

./docker-sbx/build.sh

This reads mm's tag from release.env (v0.2.1) and builds k33g/mm-sandbox:0.2.1: the image tag is the version without the v, like the release assets, and mm inside is stamped with the tag — mm --version is run in the image as proof. The script then rewrites the kits' default tag argument to 0.2.1, so the kits and the image move together. ./docker-sbx/build.sh v0.1.5 builds another version; an earlier image stays reachable with --kit-arg tag=0.1.4.

The image is in your local Docker image store: every sbx run or sbx create on these kits takes --pull never until the image is pushed (docker push k33g/mm-sandbox:0.2.1), or sbx tries to pull it and fails. If sbx runs on another machine, EXPORT=1 ./docker-sbx/build.sh also writes tmp/mm-sandbox-0.2.1.tar, to load there with sbx template load.

2. Window mode in the terminal (kit mm-tui)

From the project you want mm to work on:

sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml

The first time, sbx creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows -- as arguments — here mm -tui agent.dmr.yaml, in the workspace. Any mm flag passes the same way: -- -config other.yaml.

Without anything after --, mm reads the kit's config argument, then ./agent.yaml in the workspace, then its built-in defaults:

sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml
--kit-arg Default Effect
config=<path> empty The configuration file, as mm's AGENT_CONFIG: relative to the workspace, or absolute inside the sandbox.
tag=<version> the version last built (0.2.1) Which k33g/mm-sandbox:<version> image the sandbox is made from.

The sandbox keeps its name (mm-tui-<directory> unless you pass --name); sbx run --name <name> re-attaches to it later. Inside, the window is the one described in Use the window mode. Sessions, when sessions.enabled is on, are written under the workspace's .mm/sessions — that is your host directory, so they survive the sandbox.

3. From an editor (kit mm-acp)

An editor talks to mm -acp over the agent's raw stdin and stdout. sbx run cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts mm inside it with sbx exec -i, which pipes stdin and stdout untouched.

  1. From the project the editor will open, create the sandbox with the configuration file baked in (. is the workspace, sbx makes it absolute; the kit path must be an explicit ./, ../ or absolute path):

    cd /path/to/your/project
    sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \
        --kit-arg config=agent.dmr.yaml
    

    A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer N and fix the path.

    --kit-arg config becomes the container's AGENT_CONFIG, relative to the workspace because that is where sbx exec starts mm. The workspace is fixed at creation: one sandbox per project, each with its own --name.

  2. Register it in the editor. In Zed's settings.json:

    {
      "agent_servers": {
        "mini-me (sandbox)": {
          "type": "custom",
          "command": "sbx",
          "args": ["exec", "-i", "mm-acp", "mm", "-acp"]
        }
      }
    }
    

    Compared with the host-side setup, the command is sbx and no env is needed: AGENT_CONFIG is already in the container. If the editor cannot find sbx, put the absolute path from which sbx in "command".

  3. Open the agent panel and pick mini-me (sandbox). The editor starts mm -acp in the sandbox for every conversation; sbx exec starts the sandbox first when it is stopped. Tool calls, permission dialogs, @file mentions and the slash-command menu work as they do on the host — the difference is where the commands run.

Attaching to this sandbox with sbx run --name mm-acp does not start mm: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. sbx exec -it mm-acp bash is the shorter way to that shell.

The model server stays on the host

Inside a sandbox localhost is the sandbox itself. Your model server keeps running on your machine and is reached through host.docker.internal, which the sandbox proxy maps to your host's localhost.

  • Docker Model Runner — nothing to change: agent.dmr.yaml already has fallback: http://host.docker.internal:12434/engines/v1, and mm switches to it when localhost:12434 does not answer (the banner says which URL won).
  • llama-server — agent.llamacpp.yaml has no fallback. Either edit its baseUrl to http://host.docker.internal:8080/v1, or pass --env AGENT_BASE_URL=http://host.docker.internal:8080/v1 to sbx run / sbx create (see Run with llama.cpp).

Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: sbx policy allow network host.docker.internal:<port>.

Publishing a version

Publishing is a manual step, from a machine with Docker Desktop (buildx and the sbx CLI); the Release workflow builds only the binaries. One script builds the image for linux/amd64 and linux/arm64, pushes it, then validates and pushes the two kits, all at mm's tag:

export DOCKER_HANDLE=k33g          # the Docker Hub account, also the image owner
export DOCKER_PAT=dckr_pat_…       # an access token with read/write scope
./docker-sbx/publish.sh            # TAG from release.env: v0.2.1 → k33g/mm-sandbox:0.2.1, k33g/mm-tui:0.2.1, k33g/mm-acp:0.2.1

With both variables set, the script logs in with them and logs out when it is done; with neither, it reuses your existing docker login. Its two halves run alone as PUSH=1 ./docker-sbx/build.sh v0.2.1 (image) and ./docker-sbx/push-kits.sh v0.2.1 (kits, SBX=/path/to/sbx if the CLI is not on the PATH). Both call sync-kits.sh first, which rewrites the kits' version, default tag and image owner, so a published kit always names the image pushed with it — commit the two spec.yaml afterwards. sbx kit push talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. IMAGE_OWNER=<handle> publishes under another account.

MCP servers through the sbx gateway (GitHub as the example)

Inside a sandbox, mm does not talk to MCP servers itself and holds no token. sbx starts one MCP gateway per sandbox, on the host. When it creates the sandbox, it bakes two variables into the container: MCP_GATEWAY_URL (http://mcp-gateway.docker.internal/mcp) and MCP_SENTINEL_TOKEN_NAME, a placeholder that the sandbox proxy swaps for the real credential. When mm finds MCP_GATEWAY_URL and mcp.enabled: true, it adds a server called sbx-gateway, and its tools show up as sbx-gateway_<tool>. The kit needs no network rule for it, because the proxy routes that host itself.

  1. Register GitHub's official server once, on the host, through docker-sbx/mcp/github-mcp.sh. The script starts ghcr.io/github/github-mcp-server in Docker with your token. sbx runs it as a child of sandboxd, which does not see your shell's variables. So store the token once in the macOS keychain under the service github-mcp. The script also accepts GITHUB_PERSONAL_ACCESS_TOKEN, GITHUB_OAUTH_TOKEN or a logged-in gh. sbx runs it on the host, so the token never enters the sandbox. By default it exposes a read-only set of 25 tools, including get_me and get_teams. Set GITHUB_READ_ONLY=0 and a wider GITHUB_TOOLSETS in the script's environment for more.

    security add-generic-password -U -a "$USER" -s github-mcp -w   # prompts for the token
    sbx mcp rm github 2>/dev/null               # an earlier "github" is the ARCHIVED catalog server
    sbx mcp add github-mm --command "$PWD/docker-sbx/mcp/github-mcp.sh"
    sbx mcp ls                                  # github-mm must be ready
    

    Do not use the name github alone. The Docker MCP catalog resolves it to "GitHub (Archived)", the old npm server, which has no get_me and returns other tools. The catalog's github-official does not help in sbx's local mode either. There, a catalog image is started as a bare docker run with no environment, so it would get no token.

  2. Create a new sandbox with that server in its fixed set. --static-mcp is an sbx flag: it goes before --, since everything after -- goes to mm. It only applies at creation, so remove an older sandbox of the same name first:

    cd /path/to/project
    sbx rm mm-mcp-github 2>/dev/null
    sbx run --name mm-mcp-github --static-mcp github-mm k33g/mm-tui:<version> . -- agent.dmr.sbx.yaml
    
  3. The configuration only needs MCP turned on. demo/agent.dmr.sbx.yaml has:

    mcp:
      enabled: true
      permissions: ask
      discoveryTimeout: 10s
      servers: []        # the gateway is added by mm when MCP_GATEWAY_URL is set
    

    The banner then lists sbx-gateway_get_me, sbx-gateway_list_issues, and so on in tools:.

A kit image built before this change cannot use the gateway. It discovers nothing and warns method "tools/list" is invalid during session initialization. The gateway follows MCP's Streamable HTTP transport strictly: it needs initialize first, then the Mcp-Session-Id it returns on every request. Older mm skipped both. That was harmless against api.githubcopilot.com but fatal here. Rebuild and publish the kit after upgrading.

If the banner lists no sbx-gateway_* tools, check the registration with sbx mcp inspect github-mm, then read sbx's MCP log, mcp/mcp.log under its state directory. github-mcp: no token there means the script found no token: check the keychain item with security find-generic-password -s github-mcp. The remote endpoint https://api.githubcopilot.com/mcp/ is another route, but only with a GitHub OAuth App. GitHub offers no dynamic client registration, so in local mode sbx mcp add refuses it without --client-id, even with an Authorization header.

Name the repository in your requests, for example "list the open issues of k33g/my-repo". A small model given only "my organization" invents an owner and a repository, and every call comes back Not Found.

Variants

  • Another configuration for one editor session. Add "-e", "AGENT_CONFIG=agent.lsp.yaml" before the sandbox name in "args"; sbx exec -e overrides the baked-in value for that run only.
  • Skills. skillsDir is relative to the directory mm runs in, which is the workspace in both kits — the same rule as on the host.
  • Docker inside the sandbox. Build the template on the Docker-in-Docker shell instead: SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh.
  • Read the logs. In the window mode they are on screen; over ACP they go to stderr, which Zed shows under dev: open acp logs.
  • Remove a sandbox. sbx rm mm-acp. The project directory is a bind mount and is not touched.

See also

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# How to run mini-me in a Docker Sandbox

This guide shows how to run `mm` inside a [Docker Sandbox](https://docs.docker.com/ai/sandboxes/), so that every command the model runs and every file it edits happens in a container, on a mounted copy of your project. It covers the two ways of using it: the window mode in your terminal, and from an editor through the Agent Client Protocol. It assumes Docker Desktop with the `sbx` command, and a model server (Docker Model Runner or llama-server) running on your machine.

> These are the **v2** kits (`spec.yaml`). The same two kits also exist for the kit specification v3, under `docker-sbx-v3/`: see [Run mini-me with the v3 sandbox kits](run-with-sandbox-kits-v3.md).

Everything lives under [`docker-sbx/`](../../../docker-sbx/README.md) in the repository:

| Piece | What it is |
|---|---|
| `template/Dockerfile` | The **template**: Docker Sandboxes' `shell` image with `mm` built from this checkout, plus the two entry points below. |
| `kits/mm-tui/spec.yaml` | A **sandbox kit**: `sbx run` on it opens `mm`'s window mode on the workspace. |
| `kits/mm-acp/spec.yaml` | A **sandbox kit** for an editor: created once with `sbx create`, the editor then runs `mm -acp` inside it. |
| `build.sh` | Builds the template image, `k33g/mm-sandbox:<version>`; `PUSH=1` builds it for linux/amd64 and linux/arm64 and pushes it. |
| `sync-kits.sh`, `push-kits.sh` | Point the kits at one image, and publish them to Docker Hub as OCI kit artifacts. |

## Use the published kits

Every release publishes the image and both kits to Docker Hub at mm's version — `k33g/mm-sandbox:0.2.1`, `k33g/mm-tui:0.2.1`, `k33g/mm-acp:0.2.1` — so the shortest path needs no build at all. Docker Hub is an allowed kit source by default (`sbx settings get kit.allowedSources` shows `docker.io/`), and a reference with a `/` but no registry host is read as `docker.io/…`:

```bash
cd /path/to/your/project
sbx run k33g/mm-tui:0.2.1 . -- agent.dmr.yaml
```

for the window mode, and for an editor:

```bash
sbx create --name mm-acp k33g/mm-acp:0.2.1 . --kit-arg config=agent.dmr.yaml
```

then the editor registration of step 3 below. No `--pull never`: the kit names `k33g/mm-sandbox:0.2.1` and sbx pulls it. `sbx kit inspect k33g/mm-tui:0.2.1` shows what a kit declares before you run it. The steps that follow build the same thing locally, for a work-in-progress mm.

## Steps

### 1. Build the template

From the repository root:

```bash
./docker-sbx/build.sh
```

This reads mm's tag from `release.env` (`v0.2.1`) and builds `k33g/mm-sandbox:0.2.1`: the image tag is the version without the `v`, like the release assets, and `mm` inside is stamped with the tag — `mm --version` is run in the image as proof. The script then rewrites the kits' default `tag` argument to `0.2.1`, so the kits and the image move together. `./docker-sbx/build.sh v0.1.5` builds another version; an earlier image stays reachable with `--kit-arg tag=0.1.4`.

The image is in your local Docker image store: every `sbx run` or `sbx create` on these kits takes `--pull never` until the image is pushed (`docker push k33g/mm-sandbox:0.2.1`), or `sbx` tries to pull it and fails. If `sbx` runs on another machine, `EXPORT=1 ./docker-sbx/build.sh` also writes `tmp/mm-sandbox-0.2.1.tar`, to load there with `sbx template load`.

### 2. Window mode in the terminal (kit `mm-tui`)

From the project you want `mm` to work on:

```bash
sbx run --pull never ./docker-sbx/kits/mm-tui . -- agent.dmr.yaml
```

The first time, `sbx` creates the sandbox, mounts the current directory as its workspace and attaches; then it runs the kit's entry point with what follows `--` as arguments — here `mm -tui agent.dmr.yaml`, in the workspace. Any `mm` flag passes the same way: `-- -config other.yaml`.

Without anything after `--`, `mm` reads the kit's `config` argument, then `./agent.yaml` in the workspace, then its built-in defaults:

```bash
sbx run --pull never ./docker-sbx/kits/mm-tui . --kit-arg config=agent.dmr.yaml
```

| `--kit-arg` | Default | Effect |
|---|---|---|
| `config=<path>` | empty | The configuration file, as `mm`'s `AGENT_CONFIG`: relative to the workspace, or absolute inside the sandbox. |
| `tag=<version>` | the version last built (`0.2.1`) | Which `k33g/mm-sandbox:<version>` image the sandbox is made from. |

The sandbox keeps its name (`mm-tui-<directory>` unless you pass `--name`); `sbx run --name <name>` re-attaches to it later. Inside, the window is the one described in [Use the window mode](use-the-window-mode.md). Sessions, when `sessions.enabled` is on, are written under the workspace's `.mm/sessions` — that is your host directory, so they survive the sandbox.

### 3. From an editor (kit `mm-acp`)

An editor talks to `mm -acp` over the agent's raw stdin and stdout. `sbx run` cannot carry that: it always allocates a terminal. So the sandbox is created once without attaching, and the editor starts `mm` inside it with `sbx exec -i`, which pipes stdin and stdout untouched.

1. From the project the editor will open, create the sandbox with the configuration file baked in (`.` is the workspace, sbx makes it absolute; the kit path must be an explicit `./`, `../` or absolute path):

   ```bash
   cd /path/to/your/project
   sbx create --pull never --name mm-acp /path/to/mini-me/docker-sbx/kits/mm-acp . \
       --kit-arg config=agent.dmr.yaml
   ```

   A workspace path that does not exist on your machine makes sbx ask "The selected workspace does not exist. Would you like to create it?" — answer `N` and fix the path.

   `--kit-arg config` becomes the container's `AGENT_CONFIG`, relative to the workspace because that is where `sbx exec` starts `mm`. The workspace is fixed at creation: one sandbox per project, each with its own `--name`.

2. Register it in the editor. In Zed's `settings.json`:

   ```json
   {
     "agent_servers": {
       "mini-me (sandbox)": {
         "type": "custom",
         "command": "sbx",
         "args": ["exec", "-i", "mm-acp", "mm", "-acp"]
       }
     }
   }
   ```

   Compared with [the host-side setup](use-from-an-editor.md), the command is `sbx` and no `env` is needed: `AGENT_CONFIG` is already in the container. If the editor cannot find `sbx`, put the absolute path from `which sbx` in `"command"`.

3. Open the agent panel and pick **mini-me (sandbox)**. The editor starts `mm -acp` in the sandbox for every conversation; `sbx exec` starts the sandbox first when it is stopped. Tool calls, permission dialogs, `@file` mentions and the slash-command menu work as they do on the host — the difference is where the commands run.

Attaching to this sandbox with `sbx run --name mm-acp` does not start `mm`: its entry point prints the wiring above with the sandbox's real name and paths, then opens a shell inside the sandbox. `sbx exec -it mm-acp bash` is the shorter way to that shell.

## The model server stays on the host

Inside a sandbox `localhost` is the sandbox itself. Your model server keeps running on your machine and is reached through `host.docker.internal`, which the sandbox proxy maps to your host's `localhost`.

- **Docker Model Runner** — nothing to change: `agent.dmr.yaml` already has `fallback: http://host.docker.internal:12434/engines/v1`, and `mm` switches to it when `localhost:12434` does not answer (the banner says which URL won).
- **llama-server** — `agent.llamacpp.yaml` has no fallback. Either edit its `baseUrl` to `http://host.docker.internal:8080/v1`, or pass `--env AGENT_BASE_URL=http://host.docker.internal:8080/v1` to `sbx run` / `sbx create` (see [Run with llama.cpp](run-with-llama-cpp.md)).

Both kits allow ports 12434 and 8080 on the host in the sandbox's network policy. For another port: `sbx policy allow network host.docker.internal:<port>`.

## Publishing a version

Publishing is a manual step, from a machine with Docker Desktop (buildx and the `sbx` CLI); the Release workflow builds only the binaries. One script builds the image for linux/amd64 and linux/arm64, pushes it, then validates and pushes the two kits, all at mm's tag:

```bash
export DOCKER_HANDLE=k33g          # the Docker Hub account, also the image owner
export DOCKER_PAT=dckr_pat_…       # an access token with read/write scope
./docker-sbx/publish.sh            # TAG from release.env: v0.2.1 → k33g/mm-sandbox:0.2.1, k33g/mm-tui:0.2.1, k33g/mm-acp:0.2.1
```

With both variables set, the script logs in with them and logs out when it is done; with neither, it reuses your existing `docker login`. Its two halves run alone as `PUSH=1 ./docker-sbx/build.sh v0.2.1` (image) and `./docker-sbx/push-kits.sh v0.2.1` (kits, `SBX=/path/to/sbx` if the CLI is not on the PATH). Both call `sync-kits.sh` first, which rewrites the kits' `version`, default `tag` and image owner, so a published kit always names the image pushed with it — commit the two `spec.yaml` afterwards. `sbx kit push` talks to the registry directly and authenticates from the Docker credential store, no sandbox daemon involved. `IMAGE_OWNER=<handle>` publishes under another account.

## MCP servers through the sbx gateway (GitHub as the example)

Inside a sandbox, `mm` does not talk to MCP servers itself and holds no token. `sbx` starts one MCP gateway per sandbox, on the host. When it creates the sandbox, it bakes two variables into the container: `MCP_GATEWAY_URL` (`http://mcp-gateway.docker.internal/mcp`) and `MCP_SENTINEL_TOKEN_NAME`, a placeholder that the sandbox proxy swaps for the real credential. When `mm` finds `MCP_GATEWAY_URL` and `mcp.enabled: true`, it adds a server called `sbx-gateway`, and its tools show up as `sbx-gateway_<tool>`. The kit needs no network rule for it, because the proxy routes that host itself.

1. Register GitHub's official server once, on the host, through `docker-sbx/mcp/github-mcp.sh`. The script starts `ghcr.io/github/github-mcp-server` in Docker with your token. sbx runs it as a child of sandboxd, which does not see your shell's variables. So store the token once in the macOS keychain under the service `github-mcp`. The script also accepts `GITHUB_PERSONAL_ACCESS_TOKEN`, `GITHUB_OAUTH_TOKEN` or a logged-in `gh`. sbx runs it on the host, so the token never enters the sandbox. By default it exposes a read-only set of 25 tools, including `get_me` and `get_teams`. Set `GITHUB_READ_ONLY=0` and a wider `GITHUB_TOOLSETS` in the script's environment for more.

   ```bash
   security add-generic-password -U -a "$USER" -s github-mcp -w   # prompts for the token
   sbx mcp rm github 2>/dev/null               # an earlier "github" is the ARCHIVED catalog server
   sbx mcp add github-mm --command "$PWD/docker-sbx/mcp/github-mcp.sh"
   sbx mcp ls                                  # github-mm must be ready
   ```

   Do not use the name `github` alone. The Docker MCP catalog resolves it to "GitHub (Archived)", the old npm server, which has no `get_me` and returns other tools. The catalog's `github-official` does not help in sbx's local mode either. There, a catalog image is started as a bare `docker run` with no environment, so it would get no token.

2. Create a **new** sandbox with that server in its fixed set. `--static-mcp` is an `sbx` flag: it goes **before** `--`, since everything after `--` goes to `mm`. It only applies at creation, so remove an older sandbox of the same name first:

   ```bash
   cd /path/to/project
   sbx rm mm-mcp-github 2>/dev/null
   sbx run --name mm-mcp-github --static-mcp github-mm k33g/mm-tui:<version> . -- agent.dmr.sbx.yaml
   ```

3. The configuration only needs MCP turned on. `demo/agent.dmr.sbx.yaml` has:

   ```yaml
   mcp:
     enabled: true
     permissions: ask
     discoveryTimeout: 10s
     servers: []        # the gateway is added by mm when MCP_GATEWAY_URL is set
   ```

   The banner then lists `sbx-gateway_get_me`, `sbx-gateway_list_issues`, and so on in `tools:`.

A kit image built before this change cannot use the gateway. It discovers nothing and warns `method "tools/list" is invalid during session initialization`. The gateway follows MCP's Streamable HTTP transport strictly: it needs `initialize` first, then the `Mcp-Session-Id` it returns on every request. Older `mm` skipped both. That was harmless against `api.githubcopilot.com` but fatal here. Rebuild and publish the kit after upgrading.

If the banner lists no `sbx-gateway_*` tools, check the registration with `sbx mcp inspect github-mm`, then read sbx's MCP log, `mcp/mcp.log` under its state directory. `github-mcp: no token` there means the script found no token: check the keychain item with `security find-generic-password -s github-mcp`. The remote endpoint `https://api.githubcopilot.com/mcp/` is another route, but only with a GitHub OAuth App. GitHub offers no dynamic client registration, so in local mode `sbx mcp add` refuses it without `--client-id`, even with an `Authorization` header.

Name the repository in your requests, for example "list the open issues of k33g/my-repo". A small model given only "my organization" invents an owner and a repository, and every call comes back `Not Found`.

## Variants

- **Another configuration for one editor session.** Add `"-e", "AGENT_CONFIG=agent.lsp.yaml"` before the sandbox name in `"args"`; `sbx exec -e` overrides the baked-in value for that run only.
- **Skills.** `skillsDir` is relative to the directory `mm` runs in, which is the workspace in both kits — the same rule as on the host.
- **Docker inside the sandbox.** Build the template on the Docker-in-Docker shell instead: `SHELL_TEMPLATE=docker/sandbox-templates:shell-docker ./docker-sbx/build.sh`.
- **Read the logs.** In the window mode they are on screen; over ACP they go to stderr, which Zed shows under `dev: open acp logs`.
- **Remove a sandbox.** `sbx rm mm-acp`. The project directory is a bind mount and is not touched.

## See also

- The kit format these two files follow: the Docker Sandboxes kit specification (`schemaVersion: "2"`), bundled with the `sbx` sources.
- The same kits for the kit specification v3: [Run mini-me with the v3 sandbox kits](run-with-sandbox-kits-v3.md).
- What `mm -acp` exposes to the editor: [ACP reference](../reference/acp.md).
- The flags and environment variables `mm` reads: [command line reference](../reference/cli.md).