|
| 1 | +# Deploy a Computer MCP |
| 2 | + |
| 3 | +This example exposes Computer through MCP. It gives an MCP client one durable workspace, a fast Worker shell, and a full Linux container behind a single Code Mode `code` tool. |
| 4 | + |
| 5 | +## Deploy |
| 6 | + |
| 7 | +[](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/computer/tree/main/examples/mcp) |
| 8 | + |
| 9 | +To deploy from a clone instead, start Docker and run: |
| 10 | + |
| 11 | +```bash |
| 12 | +npm install |
| 13 | +npm run deploy --workspace @example/computer-mcp |
| 14 | +``` |
| 15 | + |
| 16 | +The endpoint fails closed until you set `MCP_TOKEN`. After deployment, add it as an encrypted Worker secret in the Cloudflare dashboard, or set it from a clone of this repository: |
| 17 | + |
| 18 | +```bash |
| 19 | +npx wrangler secret put MCP_TOKEN --config examples/mcp/wrangler.jsonc |
| 20 | +``` |
| 21 | + |
| 22 | +## Connect |
| 23 | + |
| 24 | +Configure your MCP client to use the remote HTTP endpoint: |
| 25 | + |
| 26 | +```text |
| 27 | +https://<your-worker>.workers.dev/mcp |
| 28 | +``` |
| 29 | + |
| 30 | +Send the token on every MCP request: |
| 31 | + |
| 32 | +```text |
| 33 | +Authorization: Bearer <MCP_TOKEN> |
| 34 | +``` |
| 35 | + |
| 36 | +For clients that accept MCP server configuration as JSON, the entry typically looks like this: |
| 37 | + |
| 38 | +```json |
| 39 | +{ |
| 40 | + "mcpServers": { |
| 41 | + "computer": { |
| 42 | + "type": "http", |
| 43 | + "url": "https://<your-worker>.workers.dev/mcp", |
| 44 | + "headers": { |
| 45 | + "Authorization": "Bearer <MCP_TOKEN>" |
| 46 | + } |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +The exact configuration filename and format depend on the client. Keep the token in the client's secret storage when it provides one rather than committing it to a configuration file. |
| 53 | + |
| 54 | +The Worker's root URL prints its MCP endpoint and available backends. `GET /health` returns `ok` without authentication. |
| 55 | + |
| 56 | +## Use it |
| 57 | + |
| 58 | +Once connected, ask your MCP client to work in the Computer workspace. For example: |
| 59 | + |
| 60 | +```text |
| 61 | +Create /workspace/hello.txt, read it back, and list the workspace files. |
| 62 | +``` |
| 63 | + |
| 64 | +This uses `worker-shell` by default. Select the container when the task needs a full Linux environment: |
| 65 | + |
| 66 | +```text |
| 67 | +Use container-shell to create a small Node.js project in /workspace, install its dependencies, and run its tests. |
| 68 | +``` |
| 69 | + |
| 70 | +The client sees one public MCP tool named `code`. The model uses that tool to write a small JavaScript function that combines Computer's durable filesystem and command tools: |
| 71 | + |
| 72 | +```js |
| 73 | +async () => { |
| 74 | + await codemode.write({ |
| 75 | + path: "/workspace/package.json", |
| 76 | + content: JSON.stringify({ scripts: { test: "node --test" } }), |
| 77 | + }); |
| 78 | + |
| 79 | + const result = await codemode.exec({ |
| 80 | + command: "npm test", |
| 81 | + backend: "container-shell", |
| 82 | + }); |
| 83 | + |
| 84 | + return { exitCode: result.exitCode, stdout: result.stdout }; |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +You do not need to call the underlying Computer tools individually. The `code` tool describes these functions and backends to the model: |
| 89 | + |
| 90 | +| Function | Purpose | |
| 91 | +| --- | --- | |
| 92 | +| `codemode.read({ path, offset?, limit? })` | Read a file, optionally one range at a time. | |
| 93 | +| `codemode.ls({ path })` | List a directory. | |
| 94 | +| `codemode.write({ path, content })` | Create or replace a file. | |
| 95 | +| `codemode.edit({ path, edits })` | Apply exact text replacements to a file. | |
| 96 | +| `codemode.exec({ command, cwd?, backend?, env? })` | Run a command, using `worker-shell` unless another backend is selected. | |
| 97 | + |
| 98 | +## How it works |
| 99 | + |
| 100 | +`@cloudflare/codemode` runs Code Mode orchestration code in an isolated Dynamic Worker with outbound networking disabled. Tool calls return to the Durable Object and operate on its Computer workspace. |
| 101 | + |
| 102 | +| Backend | Use it for | |
| 103 | +| --- | --- | |
| 104 | +| `worker-shell` | The fast default for common commands and workspace operations. | |
| 105 | +| `container-shell` | Full Debian Linux with Node.js, npm, git, native binaries, and outbound networking. | |
| 106 | + |
| 107 | +The model can select a backend in `codemode.exec()`. The example does not retry automatically, so backend choice, cost, and failures remain visible. |
| 108 | + |
| 109 | +The container starts only when `container-shell` is selected. Computer synchronizes `/workspace` between the Durable Object and the container's FUSE mount before and after each command. |
| 110 | + |
| 111 | +## Run locally |
| 112 | + |
| 113 | +Local development requires a running Docker daemon for the Linux container. From the repository root: |
| 114 | + |
| 115 | +```bash |
| 116 | +npm install |
| 117 | +printf 'MCP_TOKEN=development-token\n' > examples/mcp/.dev.vars |
| 118 | +npm run dev --workspace @example/computer-mcp |
| 119 | +``` |
| 120 | + |
| 121 | +The `predev` script builds the workspace packages before Wrangler starts. On the first run, Wrangler also builds the container image. Connect to `http://127.0.0.1:8787/mcp` with the same bearer token. |
| 122 | + |
| 123 | +## Validate |
| 124 | + |
| 125 | +```bash |
| 126 | +npm run typecheck --workspace @example/computer-mcp |
| 127 | +npm test --workspace @example/computer-mcp |
| 128 | +``` |
| 129 | + |
| 130 | +The workerd integration test authenticates a real MCP client, verifies that only `code` is public, runs filesystem and Worker-shell operations, and confirms that files persist across calls. It does not start the Linux container; validate `container-shell` against a deployment when changing the container image or Computer/computerd protocol. |
| 131 | + |
| 132 | +## Debug |
| 133 | + |
| 134 | +Check the public routes first: |
| 135 | + |
| 136 | +```bash |
| 137 | +curl https://<your-worker>.workers.dev/health |
| 138 | +curl https://<your-worker>.workers.dev/ |
| 139 | +``` |
| 140 | + |
| 141 | +Then stream Worker and Durable Object logs: |
| 142 | + |
| 143 | +```bash |
| 144 | +npx wrangler tail --config examples/mcp/wrangler.jsonc |
| 145 | +``` |
| 146 | + |
| 147 | +A `401` means the bearer token is missing or incorrect. A `503` means `MCP_TOKEN` has not been configured. Backend failures are returned in the `codemode.exec()` result with the selected backend name. |
| 148 | + |
| 149 | +## Security model |
| 150 | + |
| 151 | +This example is intentionally single-user. Every authenticated request reaches the same Durable Object and workspace. Keep `MCP_TOKEN` private and deploy a separate copy for each trust boundary. |
| 152 | + |
| 153 | +Code Mode's orchestration Worker cannot access the network directly. The Worker shell also has outbound access disabled. The Linux container has outbound access so package managers and development tools work. |
| 154 | + |
| 155 | +For a multi-user service, replace the bearer-token check with OAuth, derive the Durable Object name from the authenticated subject, and add per-user execution and storage limits. |
0 commit comments