bruto-mcp
v0.5.0
Published
MCP server for Bruto boards: AI agents read and answer the notes of a project, kept in .bruto/workspace.json.
Maintainers
Readme
bruto-mcp
MCP server for Bruto boards. AI agents read the tasks of a project
and answer them through tools, instead of editing .bruto/workspace.json by hand.
It runs on your machine and reads and writes the board in your project folder. Nothing goes anywhere else. Bruto, if it's open, shows each change as soon as it's written.
Install
It needs Node.js 20 or newer.
Claude Code, once for every project (the server finds the board of the folder you work in):
claude mcp add --scope user bruto -- npx -y bruto-mcpWithout --scope user, it's added to the current project only. Check it with claude mcp list: it
should say ✓ Connected (the first time takes a few seconds while npx downloads it).
Or, to share it with everyone who opens the project (Claude Code in the terminal, the desktop app or
an IDE), a .mcp.json file at its root:
{
"mcpServers": {
"bruto": { "command": "npx", "args": ["-y", "bruto-mcp"] }
}
}Claude Desktop, Cursor, Windsurf and other clients: add it to the client's MCP settings. Most clients start servers outside your project, so give it the folder:
{
"mcpServers": {
"bruto": {
"command": "npx",
"args": ["-y", "bruto-mcp", "--project", "/path/to/your/project"]
}
}
}Without --project (or BRUTO_PROJECT), the server uses the closest folder with a board, going up
from where it was started. Every tool also takes a project argument, so one server can work across
several projects.
Git worktrees
Agents often work in a git worktree. There, the server uses the main checkout's board, in the
same folder of the repository, so answers land on the board you have open, not on a copy that
diverges from it. It works whether .bruto/ is committed (each worktree has a copy) or ignored (it
has none). If the main checkout has no board there, the worktree's own is used, as before.
To keep each worktree on its own board, start the server with --worktree-board (or
BRUTO_WORKTREE_BOARD=1).
On Windows
Clients can't start npx directly: run it through cmd. Behind a company proxy that inspects HTTPS,
Node also needs the system's certificates to download the package. With Claude Code, in PowerShell,
quote the -- (Windows PowerShell drops it otherwise, and claude reads -y as its own option):
claude mcp add --scope user bruto -e NODE_OPTIONS=--use-system-ca '--' cmd /c npx -y bruto-mcpIn a client's settings or a .mcp.json:
{
"mcpServers": {
"bruto": {
"command": "cmd",
"args": ["/c", "npx", "-y", "bruto-mcp"],
"env": { "NODE_OPTIONS": "--use-system-ca" }
}
}
}Tools
| Tool | What it does |
| --------------- | ----------------------------------------------------------------------------------- |
| list_notes | The notes with their short id and status: the open ones, or the statuses asked for. |
| search_notes | Notes by words in any of their texts, files or links, or by the start of their id. |
| get_context | The Markdown Bruto copies for an AI, with instructions and standing rules. |
| get_note | One note in full, with the notes it connects with. |
| set_status | Marks a note "in-progress" when the AI starts on it: the board shows it at work. |
| answer_note | Writes what the AI did, adds the files it touched and sends the note to review. |
| create_note | Adds a note, as an idea unless told otherwise, next to the one it follows from. |
| connect_notes | Draws an arrow between two notes. |
Reading tools are marked read-only, so clients can let them run without asking. None of the tools deletes anything.
Ghost zones
A board can hold ghost zones: read-only copies of the notes in linked projects that lead to notes
here, kept by the app in ghosts. get_context describes them, with their project and how old the
copy is, list_notes names them and get_note reads one. Writing tools refuse them and say which
project they are worked on in.
And one prompt, work_on_notes: fix what was sent back, then do the todo notes one by one.
Who did what
Every write tool takes an optional agent: the agent or subagent making the call, e.g. "reviewer".
Start the server with BRUTO_AGENT=<id> to give one to every call that doesn't.
On the note. A note an agent answers, moves or creates is stamped with the client from the MCP handshake (e.g.
claude-codeand its version), the agent id and the time. Bruto shows it under the AI response, andget_notereads it back.In the log. Every write, refused ones included, is appended to
.bruto/log.jsonl, one JSON line each and never rewritten:{ "at": "2026-09-30T09:14:02.118Z", "tool": "answer_note", "client": "claude-code", "version": "2.1.0", "agent": "reviewer", "note": "842b74aa-…", "args": { "id": "842b74", "response": "Added a waitlist…", "files": ["app/calendar/Waitlist.tsx"] }, "ok": true, "result": "[842b74] Waitlist for full slots is now \"review\"…", "commit": "3b3266e…", "files": { "app/calendar/Waitlist.tsx": "sha256:9f2c…" } }commitis the commit the project was on (when it is a git repository) andfilesthe SHA-256 of each file the agent says it touched, as it was then: the answer stays tied to the exact code it describes.Read only for agents. A note the user marks so in Bruto (
"agentAccess": "read") can be read but never answered or moved: the tools refuse, and the refusal is logged.
Safe with the app and your file
- A broken board is reported, never overwritten.
- Every write goes to a temporary file first and replaces the board in one go. If the app saved the board between the tool's read and its write, the tool starts over on what's there, instead of writing over it.
- If the user was editing the same field of the note when the agent wrote it, the user's value stays. The note then says so first thing (
Reverted: …inget_noteandget_context,agent change revertedin lists), and the app adds it to the log: the agent is never left believing a change went through. - Standing rules (notes of kind
rule) are never answered or changed. Boards from before v4, where rules had the statusloop, read the same. - Fields other tools added to the file are kept.
