MCP & tools
rupu speaks the Model Context Protocol both ways — it exposes its own capabilities as an MCP server, and it lets agents consume MCP tools as if they were native.
rupu speaks MCP both ways
The Model Context Protocol (MCP)
is a standard way for tools to advertise themselves to AI clients. rupu sits on both sides of that
contract. As a server, rupu mcp serve publishes rupu's unified
SCM and issue-tracker tool catalog over JSON-RPC so any MCP-aware client — Claude Desktop, Cursor, an
IDE — can call the exact tools rupu's own agents call. As a consumer, the same catalog
is served in-process to rupu's agent runtime, where each MCP tool is wrapped so it appears in an agent's
tool list alongside the built-ins. One tool catalog, one permission model, two directions.
The built-in tools
Every rupu agent ships with six built-in tools. They are the bedrock surface an agent uses to read and change a workspace; no plugins are required.
| Tool | What it does | Kind |
|---|---|---|
bash | Execute a shell command in the workspace cwd (controlled env; SIGTERM then SIGKILL on timeout) | write |
read_file | Read full file contents, line-numbered | read |
write_file | Create or overwrite a file | write |
edit_file | Exact-match string replacement; errors if the string is not found | write |
grep | Search across the workspace (ripgrep-backed) | read |
glob | File-pattern matching | read |
By default an agent gets all six. To narrow the surface, list names in the frontmatter
tools: field — rupu builds a registry filtered to exactly those names, so the
model is only ever told about the tools it is allowed to call.
# agent frontmatter tools: - read_file - grep - glob
The same tools: list doubles as the allowlist for MCP tools.
MCP tool names are dot-namespaced (for example scm.repos.list), and allowlist
entries match by exact name, the wildcard * (everything), or a
trailing-* prefix. So scm.* grants the whole SCM
namespace while leaving issues.* out.
tools: - read_file - grep - "scm.*" # every scm.* MCP tool - issues.get # one exact MCP tool
tools: list, rupu registers only
the MCP tools that match it. Without the gate the model would see the full catalog advertised, pick a tool
it is not permitted to call, get denied, and waste turns on retries.
rupu as an MCP server
rupu mcp serve runs the embedded MCP server for an outside client. v0 ships the
stdio transport (the default); the client spawns rupu as a
subprocess and talks JSON-RPC over stdin/stdout. (An http transport is reserved
but not wired in v0 — selecting it returns an error.) Internally the very same server is also exposed over
an in-process transport, which is how rupu's own agent runtime calls the catalog without a
serialization round-trip.
The catalog it serves is rupu's unified SCM + issue surface — scm.* repos,
branches, files and pull-requests, issues.*, plus GitHub/GitLab extras. Each
tool's input schema is auto-generated and returned in the MCP tools/list
response. Authentication is shared with the rupu CLI through the OS keychain, so one
rupu auth login unlocks the catalog for both.
# run the server directly (stdio is the default) $ rupu mcp serve --transport stdio
Wiring it into an MCP client — for example Claude Desktop's claude_desktop_config.json:
{ "mcpServers": { "rupu": { "command": "/usr/local/bin/rupu", "args": ["mcp", "serve", "--transport", "stdio"] } } }
In server mode the permission gate runs with bypass mode and an allow-all allowlist: the upstream client is expected to handle write-confirmation prompts itself, which matches the rest of the MCP ecosystem. rupu does not prompt from the server side.
Consuming external MCP servers
On the consuming side, an McpToolAdapter makes each MCP tool satisfy rupu's
native Tool trait. The adapter carries the tool's name, description and input
schema, and forwards every invoke() to the shared
ToolDispatcher. From the model's point of view there is no difference between a
built-in like grep and an MCP-backed tool like
scm.prs.create — both appear in the same tool list.
When a run starts with an SCM registry available, rupu spins up the embedded MCP server over the in-process
transport, then walks the tool catalog and inserts an adapter per tool into the agent's registry — skipping
any tool the agent's tools: allowlist does not match. MCP/custom tool calls also
emit coverage touch events, so they contribute to coverage just like the self-instrumenting built-ins.
tools: allowlist. The adapter itself is
generic over any tool the dispatcher exposes; a configuration shape for registering arbitrary third-party
MCP servers as an agent's tools is not part of the surface documented here.
Permissions
Tool access is gated by two checks, applied in order: the allowlist first, then the
mode. The allowlist is the agent's tools: list (or
* when none is set), matched by exact name, *, or
trailing-* prefix. The mode is one of three, supplied at run time:
| Mode | Read tools | Write tools |
|---|---|---|
readonly | allow | deny |
ask | allow | prompt before running |
bypass | allow | allow |
A tool that is not in the allowlist is denied regardless of mode. A tool that passes the allowlist is then
mode-checked: readonly blocks write tools (bash,
write_file, edit_file, and any write-kind MCP tool)
even when allowlisted; bypass permits everything. This same model governs both
the built-in tools and the MCP catalog — rupu mcp serve simply runs it pinned to
bypass + allow-all and defers prompting to the upstream client.