Skip to content

Commit 73a77cf

Browse files
feat(mcp): make get_conformance and generate_refactoring_code opt-in
Both tools were advertised on the default surface, but neither is a good default. generate_refactoring_code returns an error until the repo sets refactoring.llm.enabled, so it added schema weight for a tool most repos can't call. get_conformance needs the workspace graph and is niche enough that it shouldn't take a default slot; its findings still surface in the get_risk PR-mode directive without opting the tool in. Flip both to default=False. get_conformance keeps requires_workspace=True, so it can only be opted in inside a workspace. Default single-repo drops from 11 to 10 tools, workspace default from 14 to 12, opt-in grows to 4. Updates the surface docs (MCP_TOOLS.md, CONFIG.md), the module docstring, and adds a selection regression test.
1 parent 466afd8 commit 73a77cf

6 files changed

Lines changed: 73 additions & 37 deletions

File tree

docs/CONFIG.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ distill:
150150
### The `mcp:` block
151151

152152
Controls which tools the MCP server advertises. The default surface is curated
153-
(11 tools in single-repo mode, plus 3 workspace-only tools in workspace mode);
153+
(10 tools in single-repo mode, plus 2 workspace-only tools in workspace mode);
154154
this block lets you opt extra tools in or trim the set down. The `repowise mcp
155155
--tools` / `--all` flags override it for a single launch.
156156

@@ -167,10 +167,12 @@ mcp:
167167
- `lean` selects the agent-lean profile: `get_answer`, `get_context`,
168168
`get_symbol`, `search_codebase`, `get_risk` (plus `list_repos` in workspace
169169
mode), small enough that Claude Code can keep every schema always loaded.
170-
- Opt-in tools are `get_dependency_path` and `get_execution_flows`.
171-
- Workspace-only tools (`get_blast_radius`, `get_conformance`,
172-
`get_architecture`) are added automatically in workspace mode and ignored if
173-
named in single-repo mode. See [MCP_TOOLS.md](MCP_TOOLS.md#configuring-the-tool-surface).
170+
- Opt-in tools are `get_dependency_path`, `get_execution_flows`,
171+
`generate_refactoring_code`, and `get_conformance` (the last only usable in
172+
workspace mode).
173+
- Workspace-only tools (`get_blast_radius`, `get_architecture`) are added
174+
automatically in workspace mode and ignored if named in single-repo mode. See
175+
[MCP_TOOLS.md](MCP_TOOLS.md#configuring-the-tool-surface).
174176

175177
### The `decisions:` block
176178

docs/MCP_TOOLS.md

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
repowise exposes a curated set of tools via the [Model Context Protocol](https://modelcontextprotocol.io) (MCP). These tools give AI coding assistants (Claude Code, Codex, Cursor, Cline, Windsurf) structured access to your codebase intelligence: dependency graph, git history, documentation, and architectural decisions.
44

5-
16 tools are registered in total. A single-repo server advertises 11 by default: the nine flagship tools below plus `list_repos` and `generate_refactoring_code`. Workspace mode adds 3 more automatically, for 14. Two further tools are off by default everywhere and must be opted in. The surface is configurable; see [Configuring the tool surface](#configuring-the-tool-surface).
5+
16 tools are registered in total. A single-repo server advertises 10 by default: the nine flagship tools below plus `list_repos`. Workspace mode adds 2 more automatically (`get_architecture`, `get_blast_radius`), for 12. Four further tools are off by default everywhere and must be opted in. The surface is configurable; see [Configuring the tool surface](#configuring-the-tool-surface).
66

77
**Start the MCP server:**
88

@@ -30,17 +30,17 @@ repowise mcp --transport sse --port 7338 # legacy SSE transport
3030
| `get_dead_code` | Unreachable code | Cleanup tasks |
3131
| `get_health` | Code-health marker scores | Before refactoring, find the worst files |
3232

33-
Also always on by default: `list_repos` (repo aliases) and `generate_refactoring_code` (opt-in code generation from a health plan). See [Supplementary tools](#supplementary-tools).
33+
Also always on by default: `list_repos` (repo aliases). See [Supplementary tools](#supplementary-tools).
3434

3535
---
3636

3737
## Configuring the tool surface
3838

3939
The default surface is deliberately small: fewer, richer tools mean fewer round-trips and less schema overhead per task. What a server advertises is resolved from three things: each tool's `default`/`requires_workspace` metadata, whether the server is in workspace mode, and an optional override.
4040

41-
- **Default (single-repo):** 11 tools, the nine flagship tools plus `list_repos` and `generate_refactoring_code` (though `generate_refactoring_code` returns an error until its config flag is set; see below).
42-
- **Default (workspace):** those 11 plus `get_architecture`, `get_blast_radius`, and `get_conformance`, added automatically when the server starts inside a workspace. They are never advertised outside one.
43-
- **Opt-in tools:** `get_dependency_path` and `get_execution_flows` are registered but off by default everywhere. Turn them on per repo.
41+
- **Default (single-repo):** 10 tools, the nine flagship tools plus `list_repos`.
42+
- **Default (workspace):** those 10 plus `get_architecture` and `get_blast_radius`, added automatically when the server starts inside a workspace. They are never advertised outside one.
43+
- **Opt-in tools:** `get_dependency_path`, `get_execution_flows`, `generate_refactoring_code`, and `get_conformance` are registered but off by default. Turn them on per repo; `get_conformance` only does useful work in workspace mode (name it there).
4444

4545
**Configure it in `.repowise/config.yaml`** under an `mcp.tools` key. Four shapes are supported:
4646

@@ -504,25 +504,6 @@ Lists the repos this server is serving. No parameters.
504504
list_repos()
505505
```
506506

507-
### `generate_refactoring_code`
508-
509-
Turns one structured refactoring plan from `get_health(include=["refactoring"])` into actual generated code and a unified diff, grounded on the plan plus the real source spans it references. For Extract Class, the result includes an LCOM4 before/after self-check.
510-
511-
**Disabled by default.** Returns `{"error": "disabled", ...}` unless `refactoring.llm.enabled: true` is set in the repo's `.repowise/config.yaml`. When enabled, it uses the repo's configured LLM provider/model (bring your own key) and caches results by a content hash, so an unchanged plan never regenerates.
512-
513-
**Parameters:**
514-
515-
| Parameter | Type | Required | Description |
516-
|-----------|------|----------|-------------|
517-
| `suggestion_id` | string | Yes | The `id` of a plan returned by `get_health(include=["refactoring"])` |
518-
| `repo` | string | No | *(workspace only)* Target repo alias |
519-
520-
**When to use:** After `get_health(include=["refactoring"])` surfaces a plan you want turned into an applyable diff, and your repo has opted into LLM-backed generation.
521-
522-
```
523-
generate_refactoring_code(suggestion_id="a1b2c3d4")
524-
```
525-
526507
### Workspace-only tools
527508

528509
*(Available only when the server is started inside a workspace; see [Workspace Mode](#workspace-mode).)*
@@ -550,6 +531,8 @@ get_blast_radius(targets=["mono::services/auth"], max_depth=2, include_behaviora
550531

551532
Architecture governance: does the live system graph obey the declared dependency rules, and are there circular service dependencies?
552533

534+
**Opt-in.** Off by default even in workspace mode; enable with `mcp.tools: ["+get_conformance"]`. Named in single-repo mode it is ignored, since it needs the workspace graph. The same findings still surface in the `get_risk` PR-mode directive (`conformance_violations` / `dependency_cycles`) without opting the tool in.
535+
553536
| Parameter | Type | Required | Description |
554537
|-----------|------|----------|-------------|
555538
| `repo` | string | No | Limit findings to those involving this repo alias |
@@ -617,6 +600,25 @@ get_execution_flows()
617600
get_execution_flows(entry_point="src/cli/main.py::main", max_depth=4)
618601
```
619602

603+
#### `generate_refactoring_code`
604+
605+
Turns one structured refactoring plan from `get_health(include=["refactoring"])` into actual generated code and a unified diff, grounded on the plan plus the real source spans it references. For Extract Class, the result includes an LCOM4 before/after self-check.
606+
607+
**Off by default twice over:** it must be opted into the tool surface (`mcp.tools: ["+generate_refactoring_code"]`), and even then returns `{"error": "disabled", ...}` unless `refactoring.llm.enabled: true` is set in the repo's `.repowise/config.yaml`. When enabled, it uses the repo's configured LLM provider/model (bring your own key) and caches results by a content hash, so an unchanged plan never regenerates.
608+
609+
**Parameters:**
610+
611+
| Parameter | Type | Required | Description |
612+
|-----------|------|----------|-------------|
613+
| `suggestion_id` | string | Yes | The `id` of a plan returned by `get_health(include=["refactoring"])` |
614+
| `repo` | string | No | *(workspace only)* Target repo alias |
615+
616+
**When to use:** After `get_health(include=["refactoring"])` surfaces a plan you want turned into an applyable diff, and your repo has opted into both the tool and LLM-backed generation.
617+
618+
```
619+
generate_refactoring_code(suggestion_id="a1b2c3d4")
620+
```
621+
620622
---
621623

622624
## Workspace Mode
@@ -633,7 +635,7 @@ The MCP server automatically enriches responses with cross-repo intelligence:
633635
- **Package dependencies** between repos
634636
- **Cross-repo blast radius** via the workspace-only `get_blast_radius` tool, and a cross-repo `directive` in `get_risk` PR-mode
635637
- **Breaking-change guard**: incompatible provider-contract changes and the consumers they endanger, in the `get_risk` PR-mode `breaking_changes` directive
636-
- **Architecture conformance**: declared dependency-rule violations and dependency cycles via the workspace-only `get_conformance` tool, and `conformance_violations` / `dependency_cycles` in the `get_risk` PR-mode directive
638+
- **Architecture conformance**: declared dependency-rule violations and dependency cycles via the workspace-only, opt-in `get_conformance` tool, and `conformance_violations` / `dependency_cycles` in the `get_risk` PR-mode directive
637639
- **Architecture metrics**: whole-system coupling (propagation cost), the cyclic core, per-service roles, and a deterministic 1-10 architecture score via the workspace-only `get_architecture` tool
638640

639641
---

packages/server/src/repowise/server/mcp_server/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
33
By default a single-repo server exposes ten tools (get_answer, get_context,
44
get_symbol, search_codebase, get_overview, get_risk, get_why, get_dead_code,
5-
get_health, list_repos); three more (get_blast_radius, get_conformance,
6-
get_architecture) are added automatically in workspace mode. Two further tools
7-
(get_dependency_path, get_execution_flows) are registered but off by default and
8-
can be opted in via the ``mcp.tools`` config block or the ``repowise mcp
9-
--tools`` flag. The selection layer lives in :mod:`._tool_selection`.
5+
get_health, list_repos); two more (get_blast_radius, get_architecture) are added
6+
automatically in workspace mode. Four further tools (get_dependency_path,
7+
get_execution_flows, generate_refactoring_code, get_conformance) are registered
8+
but off by default and can be opted in via the ``mcp.tools`` config block or the
9+
``repowise mcp --tools`` flag; get_conformance only does useful work in workspace
10+
mode. The selection layer lives in :mod:`._tool_selection`.
1011
1112
Exposes the full repowise wiki as queryable tools via the MCP protocol.
1213
Supports stdio transport (Claude Code, Cursor, Cline), streamable HTTP, and

packages/server/src/repowise/server/mcp_server/tool_conformance.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
_MCP_CYCLE_LIMIT = 25
2222

2323

24-
@mcp.tool(requires_workspace=True)
24+
@mcp.tool(default=False, requires_workspace=True)
2525
async def get_conformance(repo: str | None = None) -> dict[str, Any]:
2626
"""Architecture conformance — dependency-rule violations + cycles.
2727

packages/server/src/repowise/server/mcp_server/tool_refactoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def _loads(value: Any) -> dict[str, Any]:
5757
return sug
5858

5959

60-
@mcp.tool()
60+
@mcp.tool(default=False)
6161
async def generate_refactoring_code(suggestion_id: str, repo: str | None = None) -> dict:
6262
"""Generate refactored code + a unified diff for one refactoring plan.
6363

tests/unit/server/mcp/test_tool_selection.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,37 @@ def test_lean_profile_full_registry():
101101
assert workspace == LEAN_TOOLS | {"list_repos"}
102102

103103

104+
def test_conformance_and_refactoring_are_opt_in():
105+
"""generate_refactoring_code and get_conformance are off the default surface.
106+
107+
Both must be named explicitly to appear; get_conformance stays workspace-gated
108+
even when opted in.
109+
"""
110+
import repowise.server.mcp_server # noqa: F401 (registers the tools)
111+
from repowise.core.registry import mcp_tool_registry
112+
113+
entries = mcp_tool_registry.entries()
114+
115+
single = resolve_enabled_tools(entries, is_workspace=False)
116+
workspace = resolve_enabled_tools(entries, is_workspace=True)
117+
for surface in (single, workspace):
118+
assert "generate_refactoring_code" not in surface
119+
assert "get_conformance" not in surface
120+
121+
opted_ws = resolve_enabled_tools(
122+
entries, is_workspace=True, override="+generate_refactoring_code,+get_conformance"
123+
)
124+
assert {"generate_refactoring_code", "get_conformance"} <= opted_ws
125+
126+
# In single-repo mode refactoring can be opted in, but conformance can't:
127+
# it needs the workspace graph, so an explicit mention is ignored there.
128+
opted_single = resolve_enabled_tools(
129+
entries, is_workspace=False, override="+generate_refactoring_code,+get_conformance"
130+
)
131+
assert "generate_refactoring_code" in opted_single
132+
assert "get_conformance" not in opted_single
133+
134+
104135
def test_workspace_only_named_explicitly_is_dropped_single_repo():
105136
enabled = resolve_enabled_tools(
106137
CATALOG, is_workspace=False, override="get_answer,get_blast_radius"

0 commit comments

Comments
 (0)