Most MCP servers expose tools that mirror data entities: one file, one symbol, one diff. That forces the agent into long sequential chains. Repowise exposes a small, curated set of task-shaped tools instead. Pass multiple targets in one call. Get complete context (docs, ownership, metrics, history, decisions) back. Same answer, fewer round-trips, fewer tokens.
The core tools
These ten tools are the documented core surface, available in both single-repo and workspace modes:
| Tool | What it answers | When the agent calls it |
|---|---|---|
get_overview | Architecture summary, module map, entry points, git health | First call on any unfamiliar codebase |
get_answer | One-call RAG: retrieves over the wiki, gates on confidence, synthesizes a cited answer | First call on any code question |
get_context | The workhorse: docs, symbols, ownership, freshness, optional callers/callees/metrics for any targets | Before reading or modifying code |
get_symbol | Raw source bytes for one indexed symbol with exact line bounds | When you need one function/class body |
search_codebase | Hybrid symbol / path / concept search, mode routes by query shape | Finding a symbol or file, or discovering code by topic |
get_risk | Hotspot scores, dependents, co-change partners, blast radius, reviewers, test gaps | Before modifying files |
get_change_risk | Pre-merge defect risk for a whole commit or diff range, plus impacted tests and prior fixes | Before merging a commit or PR range |
get_why | Architectural decision search: NL query, path lookup, or health dashboard | Before architectural changes |
get_dead_code | Unreachable code sorted by confidence tier with cleanup impact | Cleanup tasks |
get_health | Code-health scores across three signals (defect, maintainability, performance): KPIs and worst files, or per-file findings | Self-check before a PR / before refactoring |
get_risk and get_change_risk are the pair people mix up. get_risk
scores indexed files by path and can report blast radius.
get_change_risk scores the shape of a live diff, so it catches
risky small changes that a file-level view misses.
One more tool is on by default, in workspace mode only:
list_repos lists every workspace repo and
which one is the default, so aliases are discoverable. A single-repo
server is already bound to its only repository, so it does not
advertise it. Ten tools by default in single-repo mode, eleven in a
workspace.
18 tools are registered in total. The other seven are opt-in and covered below.
In workspace mode, every tool accepts a repo parameter naming a
single repo. Only four tools also accept repo="all":
search_codebase, get_dead_code, get_overview, and get_why in
its workspace search mode. Every other tool returns an error for
"all" and asks you to name one repo.
Seven opt-in tools
Seven registered tools ship off by default in every mode. Three of them are workspace-scoped: they answer cross-repo questions that have no single-repo equivalent, and naming them outside a workspace does nothing, since they cannot do useful work there.
| Tool | What it answers | Scope |
|---|---|---|
get_blast_radius | If you change this service, what breaks across the other repos | workspace |
get_architecture | Whole-system coupling, the cyclic core, and a 1-10 architecture score | workspace |
get_conformance | Declared dependency-rule violations and dependency cycles | workspace |
get_dependency_path | Shortest dependency path between two files or modules | either |
get_execution_flows | Entry points and call traces | either |
generate_refactoring_code | Turns a get_health(include=["refactoring"]) plan into code and a diff | either |
set_finding_status | Records a durable verdict on a finding, so a false positive stops coming back | either |
The two workspace tools with their own page are documented in full on
the Workspace MCP mode page.
get_conformance findings still surface automatically in get_risk's
PR-mode directive (conformance_violations / dependency_cycles)
without opting the tool itself in.
set_finding_status is the only tool on the whole surface that writes.
Everything else reads.
Turning them on
Turn any of them on per repo in .repowise/config.yaml:
mcp:
tools: ["+get_execution_flows", "+get_dependency_path", "+generate_refactoring_code"]or per launch with repowise mcp --tools "+get_execution_flows". See
Configuring the tool surface for the
full +/- delta syntax and the explicit-allowlist / all shapes.
generate_refactoring_code has a second gate on top of the tool-surface
one: even opted in, it returns an error unless
refactoring.llm.enabled is also set in .repowise/config.yaml.
Reading a response: the envelope
Every tool answers inside the same envelope, and the envelope is where a response says how far to trust it. An agent that reads only the payload will over-read some answers and under-read others. Four things are worth checking.
Which commit the answer describes. _meta.indexed_commit is the
12-character SHA the index was built against, and index_age_days is
how long ago that was. live_head carries your current checkout when
.git/HEAD is readable, and index_behind says whether the two have
diverged. stale_warning fires only on a real signal: HEAD moved and
the files it moved are files this response served, or git is
unreachable and the index is over roughly 90 days old. Two commits with
identical trees report index_behind with no warning, because nothing
served actually changed. Silence on stale_warning means the index is
current. Do not infer staleness from its absence.
Whether anything broke or was capped. _meta.state appears only
when something fired. degraded: true comes with degraded_reasons,
which maps each contributing key to the reason behind it: a failed
artifact read, a synthesis reason string, the retrieval legs that
broke. That distinction matters, because it turns an empty result from
"there is nothing here" into "this read failed". state.truncated
means a field was capped when it was built, and state.partial that
part of the answer is missing.
What it cost, and what it dropped. _meta.response_budget is
always present and carries limit_chars (the ceiling that applied),
tier (default or expanded, chosen by whether the call passed an
expansion argument such as include), and serialized_chars (what was
delivered under it). The default ceiling is 24,000 characters and an
expanded call gets 32,000, lowered further when the host's own output
cap is tighter, so a response can never reach the host's
spill-to-file path and cost the agent a Read. Responses size
themselves by content class: characters convert to tokens at a
different measured rate for paths, code bodies, JSON structure and
prose, so a budget is not one flat divisor. Each tool declares its own
cheapest-loss-first shed order, so the block that goes first is the one
worth least. When a response is trimmed it says what it dropped, in
*_omitted counts and *_reason fields beside the collection they
came from. Dropped content is recoverable: _meta.omitted lists refs
(repowise#<12-hex>) that resolve via
get_symbol or repowise expand <ref> from a
shell. See Distill for the reversibility model.
What the answer did not look at. _meta.scope_hint, on
get_answer and
get_context, fires when knowledge-graph
layers exist that contain none of the paths served. It names up to
three of them with file counts. An answer about the ingestion pipeline
that never touched the CLI layer will tell you so, which is the
difference between "not relevant" and "not looked at".
A zero carries its basis
An empty callers list reads the same whether the resolver bound
nearly every call site in that language or guessed at most of them.
So an empty one never ships alone. When
get_context returns an empty callers,
callees or used_by, it attaches a matching *_basis for that
target's language:
| Field | Meaning |
|---|---|
language | The language the basis is drawn from |
resolved_call_edges | How many call edges the graph resolved in it. 0 means the empty list carries no information at all |
guessed_share | The fraction of those edges that came from a resolution strategy ranked as a guess |
unresolved_call_sites | Always null. The count is not persisted, and the note says so rather than inventing a rate |
note | The sentence to act on: call sites the resolver could not bind are not counted, so an empty list means no resolved edge reaches this symbol, not proof that nothing calls it |
A populated list gets no basis, since the rows are their own evidence.
get_dead_code carries the same thing
repo-wide as call_resolution_basis in its summary, one entry per
language.
Read the basis before deleting anything.
Connecting
repowise init automatically writes .mcp.json at the project root
and (for Claude Code) registers the server in ~/.claude/settings.json.
You don't need to do anything else.
To wire up another editor manually:
{
"mcpServers": {
"repowise": {
"command": "repowise",
"args": ["mcp", "/path/to/your/project"]
}
}
}For the hosted version, use the HTTP transport with an API key from Settings -> Editor.
Run repowise agents add --target=cursor. Cursor reads a project-local
.cursor/mcp.json whose entry needs a "type": "stdio" field, so the
generic mcpServers block above does not work here. See
Set up with your agent for the details.
Add the same mcpServers block to claude_desktop_config.json
(macOS: ~/Library/Application Support/Claude/,
Windows: %APPDATA%\Claude\). Restart Claude Desktop after editing.
Use the MCP extension and point it at repowise mcp <project>.
Add to Windsurf settings -> MCP servers using the same command.
A real five-call task
"Add rate limiting to all API endpoints." Without repowise, an agent greps and reads files for ~30 calls. With repowise, five:
get_overview()
get_context(targets=["middleware", "api/routes", "payments"])
get_risk(targets=["middleware/auth.ts"])
get_why(query="rate limiting")
search_codebase(query="rate limit OR throttle OR retry")That's the design point. Each call returns a task-shaped slice of the codebase, not a single file or symbol, so the agent reaches a decision in one or two passes instead of ten.
How to compose these
Most tasks aren't a single call. These are the hand-offs that come up most often: what a tool's output points you to next.
| Situation | Call this first | Then this |
|---|---|---|
| Any code question | get_answer | Low confidence -> search_codebase or get_context on fallback_targets |
| Before reading or editing a file | get_context | hotspot: true -> get_risk before editing |
A file's decisions list names a record | get_context | get_why(targets=[...]) for the rationale |
| Reviewing a PR / changeset | get_risk(targets, changed_files=[...]) | Read directive first (may_break, missing_tests, tests_to_run) |
| Merging a commit or branch | get_change_risk(revspec) | Lead with risk_percentile, not the raw score |
| Need one function/class body | search_codebase or get_context for the symbol_id | get_symbol(symbol_id) |
| Before a refactor | get_health() | include=["refactoring"] for structured plans, then generate_refactoring_code on one (opt-in tool, see "Seven opt-in tools" above) |
| Cleanup sprint | get_dead_code() | safe_only=true for the deletion-ready subset |
| Unfamiliar codebase | get_overview() | get_answer / get_context for anything specific |
Why a curated surface
Repowise's internal modules expose more graph navigation primitives
(callers/callees, dependency paths, graph metrics, execution flows,
community detection). Most of that surfaces as include options on the
core tools rather than separate calls; two of the primitives
(get_dependency_path, get_execution_flows) are real MCP tools but
ship off by default, because every extra tool in the schema is overhead
on tasks that don't need it.
The public tools are deliberately one level up. get_context absorbs
callers, callees, metrics, and community into a single call via its
include parameter. get_risk rolls up hotspot, dependents,
co-changes, reviewers, and test gaps. get_why covers natural-language
search, path lookup, and the global health dashboard. get_health
rolls up the three-signal marker scores plus targeted findings.
If you're building on top of repowise programmatically, the
packages/server Python API exposes the lower-level primitives. The
MCP surface is what AI agents see, and AI agents do better with fewer,
richer tools.