coder: let the omp harness drive workspaces over Coders MCP server
Coder ships `coder exp mcp server` and omp is an MCP client, so the two compose with no custom code. The agent stays on the host where its credentials already live and reaches into the workspace, rather than shipping secrets into a container. - scripts/coder-mcp.sh wraps the server, passing a dedicated `omp-mcp` token via `docker exec --env-file` (mode 600 temp file, shredded by an EXIT trap) so the token never appears in argv or ps - allow-list exposes 26 of 32 tools; template mutation is excluded because pushing a template is the one real path from a Coder principal to the host docker socket - scripts/coder-token-rotate.sh handles the 168h token lifetime cap: mint, verify against the API, then revoke the predecessors - .omp/mcp.json registers it project-scoped so 26 tools do not load into every unrelated omp session The docker exec must stay in the foreground: backgrounding it makes bash redirect stdin from /dev/null with job control disabled, and the MCP transport dies on EOF with "MCP tool load failed ... Transport closed". Verified: fresh omp session in this directory answered from the tools - coder_get_authenticated_user returned loca, workspace ws1 running.
This commit is contained in:
@@ -132,6 +132,41 @@ The risk, stated plainly: `/var/run/docker.sock` is bind-mounted into the `coder
|
||||
- `mem_limit` bounds both containers, so a runaway build cannot swallow the host; see [[reference/limits]]
|
||||
- treat Template Admin as equivalent to host root and keep it to the single operator account
|
||||
|
||||
## 🤖 Driving workspaces from the omp harness (MCP)
|
||||
|
||||
Coder ships its own MCP server, and the `omp` harness is an MCP client, so the two compose natively. This is the deliberate direction of the integration: the agent runs on the host where its credentials already live and reaches into the workspace, rather than installing an agent inside the container and shipping secrets to it.
|
||||
|
||||
Wiring:
|
||||
|
||||
- server: `coder exp mcp server`, run inside the `coder` container, speaking MCP over stdio. Confirmed `serverInfo {name: "Coder Agent", version: "v2.35.3+65e2bfb"}`
|
||||
- wrapper: `scripts/coder-mcp.sh`, which supplies credentials and the tool allow-list
|
||||
- registration: `/home/loca/dev/coder/.omp/mcp.json`, project-scoped on purpose. The server exposes 26 tools; loading those into every unrelated omp session would waste context, so it only mounts when the working directory is this project
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"coder": { "command": "/home/loca/dev/coder/scripts/coder-mcp.sh", "args": [], "disabled": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Credentials:
|
||||
|
||||
- a dedicated Coder token named `omp-mcp`, not the operator session token, so it can be revoked alone. `.mcp-token`, mode 600, gitignored
|
||||
- this deployment caps token lifetime at 168h (7d); `--lifetime 30d` is rejected outright. Rotate weekly with `scripts/coder-token-rotate.sh`, which mints the replacement, verifies it with a real API call, and only then revokes the previous ones
|
||||
- the token is handed to the container through `docker exec --env-file` with a mode-600 temp file that an EXIT trap shreds. It is never an argument, so it never appears in `ps` or in an agent transcript
|
||||
|
||||
Tool allow-list, and why:
|
||||
|
||||
- 26 of 32 tools are exposed via `--allowed-tools`
|
||||
- excluded: `coder_create_template`, `coder_create_template_version`, `coder_update_template_active_version`, `coder_delete_template`, `coder_delete_task`, `coder_upload_tar_file`
|
||||
- rationale: pushing a template is the one real path from a Coder principal to the host docker socket, as described in the section above. Template mutation stays a human action; the agent gets read-only template inspection plus full workspace access. Verified: a `tools/list` through the wrapper returns 26 names and none of the excluded ones
|
||||
- kept: `coder_workspace_bash`, `coder_workspace_read_file` / `write_file` / `edit_file` / `edit_files` / `ls`, `coder_workspace_list_apps`, `coder_workspace_port_forward`, agent and build logs, workspace create plus `coder_create_workspace_build` for start/stop/delete, the task tools, and `search` / `fetch`
|
||||
|
||||
Wrapper trap worth knowing: the `docker exec` must run in the foreground. Backgrounding it with `&` plus `wait` looks harmless but breaks the server, because with job control disabled bash redirects an async command's stdin from `/dev/null`; the MCP stdio transport then sees EOF immediately and the client logs `MCP tool load failed ... Transport closed` with no tools registered.
|
||||
|
||||
Verified end to end 2026-08-20: a fresh `omp` session started in `/home/loca/dev/coder` mounted the tools and answered from them — `coder_get_authenticated_user` returned `loca`, `coder_list_workspaces` plus `coder_get_workspace` returned `ws1` `running`.
|
||||
|
||||
## 📚 Sources
|
||||
|
||||
- https://github.com/coder/coder/blob/main/examples/templates/docker/main.tf
|
||||
|
||||
Reference in New Issue
Block a user