# tsafe-mcp
First-party MCP server for [tsafe](https://crates.io/crates/tsafe-cli) — speaks
MCP over stdio (JSON-RPC 2.0, protocol version `2025-06-18`) to MCP-aware hosts
(Claude Desktop, Cursor, Continue, Windsurf, Codex).
Companion binary published alongside `tsafe-agent`. Per
[ADR-006](../../docs/architecture/ADR-006-mcp-server.md), this binary supersedes
the Node starter under `examples/mcp/` on v1 GA.
## Install
```sh
cargo install tsafe-mcp
```
Or get it bundled with the meta-crate:
```sh
cargo install tsafe
```
The meta-crate enables the `mcp` feature on its embedded `tsafe-cli` dep so
`tsafe mcp install <host>` works out of the box.
## Bind one profile per server
Each `tsafe-mcp` process is bound to **exactly one** tsafe profile at startup.
Request-time profile switching is rejected as JSON-RPC error `-32008`
(thin-stance "one profile per server"). Run multiple processes if you need
multiple profiles.
```sh
tsafe-mcp serve \
--profile work \
--allowed-keys "demo/*,shared/*" \
--denied-keys "demo/secret" \
--audit-source "mcp:claude:1234"
```
A running `tsafe-agent` is required by default. Unlock first:
```sh
eval $(tsafe agent unlock --ttl 8h)
```
If the agent is not reachable, every tool call returns `-32001 AgentNotRunning`
with a directive to run `tsafe agent unlock`.
## Per-host install
`tsafe mcp install <host>` writes a server entry to the host's MCP config:
```sh
tsafe mcp install claude --profile work --allowed-keys "demo/*"
tsafe mcp install cursor --profile work --allowed-keys "demo/*" --project .
tsafe mcp install continue --profile work --allowed-keys "demo/*" --project .
tsafe mcp install windsurf --profile work --allowed-keys "demo/*"
tsafe mcp install codex --profile work --allowed-keys "demo/*"
```
The writer merges into existing host configs (other entries are preserved) and
refuses to write without an explicit `--allowed-keys` or `--contract`. Use
`--dry-run` to preview the file change without touching disk.
## Default tool surface (6 tools)
| `tsafe_run` | Exec a command with explicitly-allowed keys injected as env. |
| `tsafe_list_keys` | List scope-filtered key names. Values are never returned. |
| `tsafe_search_keys` | Case-insensitive substring match across scope-filtered keys. |
| `tsafe_has_key` | Boolean presence check that honors scope. |
| `tsafe_audit_tail` | Recent audit entries, redacted to 6 fields. |
| `tsafe_status` | Agent/vault/scope shape per ADR-029 schema v1. |
`tsafe_run` accepts only **literal key names** in its `allowed_keys` request
field — globs at request time are rejected with `-32602 InvalidParams`. Globs
apply only at the startup scope level.
## Opt-in 7th tool: `tsafe_reveal`
Off by default. Enable with `--allow-reveal` to expose the explicit escape
hatch:
```sh
tsafe-mcp serve --profile work --allowed-keys "demo/*" --allow-reveal
```
Every `tsafe_reveal` call writes an audit row with `operation=mcp.reveal`
**before** the value is retrieved, so a panic or biometric denial still leaves
a record of the attempt. When the OS keychain has an entry for the bound
profile, biometric consent is implicit via `keyring_store::retrieve_password`.
## Audit
Every tool call appends a row to the profile's `.audit.jsonl`:
```
source: the --audit-source value (default: mcp:unknown:<pid>)
```
Audit rows reuse the existing `tsafe-core::audit::AuditLog` chain, so they
appear in the same operator narrative as `tsafe exec`.
## Doctrine pointers
- [`docs/architecture/ADR-006-mcp-server.md`](../../docs/architecture/ADR-006-mcp-server.md) — decision record.
- [`docs/architecture/mcp-server-design.md`](../../docs/architecture/mcp-server-design.md) — concrete shape (crate layout, tool schemas, error codes, host configs, test posture).
- [`docs/research/thin-mcp-stance-2026-05.md`](../../docs/research/thin-mcp-stance-2026-05.md) — thin-stance, one-profile-per-server.
- [`docs/decisions/agent-publish-topology.md`](../../docs/decisions/agent-publish-topology.md) — companion-binary publish model.
## Out of scope for v1
- HTTP/SSE transport — stdio only.
- Multi-profile per process — refused at startup; run one process per profile.
- `tsafe_get` / `tsafe_set` / `tsafe_delete` / `tsafe_export` tools — direct
thin-stance violation, not part of the default or opt-in surface.
## License
Same as the tsafe workspace — see the repository root.