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.

MCP tool catalog scm.* · issues.* consume rupu agent runtime + embedded MCP server serve MCP client IDE · Claude Desktop
One catalog, two directions: rupu consumes MCP tools into its agents and serves them to outside clients.

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.

ToolWhat it doesKind
bashExecute a shell command in the workspace cwd (controlled env; SIGTERM then SIGKILL on timeout)write
read_fileRead full file contents, line-numberedread
write_fileCreate or overwrite a filewrite
edit_fileExact-match string replacement; errors if the string is not foundwrite
grepSearch across the workspace (ripgrep-backed)read
globFile-pattern matchingread

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
Note. When an agent declares a 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.

What's confirmed. The catalog agents consume is rupu's own embedded SCM + issue surface, served in-process and gated by the same 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:

ModeRead toolsWrite tools
readonlyallowdeny
askallowprompt before running
bypassallowallow

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.