Skip to content

Commit a9c9ba2

Browse files
committed
chore(mcp): drop 24 narrow tools from user-facing MCP surface
The v0.3.0 release exposed 34 MCP tools — 6 consolidated mode-driven plus 28 narrow tools (20 graph + 9 topology + 4 intelligence, minus run_cypher / read_file / generate_flow / review_changes which stay). Greenfield project, no pinned consumers — drop the 24 narrow tools that the consolidated layer subsumes from the MCP surface. Surface drops 34 -> 10: - 6 consolidated (graph_summary, find_in_graph, inspect_node, trace_relationships, analyze_impact, topology_view) - 4 first-class (run_cypher, read_file, generate_flow, review_changes) Implementation: keep all toolXxx(d) Tool builder functions in tools_graph.go / tools_intelligence.go / tools_topology.go because tools_consolidated.go delegates to them at the Go-API level for mode dispatch. RegisterGraph / RegisterIntelligence / RegisterTopology remain available for unit tests that exercise narrow tool behavior directly. Production cli wiring (registerAllTools) now calls a new RegisterGraphUserFacing that registers only run_cypher + read_file. Doc sweep: CLAUDE.md, README.md, PROJECT_SUMMARY.md updated to reflect the 10-tool surface; integration_test.go updated to assert 10 and spot-check the consolidated names.
1 parent ffb3a16 commit a9c9ba2

7 files changed

Lines changed: 91 additions & 46 deletions

File tree

CLAUDE.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ mcp: Kuzu → QueryService → 6 consolidated MCP tools + run_cypher escape
7474
(mutation gate in `cypher.go`).
7575
- **`internal/mcp`** — 6 consolidated mode-driven tools (`graph_summary`,
7676
`find_in_graph`, `inspect_node`, `trace_relationships`,
77-
`analyze_impact`, `topology_view`), `run_cypher` escape hatch, the
78-
34 deprecated narrow tools, plus `review_changes`.
77+
`analyze_impact`, `topology_view`), `run_cypher` escape hatch,
78+
`read_file` utility, `generate_flow`, and `review_changes` — 10
79+
user-facing tools total. The narrow toolXxx(d) builder funcs remain
80+
in tools_graph.go/tools_intelligence.go/tools_topology.go as Go-API
81+
delegation targets for the consolidated layer; they are NOT
82+
registered as user-facing MCP tools.
7983
- **`internal/review`** — diff parser, Ollama-compatible chat client,
8084
ReviewService orchestrator. Default endpoint = local Ollama;
8185
`OLLAMA_API_KEY` flips to Ollama Cloud.
@@ -197,10 +201,12 @@ codeiq mcp /path/to/repo # for Claude / Cursor wiring
197201

198202
## MCP Tools
199203

200-
The MCP server registers 6 consolidated mode-driven tools + `run_cypher`
201-
+ `review_changes`. The 34 narrow tools from the Java side stay wired
202-
for one release (v1.0.x) for back-compat with agents pinned to old
203-
names; they'll be removed in a future minor.
204+
The MCP server registers 10 user-facing tools — 6 consolidated
205+
mode-driven, `run_cypher` (escape hatch), `read_file` (utility),
206+
`generate_flow`, and `review_changes`. The 24 narrow tools that the
207+
consolidated layer subsumes were dropped from the MCP surface;
208+
their Go-API implementations (`toolXxx(d) Tool`) stay in the package
209+
because the consolidated tools delegate to them.
204210

205211
| Consolidated tool | mode dispatch |
206212
|---|---|

PROJECT_SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
| CLI / MCP server | `go/cmd/codeiq/main.go` | The only binary. All subcommands live in `internal/cli`. |
3838
| Subcommand registry | `internal/cli/root.go` | Sets up cobra root + registers per-subcommand inits. |
3939
| Detector registry | `internal/cli/detectors_register.go` | Blank-imports every detector package leaf. **Choke point** — forget it and detectors silently no-op. |
40-
| Stdio MCP | `internal/cli/mcp.go` + `internal/mcp/server.go` | Wires consolidated tools + the deprecated 34 + `review_changes`. |
40+
| Stdio MCP | `internal/cli/mcp.go` + `internal/mcp/server.go` | Wires 10 user-facing tools: 6 consolidated + `run_cypher` + `read_file` + `generate_flow` + `review_changes`. |
4141
| Analyzer pipeline | `internal/analyzer/analyzer.go` | FileDiscovery → parser → detectors (pool) → GraphBuilder → SQLite. |
4242
| Enrich pipeline | `internal/analyzer/enrich.go` | SQLite → Kuzu + linkers + layer classifier + intelligence. |
4343

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ framework usage. Same input ⇒ same output, every time.
3131
- **100 detectors** across 35+ languages — Java, Kotlin, Scala, Python,
3232
TypeScript/JavaScript, Go, Rust, C#, C++, Terraform, Bicep, Helm,
3333
Kubernetes, Docker, GitHub Actions, GitLab CI, …
34-
- **MCP server included**`codeiq mcp` runs an MCP stdio server with
35-
6 consolidated mode-driven tools (plus 34 deprecated narrow tools for
36-
back-compat) so Claude / Cursor / any MCP-aware agent can query the
37-
graph directly.
34+
- **MCP server included**`codeiq mcp` runs an MCP stdio server
35+
exposing 10 user-facing tools (6 consolidated mode-driven +
36+
`run_cypher` + `read_file` + `generate_flow` + `review_changes`)
37+
so Claude / Cursor / any MCP-aware agent can query the graph
38+
directly.
3839
- **LLM-driven PR review**`codeiq review` walks the diff, queries
3940
the indexed graph for evidence, and asks Ollama (Cloud or local) for
4041
review comments.
@@ -116,11 +117,10 @@ Add to your MCP client config (e.g. `.mcp.json` at the project root):
116117
}
117118
```
118119

119-
Six mode-driven tools (`graph_summary`, `find_in_graph`, `inspect_node`,
120-
`trace_relationships`, `analyze_impact`, `topology_view`) plus
121-
`run_cypher` (escape hatch) and `review_changes` (in-agent PR review).
122-
The deprecated 34 narrow tools remain wired for one release for
123-
back-compat.
120+
Ten user-facing tools: six mode-driven (`graph_summary`,
121+
`find_in_graph`, `inspect_node`, `trace_relationships`,
122+
`analyze_impact`, `topology_view`) plus `run_cypher` (Cypher escape
123+
hatch), `read_file` (utility), `generate_flow`, and `review_changes`.
124124

125125
## CLI reference
126126

go/internal/cli/mcp.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,26 +143,26 @@ To register with Claude Code, add to .mcp.json at the repo root:
143143
return cmd
144144
}
145145

146-
// registerAllTools wires every tool family onto srv. All four families
147-
// land here unconditionally — graph (20) + topology (9) + flow (1) +
148-
// intelligence (4) = 34 tools — matching the Java McpTools registration
149-
// count. The `optionalRegisterHooks` slice remains for forward-compat
150-
// with new tool families that may land in later phases (drill-down
151-
// flows, query planner v2, etc.) without re-touching this function.
146+
// registerAllTools wires every user-facing MCP tool family onto srv:
147+
// graph (run_cypher + read_file = 2) + flow (generate_flow = 1) +
148+
// consolidated (6 mode-driven) + review_changes = 10 tools.
149+
//
150+
// The narrow graph / topology / intelligence tool implementations are
151+
// retained inside the mcp package because the consolidated tools
152+
// delegate to them at the Go-API level, but they are no longer
153+
// registered as user-facing MCP tools (greenfield project, no
154+
// external consumers — the back-compat surface was dropped).
155+
//
156+
// `optionalRegisterHooks` remains for forward-compat with new tool
157+
// families that may land in later phases without re-touching this
158+
// function.
152159
func registerAllTools(srv *mcp.Server, d *mcp.Deps) error {
153-
if err := mcp.RegisterGraph(srv, d); err != nil {
160+
if err := mcp.RegisterGraphUserFacing(srv, d); err != nil {
154161
return fmt.Errorf("register graph tools: %w", err)
155162
}
156-
if err := mcp.RegisterTopology(srv, d); err != nil {
157-
return fmt.Errorf("register topology tools: %w", err)
158-
}
159163
if err := mcp.RegisterFlow(srv, d); err != nil {
160164
return fmt.Errorf("register flow tools: %w", err)
161165
}
162-
if err := mcp.RegisterIntelligence(srv, d); err != nil {
163-
return fmt.Errorf("register intelligence tools: %w", err)
164-
}
165-
// Plan §2 — consolidated tools alongside the deprecated 34.
166166
if err := mcp.RegisterConsolidated(srv, d); err != nil {
167167
return fmt.Errorf("register consolidated tools: %w", err)
168168
}

go/internal/mcp/integration_test.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
// End-to-end MCP integration test. Spawns the real `codeiq mcp` binary,
44
// exchanges JSON-RPC frames over its stdin / stdout, and asserts the
5-
// initialize handshake completes and tools/list returns all 34 tools.
5+
// initialize handshake completes and tools/list returns the 10
6+
// user-facing tools (2 graph + 1 flow + 6 consolidated + 1 review).
67
//
78
// Build tag `integration` keeps this out of the default `go test ./...`
89
// loop because it does a full `go build` first and stands up a fresh
@@ -240,7 +241,7 @@ func TestMCPServerInitializeAndListTools(t *testing.T) {
240241
t.Fatalf("tools/list had no result: %v", listResp)
241242
}
242243
tools, _ := listResult["tools"].([]any)
243-
if len(tools) != 34 {
244+
if len(tools) != 10 {
244245
names := make([]string, 0, len(tools))
245246
for _, tl := range tools {
246247
if m, ok := tl.(map[string]any); ok {
@@ -249,11 +250,15 @@ func TestMCPServerInitializeAndListTools(t *testing.T) {
249250
}
250251
}
251252
}
252-
t.Fatalf("tools/list returned %d tools, want 34. names=%v", len(tools), names)
253+
t.Fatalf("tools/list returned %d tools, want 10. names=%v", len(tools), names)
253254
}
254255

255-
// 4. Spot-check one tool from each family.
256-
wantNames := []string{"get_stats", "get_topology", "generate_flow", "find_node", "get_capabilities"}
256+
// 4. Spot-check representative tool names from the 10-tool surface.
257+
wantNames := []string{
258+
"graph_summary", "find_in_graph", "inspect_node",
259+
"trace_relationships", "analyze_impact", "topology_view",
260+
"run_cypher", "read_file", "generate_flow", "review_changes",
261+
}
257262
have := map[string]bool{}
258263
for _, tl := range tools {
259264
if m, ok := tl.(map[string]any); ok {
@@ -268,33 +273,34 @@ func TestMCPServerInitializeAndListTools(t *testing.T) {
268273
}
269274
}
270275

271-
// 5. Call get_capabilities — synchronous round trip that exercises
272-
// the full tool dispatch path.
276+
// 5. Call graph_summary in capabilities mode — synchronous round
277+
// trip that exercises the consolidated tool dispatch path (which
278+
// internally delegates to the toolGetCapabilities builder).
273279
callResp := client.rpc(t, map[string]any{
274280
"jsonrpc": "2.0",
275281
"id": 3,
276282
"method": "tools/call",
277283
"params": map[string]any{
278-
"name": "get_capabilities",
279-
"arguments": map[string]any{},
284+
"name": "graph_summary",
285+
"arguments": map[string]any{"mode": "capabilities"},
280286
},
281287
})
282288
callResult, ok := callResp["result"].(map[string]any)
283289
if !ok {
284-
t.Fatalf("tools/call get_capabilities had no result: %v", callResp)
290+
t.Fatalf("tools/call graph_summary had no result: %v", callResp)
285291
}
286292
content, _ := callResult["content"].([]any)
287293
if len(content) == 0 {
288-
t.Fatalf("get_capabilities returned empty content")
294+
t.Fatalf("graph_summary returned empty content")
289295
}
290296
first, _ := content[0].(map[string]any)
291297
text, _ := first["text"].(string)
292298
var body map[string]any
293299
if err := json.Unmarshal([]byte(text), &body); err != nil {
294-
t.Fatalf("parse get_capabilities body: %v\ntext=%s", err, text)
300+
t.Fatalf("parse graph_summary body: %v\ntext=%s", err, text)
295301
}
296302
if _, hasMatrix := body["matrix"]; !hasMatrix {
297-
t.Fatalf("get_capabilities body missing matrix: %v", body)
303+
t.Fatalf("graph_summary capabilities body missing matrix: %v", body)
298304
}
299305
}
300306

go/internal/mcp/tools_graph.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@ import (
1717
"github.com/randomcodespace/codeiq/go/internal/graph"
1818
)
1919

20-
// graphTools returns the slice of graph-facing Tool definitions for d.
21-
// Each tool is fully self-contained — no shared mutable state. The
22-
// returned slice is registered in order by RegisterGraph.
20+
// graphTools returns every graph-tier Tool definition for d — the 18
21+
// narrow tools the consolidated layer delegates to, plus the two
22+
// user-facing tools that survive the consolidation: run_cypher (Cypher
23+
// escape hatch) and read_file (utility).
24+
//
25+
// Production wiring (cli/mcp.go → RegisterGraphUserFacing) registers
26+
// only the 2 user-facing tools; tests that exercise the narrow tool
27+
// implementations directly call RegisterGraph to surface all 20. The
28+
// narrow toolXxx(d) builders are also called from tools_consolidated.go
29+
// for Go-API delegation, independent of MCP registration.
2330
func graphTools(d *Deps) []Tool {
2431
return []Tool{
2532
toolGetStats(d),
@@ -45,6 +52,19 @@ func graphTools(d *Deps) []Tool {
4552
}
4653
}
4754

55+
// RegisterGraphUserFacing registers only the user-facing graph-tier
56+
// tools (run_cypher + read_file). Used by production cli wiring —
57+
// the 18 narrow tools were dropped from the user MCP surface in
58+
// favor of the 6 consolidated mode-driven tools.
59+
func RegisterGraphUserFacing(srv *Server, d *Deps) error {
60+
for _, t := range []Tool{toolRunCypher(d), toolReadFile(d)} {
61+
if err := srv.Register(t); err != nil {
62+
return fmt.Errorf("mcp: register graph tool %q: %w", t.Name, err)
63+
}
64+
}
65+
return nil
66+
}
67+
4868
// RegisterGraph appends every graph-facing tool to srv. Errors halt the
4969
// loop so a duplicate name surfaces immediately during server boot.
5070
func RegisterGraph(srv *Server, d *Deps) error {

go/internal/mcp/tools_graph_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ func TestRegisterGraphRegistersAllTwentyTools(t *testing.T) {
130130
}
131131
}
132132

133+
func TestRegisterGraphUserFacingRegistersTwoTools(t *testing.T) {
134+
srv, _ := mcp.NewServer(mcp.ServerOptions{Name: "x", Version: "0"})
135+
if err := mcp.RegisterGraphUserFacing(srv, &mcp.Deps{}); err != nil {
136+
t.Fatalf("RegisterGraphUserFacing: %v", err)
137+
}
138+
want := []string{"read_file", "run_cypher"}
139+
got := srv.Registry().Names()
140+
sort.Strings(got)
141+
if !reflect.DeepEqual(got, want) {
142+
t.Fatalf("registered tools:\n got=%v\nwant=%v", got, want)
143+
}
144+
}
145+
133146
func TestGetStatsReturnsCounts(t *testing.T) {
134147
d := fixtureDeps(t)
135148
out := callTool(t, d, "get_stats", nil)

0 commit comments

Comments
 (0)