Skip to content

Commit ee27181

Browse files
committed
checkpoint: pre-yolo 2026-05-13T01:35:47
1 parent 085a7b4 commit ee27181

1 file changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// Consolidated MCP tools — Plan Phase 2.
2+
//
3+
// 34 narrow tools collapsed into 6 mode-driven tools + 1 escape hatch
4+
// (run_cypher, already in tools_graph.go) + 1 review tool (review_changes,
5+
// added in Phase 3). Each consolidated tool takes a `mode` string parameter
6+
// and DELEGATES TO THE EXISTING TOOL HANDLERS — this is a surface change,
7+
// not a query-layer rewrite. The deprecated 34 stay wired for one release
8+
// for back-compat with agents pinned to old names.
9+
//
10+
// graph_summary → get_stats, get_detailed_stats, get_capabilities, get_artifact_metadata
11+
// find_in_graph → query_nodes, query_edges, search_graph, find_node, find_component_by_file, find_related_endpoints
12+
// inspect_node → get_node_neighbors, get_ego_graph, get_evidence_pack, read_file
13+
// trace_relationships → find_callers, find_consumers, find_producers, find_dependencies, find_dependents, find_shortest_path
14+
// analyze_impact → trace_impact, blast_radius, find_cycles, find_circular_deps, find_dead_code, find_dead_services, find_bottlenecks
15+
// topology_view → get_topology, service_detail, service_dependencies, service_dependents, generate_flow
16+
// run_cypher → (escape hatch, unchanged)
17+
package mcp
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"fmt"
23+
)
24+
25+
// RegisterConsolidated appends the 6 new tools to srv. Wired alongside
26+
// the deprecated 34 by the CLI's mcp boot path.
27+
func RegisterConsolidated(srv *Server, d *Deps) error {
28+
for _, t := range []Tool{
29+
toolGraphSummary(d),
30+
toolFindInGraph(d),
31+
toolInspectNode(d),
32+
toolTraceRelationships(d),
33+
toolAnalyzeImpact(d),
34+
toolTopologyView(d),
35+
} {
36+
if err := srv.Register(t); err != nil {
37+
return fmt.Errorf("mcp: register consolidated tool %q: %w", t.Name, err)
38+
}
39+
}
40+
return nil
41+
}
42+
43+
// delegate invokes another tool's handler with a synthesized params object.
44+
// Each consolidated tool's mode dispatches through this so the consolidated
45+
// surface stays in lockstep with the deprecated tools — no logic forks.
46+
func delegate(ctx context.Context, t Tool, params map[string]any) (any, error) {
47+
raw, err := json.Marshal(params)
48+
if err != nil {
49+
return NewErrorEnvelope(CodeInternalError, fmt.Errorf("marshal params: %w", err), RequestID(ctx)), nil
50+
}
51+
return t.Handler(ctx, raw)
52+
}
53+
54+
func toolGraphSummary(d *Deps) Tool {
55+
return Tool{
56+
Name: "graph_summary",
57+
Description: "Graph overview, statistics, capabilities, and provenance " +
58+
"in one tool. `mode`: `overview` (totals + breakdowns), " +
59+
"`categories` (specific category via `category` param), " +
60+
"`capabilities` (intelligence capability matrix), `provenance` " +
61+
"(artifact + index timestamps). Default mode: overview.",
62+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["overview","categories","capabilities","provenance"]},"category":{"type":"string"}}}`),
63+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
64+
var p struct {
65+
Mode string `json:"mode"`
66+
Category string `json:"category"`
67+
}
68+
_ = json.Unmarshal(raw, &p)
69+
if p.Mode == "" {
70+
p.Mode = "overview"
71+
}
72+
switch p.Mode {
73+
case "overview":
74+
return delegate(ctx, toolGetStats(d), nil)
75+
case "categories":
76+
return delegate(ctx, toolGetDetailedStats(d), map[string]any{"category": p.Category})
77+
case "capabilities":
78+
return delegate(ctx, toolGetCapabilities(d), nil)
79+
case "provenance":
80+
return delegate(ctx, toolGetArtifactMetadata(d), nil)
81+
default:
82+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q: expected overview|categories|capabilities|provenance", p.Mode), RequestID(ctx)), nil
83+
}
84+
},
85+
}
86+
}
87+
88+
func toolFindInGraph(d *Deps) Tool {
89+
return Tool{
90+
Name: "find_in_graph",
91+
Description: "Find nodes, edges, or matches. `mode`: " +
92+
"`nodes` (filter by `kind`), `edges` (filter by `kind`), " +
93+
"`text` (label search via `query`), `fuzzy` (Planner-routed match via `query`), " +
94+
"`by_file` (`file_path`), `by_endpoint` (`node_id`).",
95+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["nodes","edges","text","fuzzy","by_file","by_endpoint"]},"kind":{"type":"string"},"query":{"type":"string"},"file_path":{"type":"string"},"node_id":{"type":"string"},"limit":{"type":"integer"}},"required":["mode"]}`),
96+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
97+
var p struct {
98+
Mode string `json:"mode"`
99+
Kind string `json:"kind"`
100+
Query string `json:"query"`
101+
FilePath string `json:"file_path"`
102+
NodeID string `json:"node_id"`
103+
Limit int `json:"limit"`
104+
}
105+
if err := json.Unmarshal(raw, &p); err != nil {
106+
return NewErrorEnvelope(CodeInvalidInput, err, RequestID(ctx)), nil
107+
}
108+
switch p.Mode {
109+
case "nodes":
110+
return delegate(ctx, toolQueryNodes(d), map[string]any{"kind": p.Kind, "limit": p.Limit})
111+
case "edges":
112+
return delegate(ctx, toolQueryEdges(d), map[string]any{"kind": p.Kind, "limit": p.Limit})
113+
case "text":
114+
return delegate(ctx, toolSearchGraph(d), map[string]any{"query": p.Query, "limit": p.Limit})
115+
case "fuzzy":
116+
return delegate(ctx, toolFindNode(d), map[string]any{"query": p.Query})
117+
case "by_file":
118+
return delegate(ctx, toolFindComponentByFile(d), map[string]any{"file_path": p.FilePath})
119+
case "by_endpoint":
120+
return delegate(ctx, toolFindRelatedEndpoints(d), map[string]any{"node_id": p.NodeID})
121+
default:
122+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q", p.Mode), RequestID(ctx)), nil
123+
}
124+
},
125+
}
126+
}
127+
128+
func toolInspectNode(d *Deps) Tool {
129+
return Tool{
130+
Name: "inspect_node",
131+
Description: "Inspect a single node. `mode`: " +
132+
"`neighbors` (1-hop via `node_id` + `direction`), " +
133+
"`ego` (`center` + `radius`), " +
134+
"`evidence` (snippets + provenance via `node_id` or `file_path`), " +
135+
"`source` (file contents via `file_path`).",
136+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["neighbors","ego","evidence","source"]},"node_id":{"type":"string"},"center":{"type":"string"},"radius":{"type":"integer"},"direction":{"type":"string"},"file_path":{"type":"string"}},"required":["mode"]}`),
137+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
138+
var p struct {
139+
Mode string `json:"mode"`
140+
NodeID string `json:"node_id"`
141+
Center string `json:"center"`
142+
Radius int `json:"radius"`
143+
Direction string `json:"direction"`
144+
FilePath string `json:"file_path"`
145+
}
146+
if err := json.Unmarshal(raw, &p); err != nil {
147+
return NewErrorEnvelope(CodeInvalidInput, err, RequestID(ctx)), nil
148+
}
149+
switch p.Mode {
150+
case "neighbors":
151+
return delegate(ctx, toolGetNodeNeighbors(d), map[string]any{"node_id": p.NodeID, "direction": p.Direction})
152+
case "ego":
153+
return delegate(ctx, toolGetEgoGraph(d), map[string]any{"center": p.Center, "radius": p.Radius})
154+
case "evidence":
155+
return delegate(ctx, toolGetEvidencePack(d), map[string]any{"node_id": p.NodeID, "file_path": p.FilePath})
156+
case "source":
157+
return delegate(ctx, toolReadFile(d), map[string]any{"file_path": p.FilePath})
158+
default:
159+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q", p.Mode), RequestID(ctx)), nil
160+
}
161+
},
162+
}
163+
}
164+
165+
func toolTraceRelationships(d *Deps) Tool {
166+
return Tool{
167+
Name: "trace_relationships",
168+
Description: "Walk relationships from a node. `mode`: " +
169+
"`callers` | `consumers` | `producers` | `dependencies` | `dependents` " +
170+
"(all use `node_id`); `shortest_path` (uses `from`+`to`).",
171+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["callers","consumers","producers","dependencies","dependents","shortest_path"]},"node_id":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}},"required":["mode"]}`),
172+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
173+
var p struct {
174+
Mode string `json:"mode"`
175+
NodeID string `json:"node_id"`
176+
From string `json:"from"`
177+
To string `json:"to"`
178+
}
179+
if err := json.Unmarshal(raw, &p); err != nil {
180+
return NewErrorEnvelope(CodeInvalidInput, err, RequestID(ctx)), nil
181+
}
182+
switch p.Mode {
183+
case "callers":
184+
return delegate(ctx, toolFindCallers(d), map[string]any{"node_id": p.NodeID})
185+
case "consumers":
186+
return delegate(ctx, toolFindConsumers(d), map[string]any{"node_id": p.NodeID})
187+
case "producers":
188+
return delegate(ctx, toolFindProducers(d), map[string]any{"node_id": p.NodeID})
189+
case "dependencies":
190+
return delegate(ctx, toolFindDependencies(d), map[string]any{"node_id": p.NodeID})
191+
case "dependents":
192+
return delegate(ctx, toolFindDependents(d), map[string]any{"node_id": p.NodeID})
193+
case "shortest_path":
194+
return delegate(ctx, toolFindShortestPath(d), map[string]any{"from": p.From, "to": p.To})
195+
default:
196+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q", p.Mode), RequestID(ctx)), nil
197+
}
198+
},
199+
}
200+
}
201+
202+
func toolAnalyzeImpact(d *Deps) Tool {
203+
return Tool{
204+
Name: "analyze_impact",
205+
Description: "Architectural-impact queries. `mode`: " +
206+
"`blast_radius` (`node_id`+`depth`), `trace` (`node_id`+`depth`), " +
207+
"`cycles` (`limit`), `circular_deps`, `dead_code` (`kind`+`limit`), " +
208+
"`dead_services`, `bottlenecks`.",
209+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["blast_radius","trace","cycles","circular_deps","dead_code","dead_services","bottlenecks"]},"node_id":{"type":"string"},"depth":{"type":"integer"},"limit":{"type":"integer"},"kind":{"type":"string"}},"required":["mode"]}`),
210+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
211+
var p struct {
212+
Mode string `json:"mode"`
213+
NodeID string `json:"node_id"`
214+
Depth int `json:"depth"`
215+
Limit int `json:"limit"`
216+
Kind string `json:"kind"`
217+
}
218+
if err := json.Unmarshal(raw, &p); err != nil {
219+
return NewErrorEnvelope(CodeInvalidInput, err, RequestID(ctx)), nil
220+
}
221+
switch p.Mode {
222+
case "blast_radius":
223+
return delegate(ctx, toolBlastRadius(d), map[string]any{"node_id": p.NodeID, "depth": p.Depth})
224+
case "trace":
225+
return delegate(ctx, toolTraceImpact(d), map[string]any{"node_id": p.NodeID, "depth": p.Depth})
226+
case "cycles":
227+
return delegate(ctx, toolFindCycles(d), map[string]any{"limit": p.Limit})
228+
case "circular_deps":
229+
return delegate(ctx, toolFindCircularDeps(d), nil)
230+
case "dead_code":
231+
return delegate(ctx, toolFindDeadCode(d), map[string]any{"kind": p.Kind, "limit": p.Limit})
232+
case "dead_services":
233+
return delegate(ctx, toolFindDeadServices(d), nil)
234+
case "bottlenecks":
235+
return delegate(ctx, toolFindBottlenecks(d), nil)
236+
default:
237+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q", p.Mode), RequestID(ctx)), nil
238+
}
239+
},
240+
}
241+
}
242+
243+
func toolTopologyView(d *Deps) Tool {
244+
return Tool{
245+
Name: "topology_view",
246+
Description: "Service topology + architecture flow diagrams. `mode`: " +
247+
"`summary`, `service` (`service_name`), `service_deps` (`service_name`), " +
248+
"`service_dependents` (`service_name`), `flow` (`view`+`format`).",
249+
Schema: json.RawMessage(`{"type":"object","properties":{"mode":{"type":"string","enum":["summary","service","service_deps","service_dependents","flow"]},"service_name":{"type":"string"},"view":{"type":"string"},"format":{"type":"string","enum":["mermaid","dot","yaml"]}},"required":["mode"]}`),
250+
Handler: func(ctx context.Context, raw json.RawMessage) (any, error) {
251+
var p struct {
252+
Mode string `json:"mode"`
253+
ServiceName string `json:"service_name"`
254+
View string `json:"view"`
255+
Format string `json:"format"`
256+
}
257+
if err := json.Unmarshal(raw, &p); err != nil {
258+
return NewErrorEnvelope(CodeInvalidInput, err, RequestID(ctx)), nil
259+
}
260+
switch p.Mode {
261+
case "summary":
262+
return delegate(ctx, toolGetTopology(d), nil)
263+
case "service":
264+
return delegate(ctx, toolServiceDetail(d), map[string]any{"service_name": p.ServiceName})
265+
case "service_deps":
266+
return delegate(ctx, toolServiceDependencies(d), map[string]any{"service_name": p.ServiceName})
267+
case "service_dependents":
268+
return delegate(ctx, toolServiceDependents(d), map[string]any{"service_name": p.ServiceName})
269+
case "flow":
270+
return delegate(ctx, toolGenerateFlow(d), map[string]any{"view": p.View, "format": p.Format})
271+
default:
272+
return NewErrorEnvelope(CodeInvalidInput, fmt.Errorf("unknown mode %q", p.Mode), RequestID(ctx)), nil
273+
}
274+
},
275+
}
276+
}

0 commit comments

Comments
 (0)