You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+59-49Lines changed: 59 additions & 49 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,37 +2,41 @@
2
2
3
3
*Your computer, served on a platter.*
4
4
5
-
MCP server that exposes **Read**, **Write**, **Edit**, **Bash**, **Glob**, and **Grep** tools over Stdio and StreamableHTTP transports. Built with [Bun](https://bun.sh), compiles to standalone executables. The **grep** tool requires [ripgrep](https://github.com/BurntSushi/ripgrep) (`rg`) to be installed on the host.
5
+
MCP server that exposes **Read**, **Write**, **Edit**, **Bash**, **Glob**, and **Grep** tools over Stdio and StreamableHTTP transports. Built with [Bun](https://bun.sh), compiles to standalone executables.
6
6
7
-
Designed to be used by browser-based (or any MCP-compatible) agents — like [Hadrian](https://github.com/ScriptSmith/hadrian) — to control a computer.
7
+
Designed to be used by browser-based (or any MCP-compatible) agents, like [Hadrian](https://github.com/ScriptSmith/hadrian), to control a computer.
8
8
9
9
## Tools
10
10
11
11
| Tool | Description |
12
12
|------|-------------|
13
-
|**read**| Read file contents with pagination (offset/limit). Truncates at 2000 lines or 50KB. |
13
+
|**read**| Read file contents with pagination (offset/limit). Detects image files (JPEG, PNG, GIF, WebP) and returns metadata. Truncates text to 2000 lines or 50KB. |
14
14
|**write**| Create or overwrite files. Auto-creates parent directories. |
15
-
|**edit**| Find-and-replace with exact (or fuzzy Unicode) matching. Requires a unique match, or use `replace_all`to replace every occurrence. Returns a unified diff. |
15
+
|**edit**| Find-and-replace with exact or fuzzy matching (normalizes smart quotes, dashes, and Unicode whitespace). Requires a unique match, or use `replace_all`for every occurrence (exact matches only). Returns a unified diff. |
16
16
|**bash**| Execute shell commands with optional timeout. Output truncated to last 2000 lines or 50KB. |
17
-
|**glob**| Fast file pattern matching. Returns paths matching a glob pattern (e.g. `**/*.ts`). |
17
+
|**glob**| Fast file pattern matching. Returns up to 500 paths matching a glob pattern (e.g. `**/*.ts`). |
18
18
|**grep**| Search file contents using [ripgrep](https://github.com/BurntSushi/ripgrep). Supports regex, file filtering, context lines, and multiple output modes. Requires `rg` to be installed. |
19
19
20
20
## Quick start
21
21
22
22
### From a release binary
23
23
24
-
Download the binary for your platform from [Releases](https://github.com/ScriptSmith/platter/releases), make it executable, and run:
24
+
Download the latest binary for your platform from [Releases](https://github.com/ScriptSmith/platter/releases), or grab it with `curl`:
25
25
26
26
```bash
27
-
chmod +x platter-linux-x64
28
-
./platter-linux-x64 # stdio mode
29
-
./platter-linux-x64 -t http # HTTP mode on :3100
27
+
# Download (replace the filename for your platform)
CORS is enabled for all origins by default (reflects the request `Origin`). To restrict to a specific origin:
167
175
@@ -177,24 +185,24 @@ The server validates the `Host` header to prevent [DNS rebinding attacks](https:
177
185
178
186
### Network (HTTP mode)
179
187
180
-
-**Bearer token authentication**— required by default (RFC 6750). A random 256-bit token is generated at startup unless you provide `--auth-token` or disable auth with `--no-auth`.
181
-
-**Host header validation**— prevents [DNS rebinding attacks](https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-w48q-cv73-mx4w). Localhost binds accept only `127.0.0.1`, `localhost`, and `::1`; remote binds accept only the specified `--host`.
182
-
-**Origin validation**— when `--cors-origin` is set to a specific origin, requests with a mismatched `Origin` header are actively rejected with 403 (not just filtered by CORS response headers).
188
+
-**Bearer token authentication**- required by default (RFC 6750). A random 256-bit token is generated at startup unless you provide `--auth-token` or disable auth with `--no-auth`.
189
+
-**Host header validation**- prevents [DNS rebinding attacks](https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-w48q-cv73-mx4w). Localhost binds accept only `127.0.0.1`, `localhost`, and `::1`; remote binds accept only the specified `--host`.
190
+
-**Origin validation**- when `--cors-origin` is set to a specific origin, requests with a mismatched `Origin` header are actively rejected with 403 (not just filtered by CORS response headers).
183
191
184
192
### Restrictions (best-effort)
185
193
186
194
`--tools`, `--allow-path`, and `--allow-command` are **defense-in-depth** controls. They raise the bar significantly but are not a sandbox. The limitations below should be understood before relying on them in a threat model.
187
195
188
196
#### What they do well
189
197
190
-
-**Tool selection** is enforced at registration time — disabled tools are never exposed via the MCP protocol. There is no way for a client to invoke or discover them.
198
+
-**Tool selection** is enforced at registration time. Disabled tools are never exposed via the MCP protocol. There is no way for a client to invoke or discover them.
191
199
-**Path validation** resolves symlinks via `realpath()` on both the target and each allowed path before comparison, preventing traversal via `../` or symlinked directories. For write targets that don't exist yet, the nearest existing ancestor is resolved instead.
192
200
-**Command validation** anchors regex patterns to match the full command string, preventing trivial bypasses like appending `&& malicious-command`.
193
201
194
202
#### Known limitations and bypasses
195
203
196
204
-**Bash is inherently unrestricted.** When the bash tool is enabled, a sufficiently creative command can bypass `--allow-path` entirely (e.g. `cat /etc/passwd`). If you set `--allow-path` without also setting `--allow-command` or removing bash from `--tools`, a warning is printed at startup. For strong file-access control, either disable bash (`--tools read,write,edit,glob,grep`) or pair `--allow-path` with a tight `--allow-command` allowlist.
197
-
-**Command regex operates on the raw string.** It does not parse shell syntax. Patterns like `--allow-command "git( .*)?"` block `rm && git status` (because the full string doesn't match), but a determined attacker could construct commands that the regex matches yet that execute unintended code — for example, if an allowed pattern is too broad. Write patterns as narrowly as possible.
205
+
-**Command regex operates on the raw string.** It does not parse shell syntax. Patterns like `--allow-command "git( .*)?"` block `rm && git status` (because the full string doesn't match), but a determined attacker could construct commands that the regex matches yet that execute unintended code, for example if an allowed pattern is too broad. Write patterns as narrowly as possible.
198
206
-**Symlink TOCTOU.** Path validation resolves symlinks at check time. If a symlink target is changed between the check and the actual file operation, the validation can be bypassed. This is a fundamental limitation of userspace path checking.
199
207
-**Glob/grep search scope.**`--allow-path` validates the search directory for glob and grep, but results within that directory tree may include symlinks pointing outside it. The content of those symlink targets could be returned in grep output or listed by glob.
200
208
-**No process-level sandboxing.** All restrictions are enforced in application code within the platter process. They do not use OS-level mechanisms (seccomp, namespaces, pledge, etc.). A vulnerability in platter itself, Bun, or a dependency could bypass all restrictions.
@@ -203,13 +211,13 @@ For stronger isolation, use the just-bash sandbox, a Docker container, or both.
203
211
204
212
### Sandbox mode (just-bash)
205
213
206
-
Opt into [just-bash](https://github.com/vercel-labs/just-bash) — a TypeScript reimplementation of bash with a virtual filesystem — for true process-level isolation. No native processes are spawned; the shell runs entirely in the Bun runtime.
214
+
Opt into [just-bash](https://github.com/vercel-labs/just-bash), a TypeScript reimplementation of bash with a virtual filesystem, for sandboxed command execution. No native processes are spawned; the shell runs entirely in the Bun runtime.
207
215
208
216
```bash
209
-
platter --sandbox # readwrite fs, no network
210
-
platter --sandbox --sandbox-fs memory # pure in-memory fs
211
-
platter --sandbox --sandbox-fs overlay # reads from disk, writes ephemeral
212
-
platter --sandbox --sandbox-allow-url "https://api.example.com"# allow network to prefix
217
+
platter --sandbox # readwrite fs, no network
218
+
platter --sandbox --sandbox-fs memory # pure in-memory fs
219
+
platter --sandbox --sandbox-fs overlay # reads from disk, writes ephemeral
220
+
platter --sandbox --sandbox-allow-url "https://api.example.com"# allow network to prefix
-**Not full bash.** just-bash is a TypeScript reimplementation — some edge cases may behave differently from GNU bash.
247
+
-**Not full bash.** just-bash is a TypeScript reimplementation; some edge cases may behave differently from GNU bash.
240
248
-**No native binaries.** Commands like `git`, `node`, `docker`, `rg`, `python` are not available. Only bash builtins and just-bash's built-in command set work.
241
249
-**Beta software.** just-bash is under active development. Test your workflows before relying on it in production.
242
250
243
251
### Container isolation (Docker)
244
252
245
-
Running platter inside a Docker container provides OS-level isolation via Linux namespaces and cgroups. The container boundary limits what the bash tool can access — even unrestricted commands can only reach the filesystems and network that the container exposes.
253
+
Running platter inside a Docker container provides OS-level isolation via Linux namespaces and cgroups. The container boundary limits what the bash tool can access. Even unrestricted commands can only reach the filesystems and network that the container exposes.
246
254
247
255
```bash
248
256
# Minimal: no host filesystem, no network
@@ -268,16 +276,16 @@ docker run --rm -p 3100:3100 \
268
276
269
277
#### Containers vs VMs
270
278
271
-
Containers share the host kernel — isolation is enforced by kernel features (namespaces, cgroups, seccomp). A kernel vulnerability or a misconfigured container (e.g. `--privileged`) can break the boundary. VMs run a separate kernel on virtualised hardware, so a guest compromise does not directly expose the host. If your threat model includes untrusted code that may attempt kernel exploits, run platter inside a VM (or a VM-backed container runtime like [Kata Containers](https://katacontainers.io/) or [Firecracker](https://firecracker-microvm.github.io/)). For most use cases — limiting blast radius from an AI agent — a properly configured container (non-root, capabilities dropped, `--network none`) is sufficient.
279
+
Containers share the host kernel; isolation is enforced by kernel features (namespaces, cgroups, seccomp). A kernel vulnerability or a misconfigured container (e.g. `--privileged`) can break the boundary. VMs run a separate kernel on virtualised hardware, so a guest compromise does not directly expose the host. If your threat model includes untrusted code that may attempt kernel exploits, run platter inside a VM (or a VM-backed container runtime like [Kata Containers](https://katacontainers.io/) or [Firecracker](https://firecracker-microvm.github.io/)). For most use cases, like limiting blast radius from an AI agent, a properly configured container (non-root, capabilities dropped, `--network none`) is sufficient.
272
280
273
281
#### Combining sandbox and container
274
282
275
283
The just-bash sandbox and Docker container address different layers. Used together, they provide defense in depth:
276
284
277
285
| Layer | Protects against |
278
286
|---|---|
279
-
|**just-bash sandbox**| Arbitrary native process execution — no `git`, `curl`, `rm`, etc. Commands run in a TypeScript interpreter, not the OS shell. |
280
-
|**Docker container**| Host filesystem/network access — even if the sandbox has a bug or is bypassed, the container limits blast radius to mounted paths and allowed networks. |
287
+
|**just-bash sandbox**| Arbitrary native process execution: no `git`, `curl`, `rm`, etc. Commands run in a TypeScript interpreter, not the OS shell. |
288
+
|**Docker container**| Host filesystem/network access: even if the sandbox has a bug or is bypassed, the container limits blast radius to mounted paths and allowed networks. |
281
289
282
290
```bash
283
291
# Maximum isolation: sandbox inside a container, overlay fs, no network
@@ -310,8 +318,8 @@ See [Docker](#docker-1) for full usage instructions including mounting paths, ne
310
318
The Docker image is based on Debian Bookworm (slim) and includes ripgrep. Multi-arch images (`linux/amd64`, `linux/arm64`) are published to GitHub Container Registry on every tagged release.
The image uses Debian, so you can install packages with `apt-get` at runtime. This is useful for quick experiments but adds startup latency — for production use, build a custom image instead (see below).
379
+
The image uses Debian, so you can install packages with `apt-get` at runtime. This is useful for quick experiments but adds startup latency. For production use, build a custom image instead (see below).
372
380
373
381
```bash
374
382
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter \
@@ -419,13 +427,15 @@ docker run --rm -i platter
419
427
420
428
```bash
421
429
bun install
422
-
bun run build # bundle to dist/
423
-
bun run compile # standalone binary for current platform → ./platter
424
-
bun run compile:all # cross-compile for linux-x64, linux-arm64, darwin-x64, darwin-arm64
425
-
bun run format # format with Biome
426
-
bun run format:check # check formatting
427
-
bun run lint # lint with Biome
428
-
bun run typecheck # typecheck with TypeScript
430
+
bun run build # bundle to dist/
431
+
bun run compile # standalone binary for current platform -> ./platter
432
+
bun run compile:all # cross-compile for linux-x64, linux-arm64, darwin-x64, darwin-arm64
0 commit comments