You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(mcp): builder helpers for tool defs to collapse Sonar dup gate
The previous attempt (map-dispatch in 9c1e511) fixed the 7-arm switch
but Sonar's gate stayed at 3.49% because the actual duplicated 14 lines
were the structurally identical InputSchema/Properties scaffolding
repeated across the seven Tool struct literals — not the dispatcher.
Introduce three small builder helpers — mkTool(name, desc, opts...),
param(name, type, desc), and required(fields...) — that own the
InputSchema initialisation and Property construction once. The toolDefs
list collapses from 7 repeating struct-literal blocks (8-12 lines each)
to 7 mkTool calls (3-5 lines each).
Same surface, same JSON shape on the wire, no behaviour change. The
helper types are unexported and only used here.
LOC delta: -20 net (65 inserted, 85 deleted). Verified by go test
./internal/mcp/... -count=1 -race (full suite passes) and gofmt clean.
Description: "Returns recent anomalies with temporal causal links, optionally filtered by service. The triage entry point — answers \"what's wrong right now\".",
28
-
InputSchema: InputSchema{
29
-
Type: "object",
30
-
Properties: map[string]Property{
31
-
"since": {Type: "string", Description: "Start time RFC3339. Defaults to 1h ago."},
32
-
"service": {Type: "string", Description: "Filter by service."},
33
-
},
34
-
},
35
-
},
36
-
{
37
-
Name: "get_service_map",
38
-
Description: "Returns the service topology with health scores, error rates, call counts, and dependency edges. Powered by the live GraphRAG.",
Description: "Returns the full span tree for a trace with service names, durations, errors, and linked logs.",
85
-
InputSchema: InputSchema{
86
-
Type: "object",
87
-
Required: []string{"trace_id"},
88
-
Properties: map[string]Property{
89
-
"trace_id": {Type: "string", Description: "The trace ID to visualize."},
90
-
},
91
-
},
92
-
},
93
-
{
94
-
Name: "search_logs",
95
-
Description: "Searches log entries by severity, service, body text, trace ID, and time range. Returns id, timestamp, severity, service_name, body, trace_id. **Limited to the last 24 hours** — windows entirely outside the 24h cap are rejected. Strongly recommend setting `service` and/or `severity` to scope the search; unscoped keyword queries scan large row counts when FTS5 is disabled. Use severity=ERROR to find errors, query= for full-text search, trace_id= to correlate with a trace. Use page= for pagination.",
96
-
InputSchema: InputSchema{
97
-
Type: "object",
98
-
Properties: map[string]Property{
99
-
"query": {Type: "string", Description: "Full-text search in log body."},
"service": {Type: "string", Description: "Filter by service name (exact match)."},
102
-
"trace_id": {Type: "string", Description: "Filter logs belonging to a specific trace ID."},
103
-
"start": {Type: "string", Description: "Start time RFC3339. Defaults to 24h ago. Cannot be earlier than now-24h; older values are clamped."},
104
-
"end": {Type: "string", Description: "End time RFC3339. Defaults to now. Cannot exceed now; future values are clamped."},
105
-
"limit": {Type: "number", Description: "Max results per page (default 50, max 200)."},
106
-
"page": {Type: "number", Description: "Page number for pagination (default 0)."},
107
-
},
108
-
},
109
-
},
54
+
mkTool("get_anomaly_timeline", "Returns recent anomalies with temporal causal links, optionally filtered by service. The triage entry point — answers \"what's wrong right now\".",
55
+
param("since", "string", "Start time RFC3339. Defaults to 1h ago."),
56
+
param("service", "string", "Filter by service."),
57
+
),
58
+
mkTool("get_service_map", "Returns the service topology with health scores, error rates, call counts, and dependency edges. Powered by the live GraphRAG.",
param("service", "string", "Focus on a specific service and its neighbors."),
61
+
),
62
+
mkTool("get_service_health", "Returns detailed health metrics for a specific service: error rate, latency percentiles, request rate, and active alerts.",
63
+
required("service_name"),
64
+
param("service_name", "string", "The service name to query."),
mkTool("trace_graph", "Returns the full span tree for a trace with service names, durations, errors, and linked logs.",
77
+
required("trace_id"),
78
+
param("trace_id", "string", "The trace ID to visualize."),
79
+
),
80
+
mkTool("search_logs", "Searches log entries by severity, service, body text, trace ID, and time range. Returns id, timestamp, severity, service_name, body, trace_id. **Limited to the last 24 hours** — windows entirely outside the 24h cap are rejected. Strongly recommend setting `service` and/or `severity` to scope the search; unscoped keyword queries scan large row counts when FTS5 is disabled. Use severity=ERROR to find errors, query= for full-text search, trace_id= to correlate with a trace. Use page= for pagination.",
81
+
param("query", "string", "Full-text search in log body."),
82
+
param("severity", "string", "Filter by severity level: ERROR, WARN, INFO, DEBUG."),
83
+
param("service", "string", "Filter by service name (exact match)."),
84
+
param("trace_id", "string", "Filter logs belonging to a specific trace ID."),
85
+
param("start", "string", "Start time RFC3339. Defaults to 24h ago. Cannot be earlier than now-24h; older values are clamped."),
86
+
param("end", "string", "End time RFC3339. Defaults to now. Cannot exceed now; future values are clamped."),
87
+
param("limit", "number", "Max results per page (default 50, max 200)."),
88
+
param("page", "number", "Page number for pagination (default 0)."),
89
+
),
110
90
}
111
91
112
92
// mcpCtx returns a tenant-scoped context for repository calls. If the caller's
0 commit comments