Skip to content

Commit d2ff242

Browse files
committed
Update README.md
1 parent e96ac2d commit d2ff242

File tree

1 file changed

+59
-49
lines changed

1 file changed

+59
-49
lines changed

README.md

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,41 @@
22

33
*Your computer, served on a platter.*
44

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.
66

7-
Designed to be used by browser-based (or any MCP-compatible) agentslike [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.
88

99
## Tools
1010

1111
| Tool | Description |
1212
|------|-------------|
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. |
1414
| **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. |
1616
| **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`). |
1818
| **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. |
1919

2020
## Quick start
2121

2222
### From a release binary
2323

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`:
2525

2626
```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)
28+
# Available: platter-linux-x64, platter-linux-arm64, platter-darwin-x64, platter-darwin-arm64
29+
curl -fsSL https://github.com/ScriptSmith/platter/releases/latest/download/platter-linux-x64 -o platter
30+
chmod +x platter
31+
32+
./platter # stdio mode
33+
./platter -t http # HTTP mode on :3100
3034
```
3135

3236
### Docker
3337

3438
```bash
35-
docker run --rm -i ghcr.io/scriptsmith/platter # stdio mode
39+
docker run --rm -i ghcr.io/scriptsmith/platter # stdio mode
3640
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 # HTTP mode
3741
```
3842

@@ -42,21 +46,25 @@ See [Docker](#docker-1) below for mounting paths, networking, installing extra s
4246

4347
```bash
4448
bun install
45-
bun run dev # run directly from TypeScript
46-
bun run compile # build standalone binary for current platform
49+
bun run dev # run directly from TypeScript
50+
bun run compile # build standalone binary for current platform
4751
```
4852

4953
## Usage
5054

5155
```
52-
platter [options]
56+
platter v1.0.0
57+
58+
Your computer, served on a platter.
59+
60+
Usage: platter [options]
5361
5462
Options:
5563
-t, --transport <stdio|http> Transport mode (default: stdio)
5664
-p, --port <number> HTTP port (default: 3100)
5765
--host <address> HTTP bind address (default: 127.0.0.1)
5866
--cwd <path> Working directory for tools (default: current directory)
59-
--cors-origin <origin> Allowed CORS origin (default: * — reflects request origin)
67+
--cors-origin <origin> Allowed CORS origin (default: *)
6068
--auth-token <token> Bearer token for HTTP auth (auto-generated if omitted)
6169
--no-auth Disable bearer token authentication
6270
@@ -72,7 +80,7 @@ Sandbox:
7280
--sandbox-fs <mode> Filesystem backend: memory, overlay, readwrite (default: readwrite)
7381
--sandbox-allow-url <url> Allow network access to URL prefix (repeatable)
7482
75-
-h, --help Show help message
83+
-h, --help Show this help message
7684
-v, --version Show version number
7785
```
7886

@@ -82,11 +90,11 @@ You can limit which tools are registered, which filesystem paths file tools can
8290

8391
#### Tool selection
8492

85-
Only register specific tools — unregistered tools are completely hidden from MCP clients:
93+
Only register specific tools. Unregistered tools are completely hidden from MCP clients:
8694

8795
```bash
88-
platter --tools read,glob,grep # read-only server
89-
platter --tools read,write,edit # no bash/search
96+
platter --tools read,glob,grep # read-only server
97+
platter --tools read,write,edit # no bash/search
9098
```
9199

92100
#### Path restrictions
@@ -103,9 +111,9 @@ platter --allow-path /home/user/project --allow-path /tmp
103111
Only allow bash commands whose **entire** command string matches at least one regex pattern:
104112

105113
```bash
106-
platter --allow-command "git( .*)?" # git only
107-
platter --allow-command "git( .*)?" --allow-command "npm( .*)?" # git or npm
108-
platter --allow-command "ls( .*)?" --allow-command "cat .*" # ls or cat
114+
platter --allow-command "git( .*)?" # git only
115+
platter --allow-command "git( .*)?" --allow-command "npm( .*)?" # git or npm
116+
platter --allow-command "ls( .*)?" --allow-command "cat .*" # ls or cat
109117
```
110118

111119
Patterns are anchored: `--allow-command "git( .*)?"` compiles to `^(?:git( .*)?)$`, so `git status` matches but `rm -rf / && git status` does not.
@@ -159,9 +167,9 @@ platter -t http -p 3100
159167
```
160168

161169
The server exposes a single endpoint at `/mcp` that handles:
162-
- `POST /mcp` JSON-RPC messages (initialize, tool calls)
163-
- `GET /mcp` SSE notification stream
164-
- `DELETE /mcp` session teardown
170+
- `POST /mcp` - JSON-RPC messages (initialize, tool calls)
171+
- `GET /mcp` - SSE notification stream
172+
- `DELETE /mcp` - session teardown
165173

166174
CORS is enabled for all origins by default (reflects the request `Origin`). To restrict to a specific origin:
167175

@@ -177,24 +185,24 @@ The server validates the `Host` header to prevent [DNS rebinding attacks](https:
177185

178186
### Network (HTTP mode)
179187

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).
183191

184192
### Restrictions (best-effort)
185193

186194
`--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.
187195

188196
#### What they do well
189197

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.
191199
- **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.
192200
- **Command validation** anchors regex patterns to match the full command string, preventing trivial bypasses like appending `&& malicious-command`.
193201

194202
#### Known limitations and bypasses
195203

196204
- **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 codefor 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.
198206
- **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.
199207
- **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.
200208
- **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.
203211

204212
### Sandbox mode (just-bash)
205213

206-
Opt into [just-bash](https://github.com/vercel-labs/just-bash)a TypeScript reimplementation of bash with a virtual filesystemfor 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.
207215

208216
```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
213221
```
214222

215223
#### Filesystem modes
@@ -236,13 +244,13 @@ platter --sandbox --sandbox-allow-url "https://api.github.com" --sandbox-allow-u
236244

237245
#### Limitations
238246

239-
- **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.
240248
- **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.
241249
- **Beta software.** just-bash is under active development. Test your workflows before relying on it in production.
242250

243251
### Container isolation (Docker)
244252

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.
246254

247255
```bash
248256
# Minimal: no host filesystem, no network
@@ -268,16 +276,16 @@ docker run --rm -p 3100:3100 \
268276

269277
#### Containers vs VMs
270278

271-
Containers share the host kernelisolation 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.
272280

273281
#### Combining sandbox and container
274282

275283
The just-bash sandbox and Docker container address different layers. Used together, they provide defense in depth:
276284

277285
| Layer | Protects against |
278286
|---|---|
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. |
281289

282290
```bash
283291
# 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
310318
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.
311319

312320
```bash
313-
docker pull ghcr.io/scriptsmith/platter # latest release
314-
docker pull ghcr.io/scriptsmith/platter:1.0.0 # specific version
321+
docker pull ghcr.io/scriptsmith/platter # latest release
322+
docker pull ghcr.io/scriptsmith/platter:1.0.0 # specific version
315323
```
316324

317325
### Running in stdio mode
@@ -368,7 +376,7 @@ docker run --rm -p 3100:3100 --network host ghcr.io/scriptsmith/platter -t http
368376

369377
### Installing additional software at runtime
370378

371-
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).
372380

373381
```bash
374382
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter \
@@ -419,13 +427,15 @@ docker run --rm -i platter
419427

420428
```bash
421429
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
433+
bun run format # format with Biome
434+
bun run format:check # check formatting
435+
bun run lint # lint with Biome
436+
bun run lint:fix # lint and auto-fix with Biome
437+
bun run typecheck # typecheck with TypeScript
438+
bun run test # run tests
429439
```
430440

431441
## License

0 commit comments

Comments
 (0)