Skip to content

Commit 2ed1329

Browse files
authored
test(kiali): add toolset schema marshalling verification (#501)
Signed-off-by: Marc Nuri <marc@marcnuri.com>
1 parent c2e4c45 commit 2ed1329

7 files changed

+305
-20
lines changed

pkg/mcp/testdata/toolsets-core-tools.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@
9898
"inputSchema": {
9999
"type": "object",
100100
"properties": {
101-
"name": {
102-
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
103-
"type": "string"
104-
},
105101
"label_selector": {
106102
"description": "Kubernetes label selector (e.g. 'node-role.kubernetes.io/worker=') to filter nodes by label (Optional, only applicable when name is not provided)",
107103
"pattern": "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]",
108104
"type": "string"
105+
},
106+
"name": {
107+
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
108+
"type": "string"
109109
}
110110
}
111111
},

pkg/mcp/testdata/toolsets-full-tools-multicluster-enum.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,14 @@
279279
],
280280
"type": "string"
281281
},
282-
"name": {
283-
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
284-
"type": "string"
285-
},
286282
"label_selector": {
287283
"description": "Kubernetes label selector (e.g. 'node-role.kubernetes.io/worker=') to filter nodes by label (Optional, only applicable when name is not provided)",
288284
"pattern": "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]",
289285
"type": "string"
286+
},
287+
"name": {
288+
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
289+
"type": "string"
290290
}
291291
}
292292
},

pkg/mcp/testdata/toolsets-full-tools-multicluster.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,14 @@
247247
"description": "Optional parameter selecting which context to run the tool in. Defaults to fake-context if not set",
248248
"type": "string"
249249
},
250-
"name": {
251-
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
252-
"type": "string"
253-
},
254250
"label_selector": {
255251
"description": "Kubernetes label selector (e.g. 'node-role.kubernetes.io/worker=') to filter nodes by label (Optional, only applicable when name is not provided)",
256252
"pattern": "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]",
257253
"type": "string"
254+
},
255+
"name": {
256+
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
257+
"type": "string"
258258
}
259259
}
260260
},

pkg/mcp/testdata/toolsets-full-tools-openshift.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@
199199
"inputSchema": {
200200
"type": "object",
201201
"properties": {
202-
"name": {
203-
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
204-
"type": "string"
205-
},
206202
"label_selector": {
207203
"description": "Kubernetes label selector (e.g. 'node-role.kubernetes.io/worker=') to filter nodes by label (Optional, only applicable when name is not provided)",
208204
"pattern": "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]",
209205
"type": "string"
206+
},
207+
"name": {
208+
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
209+
"type": "string"
210210
}
211211
}
212212
},

pkg/mcp/testdata/toolsets-full-tools.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@
199199
"inputSchema": {
200200
"type": "object",
201201
"properties": {
202-
"name": {
203-
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
204-
"type": "string"
205-
},
206202
"label_selector": {
207203
"description": "Kubernetes label selector (e.g. 'node-role.kubernetes.io/worker=') to filter nodes by label (Optional, only applicable when name is not provided)",
208204
"pattern": "([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]",
209205
"type": "string"
206+
},
207+
"name": {
208+
"description": "Name of the Node to get the resource consumption from (Optional, all Nodes if not provided)",
209+
"type": "string"
210210
}
211211
}
212212
},
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
[
2+
{
3+
"annotations": {
4+
"title": "Topology: Mesh, Graph, Health, and Status",
5+
"readOnlyHint": true,
6+
"destructiveHint": false,
7+
"openWorldHint": true
8+
},
9+
"description": "Returns the topology of a specific namespaces, health, status of the mesh and namespaces. Includes a mesh health summary overview with aggregated counts of healthy, degraded, and failing apps, workloads, and services. Use this for high-level overviews",
10+
"inputSchema": {
11+
"type": "object",
12+
"properties": {
13+
"graphType": {
14+
"description": "Type of graph to return: 'versionedApp', 'app', 'service', 'workload', 'mesh'. Default: 'versionedApp'",
15+
"type": "string"
16+
},
17+
"namespace": {
18+
"description": "Optional single namespace to include in the graph (alternative to namespaces)",
19+
"type": "string"
20+
},
21+
"namespaces": {
22+
"description": "Optional comma-separated list of namespaces to include in the graph",
23+
"type": "string"
24+
},
25+
"rateInterval": {
26+
"description": "Rate interval for fetching (e.g., '10m', '5m', '1h'). Default: '10m'",
27+
"type": "string"
28+
}
29+
}
30+
},
31+
"name": "kiali_get_mesh_graph"
32+
},
33+
{
34+
"annotations": {
35+
"title": "Get Metrics for a Resource",
36+
"readOnlyHint": true,
37+
"destructiveHint": false,
38+
"idempotentHint": true,
39+
"openWorldHint": true
40+
},
41+
"description": "Gets lists or detailed info for Kubernetes resources (services, workloads) within the mesh",
42+
"inputSchema": {
43+
"type": "object",
44+
"properties": {
45+
"byLabels": {
46+
"description": "Comma-separated list of labels to group metrics by (e.g., 'source_workload,destination_service'). Optional",
47+
"type": "string"
48+
},
49+
"direction": {
50+
"description": "Traffic direction: 'inbound' or 'outbound'. Optional, defaults to 'outbound'",
51+
"type": "string"
52+
},
53+
"duration": {
54+
"description": "Time range to get metrics for (optional string - if provided, gets metrics; if empty, get default 1800s).",
55+
"type": "string"
56+
},
57+
"namespace": {
58+
"description": "Namespace to get resources from",
59+
"type": "string"
60+
},
61+
"quantiles": {
62+
"description": "Comma-separated list of quantiles for histogram metrics (e.g., '0.5,0.95,0.99'). Optional",
63+
"type": "string"
64+
},
65+
"rateInterval": {
66+
"description": "Rate interval for metrics (e.g., '1m', '5m'). Optional, defaults to '10m'",
67+
"type": "string"
68+
},
69+
"reporter": {
70+
"description": "Metrics reporter: 'source', 'destination', or 'both'. Optional, defaults to 'source'",
71+
"type": "string"
72+
},
73+
"requestProtocol": {
74+
"description": "Filter by request protocol (e.g., 'http', 'grpc', 'tcp'). Optional",
75+
"type": "string"
76+
},
77+
"resource_name": {
78+
"description": "Name of the resource to get details for (optional string - if provided, gets details; if empty, lists all).",
79+
"type": "string"
80+
},
81+
"resource_type": {
82+
"description": "Type of resource to get details for (service, workload)",
83+
"enum": [
84+
"service",
85+
"workload"
86+
],
87+
"type": "string"
88+
},
89+
"step": {
90+
"description": "Step between data points in seconds (e.g., '15'). Optional, defaults to 15 seconds",
91+
"type": "string"
92+
}
93+
},
94+
"required": [
95+
"resource_type",
96+
"namespace",
97+
"resource_name"
98+
]
99+
},
100+
"name": "kiali_get_metrics"
101+
},
102+
{
103+
"annotations": {
104+
"title": "List or Resource Details",
105+
"readOnlyHint": true,
106+
"destructiveHint": false,
107+
"idempotentHint": true,
108+
"openWorldHint": true
109+
},
110+
"description": "Gets lists or detailed info for Kubernetes resources (services, workloads) within the mesh",
111+
"inputSchema": {
112+
"type": "object",
113+
"properties": {
114+
"namespaces": {
115+
"description": "Comma-separated list of namespaces to get services from (e.g. 'bookinfo' or 'bookinfo,default'). If not provided, will list services from all accessible namespaces",
116+
"type": "string"
117+
},
118+
"resource_name": {
119+
"description": "Name of the resource to get details for (optional string - if provided, gets details; if empty, lists all).",
120+
"type": "string"
121+
},
122+
"resource_type": {
123+
"description": "Type of resource to get details for (service, workload)",
124+
"enum": [
125+
"service",
126+
"workload"
127+
],
128+
"type": "string"
129+
}
130+
}
131+
},
132+
"name": "kiali_get_resource_details"
133+
},
134+
{
135+
"annotations": {
136+
"title": "Get Traces for a Resource or Trace Details",
137+
"readOnlyHint": true,
138+
"destructiveHint": false,
139+
"idempotentHint": true,
140+
"openWorldHint": true
141+
},
142+
"description": "Gets traces for a specific resource (app, service, workload) in a namespace, or gets detailed information for a specific trace by its ID. If traceId is provided, it returns detailed trace information and other parameters are not required.",
143+
"inputSchema": {
144+
"type": "object",
145+
"properties": {
146+
"clusterName": {
147+
"description": "Cluster name for multi-cluster environments (optional, only used when traceId is not provided)",
148+
"type": "string"
149+
},
150+
"endMicros": {
151+
"description": "End time for traces in microseconds since epoch (optional, defaults to 10 minutes after startMicros if not provided, only used when traceId is not provided)",
152+
"type": "string"
153+
},
154+
"limit": {
155+
"description": "Maximum number of traces to return (default: 100, only used when traceId is not provided)",
156+
"minimum": 1,
157+
"type": "integer"
158+
},
159+
"minDuration": {
160+
"description": "Minimum trace duration in microseconds (optional, only used when traceId is not provided)",
161+
"minimum": 0,
162+
"type": "integer"
163+
},
164+
"namespace": {
165+
"description": "Namespace to get resources from. Required if traceId is not provided.",
166+
"type": "string"
167+
},
168+
"resource_name": {
169+
"description": "Name of the resource to get traces for. Required if traceId is not provided.",
170+
"type": "string"
171+
},
172+
"resource_type": {
173+
"description": "Type of resource to get traces for (app, service, workload). Required if traceId is not provided.",
174+
"enum": [
175+
"app",
176+
"service",
177+
"workload"
178+
],
179+
"type": "string"
180+
},
181+
"startMicros": {
182+
"description": "Start time for traces in microseconds since epoch (optional, defaults to 10 minutes before current time if not provided, only used when traceId is not provided)",
183+
"type": "string"
184+
},
185+
"tags": {
186+
"description": "JSON string of tags to filter traces (optional, only used when traceId is not provided)",
187+
"type": "string"
188+
},
189+
"traceId": {
190+
"description": "Unique identifier of the trace to retrieve detailed information for. If provided, this will return detailed trace information and other parameters (resource_type, namespace, resource_name) are not required.",
191+
"type": "string"
192+
}
193+
}
194+
},
195+
"name": "kiali_get_traces"
196+
},
197+
{
198+
"annotations": {
199+
"title": "Manage Istio Config: List, Get, Create, Patch, Delete",
200+
"destructiveHint": true,
201+
"idempotentHint": true,
202+
"openWorldHint": true
203+
},
204+
"description": "Manages Istio configuration objects (Gateways, VirtualServices, etc.). Can list (objects and validations), get, create, patch, or delete objects",
205+
"inputSchema": {
206+
"type": "object",
207+
"properties": {
208+
"action": {
209+
"description": "Action to perform: list, get, create, patch, or delete",
210+
"type": "string"
211+
},
212+
"group": {
213+
"description": "API group of the Istio object (e.g., 'networking.istio.io', 'gateway.networking.k8s.io')",
214+
"type": "string"
215+
},
216+
"json_data": {
217+
"description": "JSON data to apply or create the object",
218+
"type": "string"
219+
},
220+
"kind": {
221+
"description": "Kind of the Istio object (e.g., 'DestinationRule', 'VirtualService', 'HTTPRoute', 'Gateway')",
222+
"type": "string"
223+
},
224+
"name": {
225+
"description": "Name of the Istio object",
226+
"type": "string"
227+
},
228+
"namespace": {
229+
"description": "Namespace containing the Istio object",
230+
"type": "string"
231+
},
232+
"version": {
233+
"description": "API version of the Istio object (e.g., 'v1', 'v1beta1')",
234+
"type": "string"
235+
}
236+
},
237+
"required": [
238+
"action"
239+
]
240+
},
241+
"name": "kiali_manage_istio_config"
242+
},
243+
{
244+
"annotations": {
245+
"title": "Workload: Logs",
246+
"readOnlyHint": true,
247+
"destructiveHint": false,
248+
"openWorldHint": true
249+
},
250+
"description": "Get logs for a specific workload's pods in a namespace. Only requires namespace and workload name - automatically discovers pods and containers. Optionally filter by container name, time range, and other parameters. Container is auto-detected if not specified.",
251+
"inputSchema": {
252+
"type": "object",
253+
"properties": {
254+
"container": {
255+
"description": "Optional container name to filter logs. If not provided, automatically detects and uses the main application container (excludes istio-proxy and istio-init)",
256+
"type": "string"
257+
},
258+
"namespace": {
259+
"description": "Namespace containing the workload",
260+
"type": "string"
261+
},
262+
"since": {
263+
"description": "Time duration to fetch logs from (e.g., '5m', '1h', '30s'). If not provided, returns recent logs",
264+
"type": "string"
265+
},
266+
"tail": {
267+
"description": "Number of lines to retrieve from the end of logs (default: 100)",
268+
"minimum": 1,
269+
"type": "integer"
270+
},
271+
"workload": {
272+
"description": "Name of the workload to get logs for",
273+
"type": "string"
274+
}
275+
},
276+
"required": [
277+
"namespace",
278+
"workload"
279+
]
280+
},
281+
"name": "workload_logs"
282+
}
283+
]

pkg/mcp/toolsets_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/containers/kubernetes-mcp-server/pkg/toolsets/config"
1616
"github.com/containers/kubernetes-mcp-server/pkg/toolsets/core"
1717
"github.com/containers/kubernetes-mcp-server/pkg/toolsets/helm"
18+
"github.com/containers/kubernetes-mcp-server/pkg/toolsets/kiali"
1819
"github.com/containers/kubernetes-mcp-server/pkg/toolsets/kubevirt"
1920
"github.com/mark3labs/mcp-go/mcp"
2021
"github.com/stretchr/testify/suite"
@@ -156,6 +157,7 @@ func (s *ToolsetsSuite) TestGranularToolsetsTools() {
156157
&core.Toolset{},
157158
&config.Toolset{},
158159
&helm.Toolset{},
160+
&kiali.Toolset{},
159161
&kubevirt.Toolset{},
160162
}
161163
for _, testCase := range testCases {

0 commit comments

Comments
 (0)