83 lines
4.3 KiB
Markdown
83 lines
4.3 KiB
Markdown
# coding-agent-mcp
|
|
|
|
An MCP server for coding agents. It provides workspace-subpath-scoped file editing and typed build/test tasks, including Python tests.
|
|
|
|
## Security model
|
|
|
|
This is intentionally a single combined server: `coding-agent-mcp`. The old split servers and the generic `run_command` API were removed.
|
|
|
|
- HTTP requires an API key. Set `CODING_AGENT_MCP_API_KEYS` to `SUBJECT=TOKEN` entries separated by commas.
|
|
- An SSE connection creates a server-generated session bound to that authenticated subject. MCP requests must use that live session; callers cannot choose a session ID.
|
|
- The session's optional `workspaceSubpath` is bound when SSE opens and cannot change afterward. It is the only filesystem scope beneath the configured workspace root; the server-generated session ID is not part of the directory path.
|
|
- File and task paths are relative only. The client cannot select a root or use absolute paths.
|
|
- File paths are checked for traversal and symlink escapes.
|
|
- HTTP sends no permissive CORS header. Set `CODING_AGENT_MCP_CORS_ORIGINS` to an explicit comma-separated origin allowlist only when browser access is required.
|
|
- Remote HTTP command execution defaults to **disabled**. Set `CODING_AGENT_MCP_EXECUTION_BACKEND=local` only in the hardened runner deployment below.
|
|
|
|
`run_task` deliberately accepts no shell command, flags, environment variables, npm script name, or local executable. It maps a typed request to a fixed invocation. Project tests and build scripts still execute project code, so this validation is not a sandbox.
|
|
|
|
## Tools
|
|
|
|
Workspace tools: `list_files`, `read_file`, `read_files`, `read_binary_file`, `search_text`, `write_file`, `write_binary_file`, `apply_patch`, `make_directory`, `delete_path`, `move_path`, `rename_path`, and `file_info`.
|
|
|
|
Execution tool: `run_task`.
|
|
|
|
Example Python request:
|
|
|
|
```json
|
|
{
|
|
"runner": "pytest",
|
|
"task": "test",
|
|
"paths": ["tests/unit"],
|
|
"timeoutSeconds": 300
|
|
}
|
|
```
|
|
|
|
Supported runners are `pytest`, `npm`, `pnpm`, `yarn`, `maven`, `gradle`, `go`, and `cargo`. Supported tasks depend on the runner; the server rejects unsupported combinations.
|
|
|
|
## Local trusted use
|
|
|
|
Stdio is intended for a local, trusted client and enables typed task execution by default:
|
|
|
|
```bash
|
|
npm run start:coding-agent -- --root /absolute/path/to/workspace
|
|
```
|
|
|
|
The runtime needs the selected tool installed. The supplied Docker image contains Python 3 and pytest.
|
|
|
|
## Remote hardened deployment
|
|
|
|
1. Copy `.env.example` to `.env` and set a long random API token.
|
|
2. Create a dedicated workspace directory; do not mount a home directory, source checkout, Docker socket, or secrets.
|
|
3. Make that directory writable by `CODING_AGENT_UID:CODING_AGENT_GID`.
|
|
4. Deploy with `docker compose up --build -d`.
|
|
|
|
The Compose deployment runs as a non-root UID, makes the container filesystem read-only, provides an ephemeral `/tmp`, removes Linux capabilities, applies process/memory/CPU limits, and places the MCP service on an internal-only Docker network. The workspace mount is the only intended writable persistent path.
|
|
|
|
This is a hardened local runner, not a VM-grade security boundary. For untrusted agents or untrusted repositories, connect `run_task` to an external ephemeral VM/container runner with a copy-on-write workspace, no host bind mounts, no Docker socket, no inherited secrets, resource quotas, and explicit network policy. Keep execution disabled until such a runner is configured.
|
|
|
|
## HTTP protocol
|
|
|
|
All HTTP endpoints require `Authorization: Bearer TOKEN` (or `x-api-key`).
|
|
|
|
### Streamable HTTP (recommended)
|
|
|
|
- Send an MCP `initialize` JSON-RPC request to `POST /mcp`. The response includes `Mcp-Session-Id`.
|
|
- Send subsequent MCP requests to `POST /mcp` with that `Mcp-Session-Id` header.
|
|
- Use `DELETE /mcp` with `Mcp-Session-Id` to terminate the session.
|
|
- `GET /mcp` with `Mcp-Session-Id` opens an optional server-to-client SSE stream.
|
|
|
|
### Legacy SSE
|
|
|
|
- `GET /sse` opens the former MCP SSE transport. It emits an `endpoint` event for posting messages.
|
|
- For automatic legacy-client fallback, `GET /mcp` without `Mcp-Session-Id` provides the same SSE handshake.
|
|
- The pre-existing custom transport is retained: `GET /events` returns `x-session-id`; send it on `POST /mcp` as `x-session-id`.
|
|
- Bind `x-workspace-subpath` only when opening or initializing a session; it cannot change afterward.
|
|
- `GET /health` reports server health.
|
|
|
|
## Development
|
|
|
|
```bash
|
|
npm test
|
|
```
|