@rad-security/mcp-server 0.0.23 → 0.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import cors from 'cors';
4
+ import cors from "cors";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
7
7
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
8
8
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
9
- import { zodToJsonSchema } from 'zod-to-json-schema';
9
+ import { zodToJsonSchema } from "zod-to-json-schema";
10
10
  import { z } from "zod";
11
11
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
12
12
  import express from "express";
@@ -27,18 +27,25 @@ import * as cves from "./operations/cves.js";
27
27
  import * as inbox from "./operations/inbox.js";
28
28
  import * as workflows from "./operations/workflows.js";
29
29
  import * as knowledgeBase from "./operations/knowledge-base.js";
30
+ import * as radql from "./operations/radql.js";
30
31
  import * as cloudCompliance from "./operations/cloud-compliance.js";
32
+ import * as dashboards from "./operations/dashboards.js";
31
33
  import { VERSION } from "./version.js";
34
+ import { logger } from "./logger.js";
32
35
  // Parse toolkit filters from environment variables
33
36
  function parseToolkitFilters() {
34
37
  const includeEnv = process.env.INCLUDE_TOOLKITS;
35
38
  const excludeEnv = process.env.EXCLUDE_TOOLKITS;
36
39
  const result = {};
37
40
  if (includeEnv) {
38
- result.include = includeEnv.split(',').map(s => s.trim());
41
+ result.include = includeEnv
42
+ .split(",")
43
+ .map((s) => s.trim());
39
44
  }
40
45
  if (excludeEnv) {
41
- result.exclude = excludeEnv.split(',').map(s => s.trim());
46
+ result.exclude = excludeEnv
47
+ .split(",")
48
+ .map((s) => s.trim());
42
49
  }
43
50
  return result;
44
51
  }
@@ -66,354 +73,498 @@ async function newServer() {
66
73
  prompts: {},
67
74
  resources: {},
68
75
  tools: {},
69
- logging: {},
70
76
  },
71
77
  });
72
78
  server.setRequestHandler(ListToolsRequestSchema, async () => {
73
79
  const allTools = [
74
80
  // Container tools
75
- ...(isToolkitEnabled("containers", toolkitFilters) ? [
76
- {
77
- name: "list_containers",
78
- description: "List containers secured by RAD Security with optional filtering by image name, image digest, namespace, cluster_id, or free text search",
79
- inputSchema: zodToJsonSchema(containers.ListContainersSchema),
80
- },
81
- {
82
- name: "get_container_details",
83
- description: "Get detailed information about a container secured by RAD Security",
84
- inputSchema: zodToJsonSchema(containers.GetContainerDetailsSchema),
85
- },
86
- ] : []),
81
+ ...(isToolkitEnabled("containers", toolkitFilters)
82
+ ? [
83
+ {
84
+ name: "list_containers",
85
+ description: "List containers secured by RAD Security with optional filtering by image name, image digest, namespace, cluster_id, or free text search",
86
+ inputSchema: zodToJsonSchema(containers.ListContainersSchema),
87
+ },
88
+ {
89
+ name: "get_container_details",
90
+ description: "Get detailed information about a container secured by RAD Security",
91
+ inputSchema: zodToJsonSchema(containers.GetContainerDetailsSchema),
92
+ },
93
+ ]
94
+ : []),
87
95
  // Cluster tools
88
- ...(isToolkitEnabled("clusters", toolkitFilters) ? [
89
- {
90
- name: "list_clusters",
91
- description: "List Kubernetes clusters managed by RAD Security",
92
- inputSchema: zodToJsonSchema(clusters.ListClustersSchema),
93
- },
94
- {
95
- name: "get_cluster_details",
96
- description: "Get detailed information about a specific Kubernetes cluster managed by RAD Security",
97
- inputSchema: zodToJsonSchema(clusters.GetClusterDetailsSchema),
98
- },
99
- ] : []),
96
+ ...(isToolkitEnabled("clusters", toolkitFilters)
97
+ ? [
98
+ {
99
+ name: "list_clusters",
100
+ description: "List Kubernetes clusters managed by RAD Security",
101
+ inputSchema: zodToJsonSchema(clusters.ListClustersSchema),
102
+ },
103
+ {
104
+ name: "get_cluster_details",
105
+ description: "Get detailed information about a specific Kubernetes cluster managed by RAD Security",
106
+ inputSchema: zodToJsonSchema(clusters.GetClusterDetailsSchema),
107
+ },
108
+ ]
109
+ : []),
100
110
  // Identity tools
101
- ...(isToolkitEnabled("identities", toolkitFilters) ? [
102
- {
103
- name: "list_identities",
104
- description: "Get list of identities for a specific Kubernetes cluster",
105
- inputSchema: zodToJsonSchema(identities.ListIdentitiesSchema),
106
- },
107
- {
108
- name: "get_identity_details",
109
- description: "Get detailed information about a specific identity in a Kubernetes cluster",
110
- inputSchema: zodToJsonSchema(identities.GetIdentityDetailsSchema),
111
- },
112
- ] : []),
111
+ ...(isToolkitEnabled("identities", toolkitFilters)
112
+ ? [
113
+ {
114
+ name: "list_identities",
115
+ description: "Get list of identities for a specific Kubernetes cluster",
116
+ inputSchema: zodToJsonSchema(identities.ListIdentitiesSchema),
117
+ },
118
+ {
119
+ name: "get_identity_details",
120
+ description: "Get detailed information about a specific identity in a Kubernetes cluster",
121
+ inputSchema: zodToJsonSchema(identities.GetIdentityDetailsSchema),
122
+ },
123
+ ]
124
+ : []),
113
125
  // Audit tools
114
- ...(isToolkitEnabled("audit", toolkitFilters) ? [
115
- {
116
- name: "who_shelled_into_pod",
117
- description: "Get k8s audit logs with information about users who shelled into a pod",
118
- inputSchema: zodToJsonSchema(audit.WhoShelledIntoPodSchema),
119
- },
120
- ] : []),
126
+ ...(isToolkitEnabled("audit", toolkitFilters)
127
+ ? [
128
+ {
129
+ name: "who_shelled_into_pod",
130
+ description: "Get k8s audit logs with information about users who shelled into a pod",
131
+ inputSchema: zodToJsonSchema(audit.WhoShelledIntoPodSchema),
132
+ },
133
+ ]
134
+ : []),
121
135
  // Cloud Inventory tools
122
- ...(isToolkitEnabled("cloud_inventory", toolkitFilters) ? [
123
- {
124
- name: "list_cloud_resources",
125
- description: "List cloud resources for a specific provider with optional filtering",
126
- inputSchema: zodToJsonSchema(cloudInventory.ListCloudResourcesSchema),
127
- },
128
- {
129
- name: "get_cloud_resource_details",
130
- description: "Get detailed information about a specific cloud resource",
131
- inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceDetailsSchema),
132
- },
133
- {
134
- name: "get_cloud_resource_facets",
135
- description: "Get available facets for filtering cloud resources from a provider",
136
- inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetsSchema),
137
- },
138
- {
139
- name: "get_cloud_resource_facet_value",
140
- description: "Get values for a specific facet from a cloud provider",
141
- inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetValuesSchema),
142
- },
143
- ] : []),
136
+ ...(isToolkitEnabled("cloud_inventory", toolkitFilters)
137
+ ? [
138
+ {
139
+ name: "list_cloud_resources",
140
+ description: "List cloud resources for a specific provider with optional filtering",
141
+ inputSchema: zodToJsonSchema(cloudInventory.ListCloudResourcesSchema),
142
+ },
143
+ {
144
+ name: "get_cloud_resource_details",
145
+ description: "Get detailed information about a specific cloud resource",
146
+ inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceDetailsSchema),
147
+ },
148
+ {
149
+ name: "get_cloud_resource_facets",
150
+ description: "Get available facets for filtering cloud resources from a provider",
151
+ inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetsSchema),
152
+ },
153
+ {
154
+ name: "get_cloud_resource_facet_value",
155
+ description: "Get values for a specific facet from a cloud provider",
156
+ inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetValuesSchema),
157
+ },
158
+ ]
159
+ : []),
144
160
  // Cloud Compliance tools
145
- ...(isToolkitEnabled("cloud_compliance", toolkitFilters) ? [
146
- {
147
- name: "list_compliance_frameworks",
148
- description: "List all compliance frameworks available for cloud resources (e.g., CIS, SOC2, PCI-DSS)",
149
- inputSchema: zodToJsonSchema(cloudCompliance.ListComplianceFrameworksSchema),
150
- },
151
- {
152
- name: "list_framework_requirements",
153
- description: "List all requirements for a specific compliance framework",
154
- inputSchema: zodToJsonSchema(cloudCompliance.ListFrameworkRequirementsSchema),
155
- },
156
- {
157
- name: "list_requirement_controls",
158
- description: "List controls associated with a specific requirement within a compliance framework",
159
- inputSchema: zodToJsonSchema(cloudCompliance.ListRequirementControlsSchema),
160
- },
161
- {
162
- name: "list_compliance_controls",
163
- description: "List all compliance control summaries for the account",
164
- inputSchema: zodToJsonSchema(cloudCompliance.ListComplianceControlsSchema),
165
- },
166
- {
167
- name: "get_compliance_control",
168
- description: "Get detailed information about a specific compliance control",
169
- inputSchema: zodToJsonSchema(cloudCompliance.GetComplianceControlSchema),
170
- },
171
- {
172
- name: "list_control_resources",
173
- description: "List cloud resources associated with a specific compliance control",
174
- inputSchema: zodToJsonSchema(cloudCompliance.ListControlResourcesSchema),
175
- },
176
- ] : []),
161
+ ...(isToolkitEnabled("cloud_compliance", toolkitFilters)
162
+ ? [
163
+ {
164
+ name: "list_compliance_frameworks",
165
+ description: "List all compliance frameworks available for cloud resources (e.g., CIS, SOC2, PCI-DSS)",
166
+ inputSchema: zodToJsonSchema(cloudCompliance.ListComplianceFrameworksSchema),
167
+ },
168
+ {
169
+ name: "list_framework_requirements",
170
+ description: "List all requirements for a specific compliance framework",
171
+ inputSchema: zodToJsonSchema(cloudCompliance.ListFrameworkRequirementsSchema),
172
+ },
173
+ {
174
+ name: "list_requirement_controls",
175
+ description: "List controls associated with a specific requirement within a compliance framework",
176
+ inputSchema: zodToJsonSchema(cloudCompliance.ListRequirementControlsSchema),
177
+ },
178
+ {
179
+ name: "list_compliance_controls",
180
+ description: "List all compliance control summaries for the account",
181
+ inputSchema: zodToJsonSchema(cloudCompliance.ListComplianceControlsSchema),
182
+ },
183
+ {
184
+ name: "get_compliance_control",
185
+ description: "Get detailed information about a specific compliance control",
186
+ inputSchema: zodToJsonSchema(cloudCompliance.GetComplianceControlSchema),
187
+ },
188
+ {
189
+ name: "list_control_resources",
190
+ description: "List cloud resources associated with a specific compliance control",
191
+ inputSchema: zodToJsonSchema(cloudCompliance.ListControlResourcesSchema),
192
+ },
193
+ ]
194
+ : []),
177
195
  // Image tools
178
- ...(isToolkitEnabled("images", toolkitFilters) ? [
179
- {
180
- name: "list_images",
181
- description: "List container images with optional filtering by page, page size, sort, and search query",
182
- inputSchema: zodToJsonSchema(images.ListImagesSchema),
183
- },
184
- {
185
- name: "list_image_vulnerabilities",
186
- description: "List vulnerabilities in a container image with optional filtering by severity",
187
- inputSchema: zodToJsonSchema(images.ListImageVulnerabilitiesSchema),
188
- },
189
- {
190
- name: "get_top_vulnerable_images",
191
- description: "Get the most vulnerable images from your account",
192
- inputSchema: zodToJsonSchema(z.object({})),
193
- },
194
- {
195
- name: "get_image_sbom",
196
- description: "Get the SBOM of a container image",
197
- inputSchema: zodToJsonSchema(images.GetImageSBOMSchema),
198
- },
199
- ] : []),
196
+ ...(isToolkitEnabled("images", toolkitFilters)
197
+ ? [
198
+ {
199
+ name: "list_images",
200
+ description: "List container images with optional filtering by page, page size, sort, and search query",
201
+ inputSchema: zodToJsonSchema(images.ListImagesSchema),
202
+ },
203
+ {
204
+ name: "list_image_vulnerabilities",
205
+ description: "List vulnerabilities in a container image with optional filtering by severity",
206
+ inputSchema: zodToJsonSchema(images.ListImageVulnerabilitiesSchema),
207
+ },
208
+ {
209
+ name: "get_top_vulnerable_images",
210
+ description: "Get the most vulnerable images from your account",
211
+ inputSchema: zodToJsonSchema(z.object({})),
212
+ },
213
+ {
214
+ name: "get_image_sbom",
215
+ description: "Get the SBOM of a container image",
216
+ inputSchema: zodToJsonSchema(images.GetImageSBOMSchema),
217
+ },
218
+ ]
219
+ : []),
200
220
  // Kubernetes Object tools
201
- ...(isToolkitEnabled("kubeobject", toolkitFilters) ? [
202
- {
203
- name: "get_k8s_resource_details",
204
- description: "Get the latest manifest of a Kubernetes resource",
205
- inputSchema: zodToJsonSchema(kubeobject.GetKubernetesResourceDetailsSchema),
206
- },
207
- {
208
- name: "list_k8s_resources",
209
- description: "List Kubernetes resources with optional filtering by namespace, resource types, and cluster",
210
- inputSchema: zodToJsonSchema(kubeobject.ListKubernetesResourcesSchema),
211
- },
212
- ] : []),
221
+ ...(isToolkitEnabled("kubeobject", toolkitFilters)
222
+ ? [
223
+ {
224
+ name: "get_k8s_resource_details",
225
+ description: "Get the latest manifest of a Kubernetes resource",
226
+ inputSchema: zodToJsonSchema(kubeobject.GetKubernetesResourceDetailsSchema),
227
+ },
228
+ {
229
+ name: "list_k8s_resources",
230
+ description: "List Kubernetes resources with optional filtering by namespace, resource types, and cluster",
231
+ inputSchema: zodToJsonSchema(kubeobject.ListKubernetesResourcesSchema),
232
+ },
233
+ ]
234
+ : []),
213
235
  // Manifest Misconfigurations tools
214
- ...(isToolkitEnabled("misconfigs", toolkitFilters) ? [
215
- {
216
- name: "list_k8s_resource_misconfigs",
217
- description: "Get manifest misconfigurations for a Kubernetes resource",
218
- inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationsSchema),
219
- },
220
- {
221
- name: "get_k8s_resource_misconfig",
222
- description: "Get detailed information about a specific Kubernetes resource misconfiguration",
223
- inputSchema: zodToJsonSchema(misconfigs.GetKubernetesResourceMisconfigurationDetailsSchema),
224
- },
225
- {
226
- name: "list_k8s_resource_misconfig_policies",
227
- description: "List available misconfiguration policies used by RAD Security to detect Kubernetes resource misconfigurations",
228
- inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationPoliciesSchema),
229
- },
230
- ] : []),
236
+ ...(isToolkitEnabled("misconfigs", toolkitFilters)
237
+ ? [
238
+ {
239
+ name: "list_k8s_resource_misconfigs",
240
+ description: "Get manifest misconfigurations for a Kubernetes resource",
241
+ inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationsSchema),
242
+ },
243
+ {
244
+ name: "get_k8s_resource_misconfig",
245
+ description: "Get detailed information about a specific Kubernetes resource misconfiguration",
246
+ inputSchema: zodToJsonSchema(misconfigs.GetKubernetesResourceMisconfigurationDetailsSchema),
247
+ },
248
+ {
249
+ name: "list_k8s_resource_misconfig_policies",
250
+ description: "List available misconfiguration policies used by RAD Security to detect Kubernetes resource misconfigurations",
251
+ inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationPoliciesSchema),
252
+ },
253
+ ]
254
+ : []),
231
255
  // Runtime tools
232
- ...(isToolkitEnabled("runtime", toolkitFilters) ? [
233
- {
234
- name: "get_containers_process_trees",
235
- description: "Get process trees for multiple containers",
236
- inputSchema: zodToJsonSchema(runtime.GetContainersProcessTreesSchema),
237
- },
238
- {
239
- name: "get_containers_baselines",
240
- description: "Get runtime baselines for multiple containers",
241
- inputSchema: zodToJsonSchema(runtime.GetContainersBaselinesSchema),
242
- },
243
- {
244
- name: "get_container_llm_analysis",
245
- description: "Get LLM analysis of a container's process tree",
246
- inputSchema: zodToJsonSchema(runtime.GetContainerLLMAnalysisSchema),
247
- },
248
- ] : []),
256
+ ...(isToolkitEnabled("runtime", toolkitFilters)
257
+ ? [
258
+ {
259
+ name: "get_containers_process_trees",
260
+ description: "Get process trees for multiple containers",
261
+ inputSchema: zodToJsonSchema(runtime.GetContainersProcessTreesSchema),
262
+ },
263
+ {
264
+ name: "get_containers_baselines",
265
+ description: "Get runtime baselines for multiple containers",
266
+ inputSchema: zodToJsonSchema(runtime.GetContainersBaselinesSchema),
267
+ },
268
+ {
269
+ name: "get_container_llm_analysis",
270
+ description: "Get LLM analysis of a container's process tree",
271
+ inputSchema: zodToJsonSchema(runtime.GetContainerLLMAnalysisSchema),
272
+ },
273
+ ]
274
+ : []),
249
275
  // Runtime Network tools
250
- ...(isToolkitEnabled("runtime_network", toolkitFilters) ? [
251
- {
252
- name: "list_http_requests",
253
- description: "List HTTP requests insights with optional filtering by method, path, source and destination workloads, and PII detection",
254
- inputSchema: zodToJsonSchema(runtimeNetwork.listHttpRequestsSchema),
255
- },
256
- {
257
- name: "list_network_connections",
258
- description: "List network connections with optional filtering",
259
- inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionsSchema),
260
- },
261
- {
262
- name: "list_network_connection_srcs",
263
- description: "List network connection sources with optional filtering by source and destination workloads",
264
- inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionSourcesSchema),
265
- },
266
- ] : []),
276
+ ...(isToolkitEnabled("runtime_network", toolkitFilters)
277
+ ? [
278
+ {
279
+ name: "list_http_requests",
280
+ description: "List HTTP requests insights with optional filtering by method, path, source and destination workloads, and PII detection",
281
+ inputSchema: zodToJsonSchema(runtimeNetwork.listHttpRequestsSchema),
282
+ },
283
+ {
284
+ name: "list_network_connections",
285
+ description: "List network connections with optional filtering",
286
+ inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionsSchema),
287
+ },
288
+ {
289
+ name: "list_network_connection_srcs",
290
+ description: "List network connection sources with optional filtering by source and destination workloads",
291
+ inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionSourcesSchema),
292
+ },
293
+ ]
294
+ : []),
267
295
  // Threat Vectors tools
268
- ...(isToolkitEnabled("threats", toolkitFilters) ? [
269
- {
270
- name: "list_threat_vectors",
271
- description: "List threat vectors",
272
- inputSchema: zodToJsonSchema(threats.listThreatVectorsSchema),
273
- },
274
- ] : []),
296
+ ...(isToolkitEnabled("threats", toolkitFilters)
297
+ ? [
298
+ {
299
+ name: "list_threat_vectors",
300
+ description: "List threat vectors",
301
+ inputSchema: zodToJsonSchema(threats.listThreatVectorsSchema),
302
+ },
303
+ ]
304
+ : []),
275
305
  // Findings tools
276
- ...(isToolkitEnabled("findings", toolkitFilters) ? [
277
- {
278
- name: "list_security_findings",
279
- description: "List security findings with optional filtering by types, severities, sources, and status",
280
- inputSchema: zodToJsonSchema(findings.listFindingsSchema),
281
- },
282
- {
283
- name: "update_security_finding_status",
284
- description: "Update the status of a security finding",
285
- inputSchema: zodToJsonSchema(findings.updateFindingStatusSchema),
286
- },
287
- ] : []),
306
+ ...(isToolkitEnabled("findings", toolkitFilters)
307
+ ? [
308
+ {
309
+ name: "list_security_findings",
310
+ description: "List security findings with optional filtering by types, severities, sources, and status",
311
+ inputSchema: zodToJsonSchema(findings.listFindingsSchema),
312
+ },
313
+ {
314
+ name: "update_security_finding_status",
315
+ description: "Update the status of a security finding",
316
+ inputSchema: zodToJsonSchema(findings.updateFindingStatusSchema),
317
+ },
318
+ ]
319
+ : []),
288
320
  // CVE tools
289
- ...(isToolkitEnabled("cves", toolkitFilters) ? [
290
- {
291
- name: "list_cve_vendors",
292
- description: "Get a list of all vendors in the CVE database. Source: cve-search.org",
293
- inputSchema: zodToJsonSchema(z.object({})),
294
- },
295
- {
296
- name: "list_cve_products",
297
- description: "Get a list of all products associated with a vendor in the CVE database. Source: cve-search.org",
298
- inputSchema: zodToJsonSchema(z.object({
299
- vendor: z.string().describe("Vendor name to list products for")
300
- })),
301
- },
302
- {
303
- name: "search_cves",
304
- description: "Search CVEs by vendor and optionally product. Source: cve-search.org",
305
- inputSchema: zodToJsonSchema(cves.searchCvesSchema),
306
- },
307
- {
308
- name: "get_cve",
309
- description: "Get details for a specific CVE ID. Source: cve-search.org",
310
- inputSchema: zodToJsonSchema(cves.getCveSchema),
311
- },
312
- {
313
- name: "get_latest_30_cves",
314
- description: "Get the latest/newest 30 CVEs including CAPEC, CWE and CPE expansions. Source: cve-search.org",
315
- inputSchema: zodToJsonSchema(z.object({})),
316
- },
317
- ] : []),
321
+ ...(isToolkitEnabled("cves", toolkitFilters)
322
+ ? [
323
+ {
324
+ name: "list_cve_vendors",
325
+ description: "Get a list of all vendors in the CVE database. Source: cve-search.org",
326
+ inputSchema: zodToJsonSchema(z.object({})),
327
+ },
328
+ {
329
+ name: "list_cve_products",
330
+ description: "Get a list of all products associated with a vendor in the CVE database. Source: cve-search.org",
331
+ inputSchema: zodToJsonSchema(z.object({
332
+ vendor: z
333
+ .string()
334
+ .describe("Vendor name to list products for"),
335
+ })),
336
+ },
337
+ {
338
+ name: "search_cves",
339
+ description: "Search CVEs by vendor and optionally product. Source: cve-search.org",
340
+ inputSchema: zodToJsonSchema(cves.searchCvesSchema),
341
+ },
342
+ {
343
+ name: "get_cve",
344
+ description: "Get details for a specific CVE ID. Source: cve-search.org",
345
+ inputSchema: zodToJsonSchema(cves.getCveSchema),
346
+ },
347
+ {
348
+ name: "get_latest_30_cves",
349
+ description: "Get the latest/newest 30 CVEs including CAPEC, CWE and CPE expansions. Source: cve-search.org",
350
+ inputSchema: zodToJsonSchema(z.object({})),
351
+ },
352
+ ]
353
+ : []),
318
354
  // Inbox tools
319
- ...(isToolkitEnabled("inbox", toolkitFilters) ? [
320
- {
321
- name: "mark_inbox_item_as_false_positive",
322
- description: "Mark an inbox item as a false positive with a reason",
323
- inputSchema: zodToJsonSchema(inbox.MarkInboxItemAsFalsePositiveSchema),
324
- },
325
- {
326
- name: "list_inbox_items",
327
- description: "List inbox items with optional filtering by any field. Multiple filters can be combined eg. 'search:cve-2024-12345 and severity:high'",
328
- inputSchema: zodToJsonSchema(inbox.ListInboxItemsSchema),
329
- },
330
- {
331
- name: "get_inbox_item_details",
332
- description: "Get detailed information about a specific inbox item",
333
- inputSchema: zodToJsonSchema(inbox.GetInboxItemDetailsSchema),
334
- },
335
- ] : []),
355
+ ...(isToolkitEnabled("inbox", toolkitFilters)
356
+ ? [
357
+ {
358
+ name: "mark_inbox_item_as_false_positive",
359
+ description: "Mark an inbox item as a false positive with a reason",
360
+ inputSchema: zodToJsonSchema(inbox.MarkInboxItemAsFalsePositiveSchema),
361
+ },
362
+ {
363
+ name: "list_inbox_items",
364
+ description: "List inbox items with optional filtering by any field. Multiple filters can be combined eg. 'search:cve-2024-12345 and severity:high'",
365
+ inputSchema: zodToJsonSchema(inbox.ListInboxItemsSchema),
366
+ },
367
+ {
368
+ name: "get_inbox_item_details",
369
+ description: "Get detailed information about a specific inbox item",
370
+ inputSchema: zodToJsonSchema(inbox.GetInboxItemDetailsSchema),
371
+ },
372
+ ]
373
+ : []),
336
374
  // Workflows tools
337
- ...(isToolkitEnabled("workflows", toolkitFilters) ? [
338
- {
339
- name: "list_workflows",
340
- description: "List all workflows",
341
- inputSchema: zodToJsonSchema(workflows.ListWorkflowsSchema),
342
- },
343
- {
344
- name: "get_workflow",
345
- description: "Get detailed information about a specific workflow by ID. It contains the workflow definition, default arguments, and schema how to run the workflow",
346
- inputSchema: zodToJsonSchema(workflows.GetWorkflowSchema),
347
- },
348
- {
349
- name: "list_workflow_runs",
350
- description: "List workflow runs with optional filtering by workflow ID",
351
- inputSchema: zodToJsonSchema(workflows.ListWorkflowRunsSchema),
352
- },
353
- {
354
- name: "get_workflow_run",
355
- description: "Get detailed information about a specific workflow run",
356
- inputSchema: zodToJsonSchema(workflows.GetWorkflowRunSchema),
357
- },
358
- {
359
- name: "run_workflow",
360
- description: "Run a workflow with optional argument overrides",
361
- inputSchema: zodToJsonSchema(workflows.RunWorkflowSchema),
362
- },
363
- {
364
- name: "list_workflow_schedules",
365
- description: "List workflow schedules with optional filtering by workflow ID",
366
- inputSchema: zodToJsonSchema(workflows.ListWorkflowSchedulesSchema),
367
- },
368
- ] : []),
375
+ ...(isToolkitEnabled("workflows", toolkitFilters)
376
+ ? [
377
+ {
378
+ name: "list_workflows",
379
+ description: "List all workflows",
380
+ inputSchema: zodToJsonSchema(workflows.ListWorkflowsSchema),
381
+ },
382
+ {
383
+ name: "get_workflow",
384
+ description: "Get detailed information about a specific workflow by ID. It contains the workflow definition, default arguments, and schema how to run the workflow",
385
+ inputSchema: zodToJsonSchema(workflows.GetWorkflowSchema),
386
+ },
387
+ {
388
+ name: "list_workflow_runs",
389
+ description: "List workflow runs with optional filtering by workflow ID",
390
+ inputSchema: zodToJsonSchema(workflows.ListWorkflowRunsSchema),
391
+ },
392
+ {
393
+ name: "get_workflow_run",
394
+ description: "Get detailed information about a specific workflow run",
395
+ inputSchema: zodToJsonSchema(workflows.GetWorkflowRunSchema),
396
+ },
397
+ {
398
+ name: "run_workflow",
399
+ description: "Run a workflow with optional argument overrides",
400
+ inputSchema: zodToJsonSchema(workflows.RunWorkflowSchema),
401
+ },
402
+ {
403
+ name: "list_workflow_schedules",
404
+ description: "List workflow schedules with optional filtering by workflow ID",
405
+ inputSchema: zodToJsonSchema(workflows.ListWorkflowSchedulesSchema),
406
+ },
407
+ ]
408
+ : []),
369
409
  // Knowledge Base tools
370
- ...(isToolkitEnabled("knowledge_base", toolkitFilters) ? [
371
- {
372
- name: "search_knowledge_base",
373
- description: "Search your organization's knowledge base to find relevant uploaded documents, procedures, reports, and other content using natural language queries",
374
- inputSchema: zodToJsonSchema(knowledgeBase.SearchKnowledgeBaseSchema),
375
- },
376
- {
377
- name: "list_knowledge_base_collections",
378
- description: "List all collections in your organization's knowledge base. Collections are used to organize and categorize documents",
379
- inputSchema: zodToJsonSchema(knowledgeBase.ListCollectionsSchema),
380
- },
381
- {
382
- name: "list_knowledge_base_documents",
383
- description: "List documents in your organization's knowledge base with optional filtering by collections, file type, or status",
384
- inputSchema: zodToJsonSchema(knowledgeBase.ListDocumentsSchema),
385
- },
386
- {
387
- name: "query_knowledge_base_document",
388
- description: "Query a CSV document from the knowledge base using natural language. IMPORTANT: This tool ONLY works with CSV documents. Use list_knowledge_base_documents with filters='file_type:csv' to find CSV document IDs (search_knowledge_base results also contain document IDs). Results are returned as a markdown table",
389
- inputSchema: zodToJsonSchema(knowledgeBase.StructuredQueryDocumentSchema),
390
- },
391
- ] : []),
410
+ ...(isToolkitEnabled("knowledge_base", toolkitFilters)
411
+ ? [
412
+ {
413
+ name: "search_knowledge_base",
414
+ description: "Search your organization's knowledge base to find relevant uploaded documents, procedures, reports, and other content using natural language queries",
415
+ inputSchema: zodToJsonSchema(knowledgeBase.SearchKnowledgeBaseSchema),
416
+ },
417
+ {
418
+ name: "list_knowledge_base_collections",
419
+ description: "List all collections in your organization's knowledge base. Collections are used to organize and categorize documents",
420
+ inputSchema: zodToJsonSchema(knowledgeBase.ListCollectionsSchema),
421
+ },
422
+ {
423
+ name: "list_knowledge_base_documents",
424
+ description: "List documents in your organization's knowledge base with optional filtering by collections, file type, or status",
425
+ inputSchema: zodToJsonSchema(knowledgeBase.ListDocumentsSchema),
426
+ },
427
+ {
428
+ name: "query_knowledge_base_document",
429
+ description: "Query a CSV document from the knowledge base using natural language. IMPORTANT: This tool ONLY works with CSV documents. Use list_knowledge_base_documents with filters='file_type:csv' to find CSV document IDs (search_knowledge_base results also contain document IDs). Results are returned as a markdown table",
430
+ inputSchema: zodToJsonSchema(knowledgeBase.StructuredQueryDocumentSchema),
431
+ },
432
+ ]
433
+ : []),
434
+ // RadQL tools
435
+ ...(isToolkitEnabled("radql", toolkitFilters)
436
+ ? [
437
+ {
438
+ name: "radql_list_data_types",
439
+ description: "List all available RadQL data types (discovery). ALWAYS call this FIRST before using other RadQL tools to discover what data is available to query. Returns data types like 'containers', 'kubernetes_resources', 'inbox_items', 'vulnerabilities', etc. with descriptions.",
440
+ inputSchema: zodToJsonSchema(radql.RadQLListDataTypesSchema),
441
+ },
442
+ {
443
+ name: "radql_get_type_metadata",
444
+ description: "Get schema/metadata for a specific RadQL data type. Shows available fields, data types, which fields can be filtered/searched, and provides query examples. Call this AFTER radql_list_data_types to understand how to query a specific data type.",
445
+ inputSchema: zodToJsonSchema(radql.RadQLGetTypeMetadataSchema),
446
+ },
447
+ {
448
+ name: "radql_list_filter_values",
449
+ description: "List possible values for a filter field (e.g., namespace list, cluster list, severity values). Useful for building dynamic filters when you need to know available enum-like values. Call this when constructing filters that need specific values.",
450
+ inputSchema: zodToJsonSchema(radql.RadQLListFilterValuesSchema),
451
+ },
452
+ {
453
+ name: "radql_query",
454
+ description: `Execute RadQL queries for security investigations. Supports: list (filter/search), get_by_id (single item), stats (aggregations).
455
+
456
+ WORKFLOW: radql_list_data_types -> radql_get_type_metadata -> radql_query
457
+
458
+ COMMON FIELDS BY DATA TYPE:
459
+ containers: name, image_name, image_repo, owner_kind, cluster_id, created_at
460
+ Example: image_name:*nginx* AND owner_kind:Pod
461
+
462
+ finding_groups: type, source_kind, source_name, rule_title, severity, event_timestamp
463
+ Types: k8s_misconfiguration, k8s_audit_logs_anomaly, threat_vector
464
+ Example: type:k8s_misconfiguration AND severity:critical
465
+
466
+ inbox_items: severity (High|Medium|Low), type, title, archived, false_positive, created_at
467
+ Example: severity:High AND archived:false
468
+
469
+ kubernetes_resources: kind, name, namespace, cluster_id, owner_kind, created_at
470
+ Example: kind:Deployment AND namespace:production
471
+
472
+ CRITICAL QUOTING RULES:
473
+ MUST quote when value contains:
474
+ - Dates/timestamps: created_at>"2024-01-01" (NOT created_at>2024-01-01)
475
+ - Hyphens: cluster_id:"abc-123-def", name:"kube-system"
476
+ - UUIDs: id:"550e8400-e29b-41d4-a716-446655440000"
477
+ - Spaces: title:"my alert"
478
+ - Special chars: :, =, <, >, !, (, )
479
+ - Wildcards with hyphens: name:"kube-*"
480
+
481
+ OK to leave unquoted:
482
+ - Simple strings: status:active, kind:Pod
483
+ - Numbers: count:123
484
+ - Booleans: archived:true
485
+ - Simple wildcards: name:nginx*
486
+
487
+ For complete schema: call radql_get_type_metadata with target data_type`,
488
+ inputSchema: zodToJsonSchema(radql.RadQLQuerySchema),
489
+ },
490
+ {
491
+ name: "radql_query_builder",
492
+ description: "Helper tool to build RadQL queries programmatically from structured conditions. Useful when you need to construct complex filter or stats queries from structured inputs.",
493
+ inputSchema: zodToJsonSchema(radql.RadQLQueryBuilderSchema),
494
+ },
495
+ {
496
+ name: "radql_batch_query",
497
+ description: "Execute multiple RadQL queries in parallel for efficiency. Useful for fetching related data from different data types simultaneously (e.g., container details + vulnerabilities + network connections).",
498
+ inputSchema: zodToJsonSchema(radql.RadQLBatchQuerySchema),
499
+ },
500
+ ]
501
+ : []),
502
+ // Dashboards tools
503
+ ...(isToolkitEnabled("dashboards", toolkitFilters)
504
+ ? [
505
+ {
506
+ name: "list_widget_templates",
507
+ description: "List widget templates with optional filtering by visualization type and category",
508
+ inputSchema: zodToJsonSchema(dashboards.ListWidgetTemplatesSchema),
509
+ },
510
+ {
511
+ name: "get_widget_template",
512
+ description: "Get detailed information about a specific widget template",
513
+ inputSchema: zodToJsonSchema(dashboards.GetWidgetTemplateSchema),
514
+ },
515
+ {
516
+ name: "list_dashboard_templates",
517
+ description: "List dashboard templates with optional filtering by category",
518
+ inputSchema: zodToJsonSchema(dashboards.ListDashboardTemplatesSchema),
519
+ },
520
+ {
521
+ name: "get_dashboard_template",
522
+ description: "Get detailed information about a specific dashboard template",
523
+ inputSchema: zodToJsonSchema(dashboards.GetDashboardTemplateSchema),
524
+ },
525
+ {
526
+ name: "list_dashboards",
527
+ description: "List dashboards for the account",
528
+ inputSchema: zodToJsonSchema(dashboards.ListDashboardsSchema),
529
+ },
530
+ {
531
+ name: "get_dashboard",
532
+ description: "Get detailed information about a specific dashboard",
533
+ inputSchema: zodToJsonSchema(dashboards.GetDashboardSchema),
534
+ },
535
+ ]
536
+ : []),
392
537
  ];
393
538
  return {
394
539
  tools: allTools,
395
540
  };
396
541
  });
397
542
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
543
+ const startTime = Date.now();
544
+ const toolName = request.params.name;
398
545
  try {
399
546
  if (!request.params.arguments) {
400
547
  throw new Error("Arguments are required");
401
548
  }
402
- const toolName = request.params.name;
549
+ logger.info({ tool: toolName, arguments: request.params.arguments }, "tool_invoked");
403
550
  switch (toolName) {
404
551
  // Container tools
405
552
  case "list_containers": {
406
553
  const args = containers.ListContainersSchema.parse(request.params.arguments);
407
554
  const response = await containers.listContainers(client, args.offset, args.limit, args.filters, args.q);
408
555
  return {
409
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
556
+ content: [
557
+ { type: "text", text: JSON.stringify(response, null, 2) },
558
+ ],
410
559
  };
411
560
  }
412
561
  case "get_container_details": {
413
562
  const args = containers.GetContainerDetailsSchema.parse(request.params.arguments);
414
563
  const response = await containers.getContainerDetails(client, args.container_id);
415
564
  return {
416
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
565
+ content: [
566
+ { type: "text", text: JSON.stringify(response, null, 2) },
567
+ ],
417
568
  };
418
569
  }
419
570
  // Cluster tools
@@ -421,14 +572,18 @@ async function newServer() {
421
572
  const args = clusters.ListClustersSchema.parse(request.params.arguments);
422
573
  const response = await clusters.listClusters(client, args.page_size, args.page);
423
574
  return {
424
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
575
+ content: [
576
+ { type: "text", text: JSON.stringify(response, null, 2) },
577
+ ],
425
578
  };
426
579
  }
427
580
  case "get_cluster_details": {
428
581
  const args = clusters.GetClusterDetailsSchema.parse(request.params.arguments);
429
582
  const response = await clusters.getClusterDetails(client, args.cluster_id);
430
583
  return {
431
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
584
+ content: [
585
+ { type: "text", text: JSON.stringify(response, null, 2) },
586
+ ],
432
587
  };
433
588
  }
434
589
  // Identity tools
@@ -436,14 +591,18 @@ async function newServer() {
436
591
  const args = identities.ListIdentitiesSchema.parse(request.params.arguments);
437
592
  const response = await identities.listIdentities(client, args.identity_types, args.cluster_ids, args.page, args.page_size, args.q);
438
593
  return {
439
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
594
+ content: [
595
+ { type: "text", text: JSON.stringify(response, null, 2) },
596
+ ],
440
597
  };
441
598
  }
442
599
  case "get_identity_details": {
443
600
  const args = identities.GetIdentityDetailsSchema.parse(request.params.arguments);
444
601
  const response = await identities.getIdentityDetails(client, args.identity_id);
445
602
  return {
446
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
603
+ content: [
604
+ { type: "text", text: JSON.stringify(response, null, 2) },
605
+ ],
447
606
  };
448
607
  }
449
608
  // Audit tools
@@ -451,7 +610,9 @@ async function newServer() {
451
610
  const args = audit.WhoShelledIntoPodSchema.parse(request.params.arguments);
452
611
  const response = await audit.whoShelledIntoPod(client, args.name, args.namespace, args.cluster_id, args.from_time, args.to_time, args.limit, args.page);
453
612
  return {
454
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
613
+ content: [
614
+ { type: "text", text: JSON.stringify(response, null, 2) },
615
+ ],
455
616
  };
456
617
  }
457
618
  // Cloud Inventory tools
@@ -459,28 +620,36 @@ async function newServer() {
459
620
  const args = cloudInventory.ListCloudResourcesSchema.parse(request.params.arguments);
460
621
  const response = await cloudInventory.listCloudResources(client, args.provider, args.filters, args.offset, args.limit, args.q);
461
622
  return {
462
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
623
+ content: [
624
+ { type: "text", text: JSON.stringify(response, null, 2) },
625
+ ],
463
626
  };
464
627
  }
465
628
  case "get_cloud_resource_details": {
466
629
  const args = cloudInventory.GetCloudResourceDetailsSchema.parse(request.params.arguments);
467
630
  const response = await cloudInventory.getCloudResourceDetails(client, args.provider, args.resource_type, args.resource_id);
468
631
  return {
469
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
632
+ content: [
633
+ { type: "text", text: JSON.stringify(response, null, 2) },
634
+ ],
470
635
  };
471
636
  }
472
637
  case "get_cloud_resource_facets": {
473
638
  const args = cloudInventory.GetCloudResourceFacetsSchema.parse(request.params.arguments);
474
639
  const response = await cloudInventory.getCloudResourceFacets(client, args.provider);
475
640
  return {
476
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
641
+ content: [
642
+ { type: "text", text: JSON.stringify(response, null, 2) },
643
+ ],
477
644
  };
478
645
  }
479
646
  case "get_cloud_resource_facet_value": {
480
647
  const args = cloudInventory.GetCloudResourceFacetValuesSchema.parse(request.params.arguments);
481
648
  const response = await cloudInventory.getCloudResourceFacetValues(client, args.provider, args.facet_id);
482
649
  return {
483
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
650
+ content: [
651
+ { type: "text", text: JSON.stringify(response, null, 2) },
652
+ ],
484
653
  };
485
654
  }
486
655
  // Cloud Compliance tools
@@ -488,42 +657,54 @@ async function newServer() {
488
657
  const args = cloudCompliance.ListComplianceFrameworksSchema.parse(request.params.arguments);
489
658
  const response = await cloudCompliance.listComplianceFrameworks(client, args.datasource_ids, args.page, args.page_size);
490
659
  return {
491
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
660
+ content: [
661
+ { type: "text", text: JSON.stringify(response, null, 2) },
662
+ ],
492
663
  };
493
664
  }
494
665
  case "list_framework_requirements": {
495
666
  const args = cloudCompliance.ListFrameworkRequirementsSchema.parse(request.params.arguments);
496
667
  const response = await cloudCompliance.listFrameworkRequirements(client, args.framework_name, args.datasource_ids, args.page, args.page_size);
497
668
  return {
498
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
669
+ content: [
670
+ { type: "text", text: JSON.stringify(response, null, 2) },
671
+ ],
499
672
  };
500
673
  }
501
674
  case "list_requirement_controls": {
502
675
  const args = cloudCompliance.ListRequirementControlsSchema.parse(request.params.arguments);
503
676
  const response = await cloudCompliance.listRequirementControls(client, args.framework_name, args.requirement_id, args.datasource_ids, args.page, args.page_size);
504
677
  return {
505
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
678
+ content: [
679
+ { type: "text", text: JSON.stringify(response, null, 2) },
680
+ ],
506
681
  };
507
682
  }
508
683
  case "list_compliance_controls": {
509
684
  const args = cloudCompliance.ListComplianceControlsSchema.parse(request.params.arguments);
510
685
  const response = await cloudCompliance.listComplianceControls(client, args.status, args.providers, args.page, args.page_size);
511
686
  return {
512
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
687
+ content: [
688
+ { type: "text", text: JSON.stringify(response, null, 2) },
689
+ ],
513
690
  };
514
691
  }
515
692
  case "get_compliance_control": {
516
693
  const args = cloudCompliance.GetComplianceControlSchema.parse(request.params.arguments);
517
694
  const response = await cloudCompliance.getComplianceControl(client, args.control_name, args.datasource_ids);
518
695
  return {
519
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
696
+ content: [
697
+ { type: "text", text: JSON.stringify(response, null, 2) },
698
+ ],
520
699
  };
521
700
  }
522
701
  case "list_control_resources": {
523
702
  const args = cloudCompliance.ListControlResourcesSchema.parse(request.params.arguments);
524
703
  const response = await cloudCompliance.listControlResources(client, args.control_name, args.datasource_ids, args.page, args.page_size);
525
704
  return {
526
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
705
+ content: [
706
+ { type: "text", text: JSON.stringify(response, null, 2) },
707
+ ],
527
708
  };
528
709
  }
529
710
  // Image tools
@@ -531,27 +712,35 @@ async function newServer() {
531
712
  const args = images.ListImagesSchema.parse(request.params.arguments);
532
713
  const response = await images.listImages(client, args.limit, args.offset, args.sort, args.filters, args.q);
533
714
  return {
534
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
715
+ content: [
716
+ { type: "text", text: JSON.stringify(response, null, 2) },
717
+ ],
535
718
  };
536
719
  }
537
720
  case "list_image_vulnerabilities": {
538
721
  const args = images.ListImageVulnerabilitiesSchema.parse(request.params.arguments);
539
722
  const response = await images.listImageVulnerabilities(client, args.digest, args.severities, args.page, args.page_size);
540
723
  return {
541
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
724
+ content: [
725
+ { type: "text", text: JSON.stringify(response, null, 2) },
726
+ ],
542
727
  };
543
728
  }
544
729
  case "get_top_vulnerable_images": {
545
730
  const response = await images.getTopVulnerableImages(client);
546
731
  return {
547
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
732
+ content: [
733
+ { type: "text", text: JSON.stringify(response, null, 2) },
734
+ ],
548
735
  };
549
736
  }
550
737
  case "get_image_sbom": {
551
738
  const args = images.GetImageSBOMSchema.parse(request.params.arguments);
552
739
  const response = await images.getImageSBOM(client, args.digest);
553
740
  return {
554
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
741
+ content: [
742
+ { type: "text", text: JSON.stringify(response, null, 2) },
743
+ ],
555
744
  };
556
745
  }
557
746
  // Kubernetes Objects tools
@@ -559,14 +748,18 @@ async function newServer() {
559
748
  const args = kubeobject.GetKubernetesResourceDetailsSchema.parse(request.params.arguments);
560
749
  const response = await kubeobject.getKubernetesResourceDetails(client, args.cluster_id, args.resource_uid);
561
750
  return {
562
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
751
+ content: [
752
+ { type: "text", text: JSON.stringify(response, null, 2) },
753
+ ],
563
754
  };
564
755
  }
565
756
  case "list_k8s_resources": {
566
757
  const args = kubeobject.ListKubernetesResourcesSchema.parse(request.params.arguments);
567
758
  const response = await kubeobject.listKubernetesResources(client, args.kinds, args.namespace, args.cluster_id, args.page, args.page_size);
568
759
  return {
569
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
760
+ content: [
761
+ { type: "text", text: JSON.stringify(response, null, 2) },
762
+ ],
570
763
  };
571
764
  }
572
765
  // Kubernetes Resource Misconfigurations tools
@@ -574,20 +767,26 @@ async function newServer() {
574
767
  const args = misconfigs.ListKubernetesResourceMisconfigurationsSchema.parse(request.params.arguments);
575
768
  const response = await misconfigs.listKubernetesResourceMisconfigurations(client, args.resource_uid);
576
769
  return {
577
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
770
+ content: [
771
+ { type: "text", text: JSON.stringify(response, null, 2) },
772
+ ],
578
773
  };
579
774
  }
580
775
  case "get_k8s_resource_misconfig": {
581
776
  const args = misconfigs.GetKubernetesResourceMisconfigurationDetailsSchema.parse(request.params.arguments);
582
777
  const response = await misconfigs.getKubernetesResourceMisconfigurationDetails(client, args.cluster_id, args.misconfig_id);
583
778
  return {
584
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
779
+ content: [
780
+ { type: "text", text: JSON.stringify(response, null, 2) },
781
+ ],
585
782
  };
586
783
  }
587
784
  case "list_k8s_resource_misconfig_policies": {
588
785
  const response = await misconfigs.listKubernetesResourceMisconfigurationPolicies();
589
786
  return {
590
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
787
+ content: [
788
+ { type: "text", text: JSON.stringify(response, null, 2) },
789
+ ],
591
790
  };
592
791
  }
593
792
  // Runtime tools
@@ -595,21 +794,27 @@ async function newServer() {
595
794
  const args = runtime.GetContainersProcessTreesSchema.parse(request.params.arguments);
596
795
  const response = await runtime.getContainersProcessTrees(client, args.container_ids, args.processes_limit);
597
796
  return {
598
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
797
+ content: [
798
+ { type: "text", text: JSON.stringify(response, null, 2) },
799
+ ],
599
800
  };
600
801
  }
601
802
  case "get_containers_baselines": {
602
803
  const args = runtime.GetContainersBaselinesSchema.parse(request.params.arguments);
603
804
  const response = await runtime.getContainersBaselines(client, args.container_ids);
604
805
  return {
605
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
806
+ content: [
807
+ { type: "text", text: JSON.stringify(response, null, 2) },
808
+ ],
606
809
  };
607
810
  }
608
811
  case "get_container_llm_analysis": {
609
812
  const args = runtime.GetContainerLLMAnalysisSchema.parse(request.params.arguments);
610
813
  const response = await runtime.getContainerLLMAnalysis(client, args.container_id);
611
814
  return {
612
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
815
+ content: [
816
+ { type: "text", text: JSON.stringify(response, null, 2) },
817
+ ],
613
818
  };
614
819
  }
615
820
  // Runtime Network tools
@@ -617,21 +822,27 @@ async function newServer() {
617
822
  const args = runtimeNetwork.listHttpRequestsSchema.parse(request.params.arguments);
618
823
  const response = await runtimeNetwork.listHttpRequests(client, args);
619
824
  return {
620
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
825
+ content: [
826
+ { type: "text", text: JSON.stringify(response, null, 2) },
827
+ ],
621
828
  };
622
829
  }
623
830
  case "list_network_connections": {
624
831
  const args = runtimeNetwork.listNetworkConnectionsSchema.parse(request.params.arguments);
625
832
  const response = await runtimeNetwork.listNetworkConnections(client, args);
626
833
  return {
627
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
834
+ content: [
835
+ { type: "text", text: JSON.stringify(response, null, 2) },
836
+ ],
628
837
  };
629
838
  }
630
839
  case "list_network_connection_srcs": {
631
840
  const args = runtimeNetwork.listNetworkConnectionSourcesSchema.parse(request.params.arguments);
632
841
  const response = await runtimeNetwork.listNetworkConnectionSources(client, args);
633
842
  return {
634
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
843
+ content: [
844
+ { type: "text", text: JSON.stringify(response, null, 2) },
845
+ ],
635
846
  };
636
847
  }
637
848
  // Threat Vectors tools
@@ -639,7 +850,9 @@ async function newServer() {
639
850
  const args = threats.listThreatVectorsSchema.parse(request.params.arguments);
640
851
  const response = await threats.listThreatVectors(client, args.clustersIds, args.namespaces, args.resource_uid, args.page, args.page_size);
641
852
  return {
642
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
853
+ content: [
854
+ { type: "text", text: JSON.stringify(response, null, 2) },
855
+ ],
643
856
  };
644
857
  }
645
858
  // Findings tools
@@ -647,50 +860,72 @@ async function newServer() {
647
860
  const args = findings.listFindingsSchema.parse(request.params.arguments);
648
861
  const response = await findings.listFindings(client, args.limit, args.types, args.severities, args.source_types, args.source_kinds, args.source_names, args.source_namespaces, args.status, args.from_time, args.to_time);
649
862
  return {
650
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
863
+ content: [
864
+ { type: "text", text: JSON.stringify(response, null, 2) },
865
+ ],
651
866
  };
652
867
  }
653
868
  case "update_security_finding_status": {
654
869
  const args = findings.updateFindingStatusSchema.parse(request.params.arguments);
655
870
  await findings.updateFindingGroupStatus(client, args.id, args.status);
656
871
  return {
657
- content: [{ type: "text", text: JSON.stringify({ success: true, message: `Finding ${args.id} status updated to ${args.status}` }, null, 2) }],
872
+ content: [
873
+ {
874
+ type: "text",
875
+ text: JSON.stringify({
876
+ success: true,
877
+ message: `Finding ${args.id} status updated to ${args.status}`,
878
+ }, null, 2),
879
+ },
880
+ ],
658
881
  };
659
882
  }
660
883
  // CVE tools
661
884
  case "list_cve_vendors": {
662
885
  const response = await cves.listCveVendors();
663
886
  return {
664
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
887
+ content: [
888
+ { type: "text", text: JSON.stringify(response, null, 2) },
889
+ ],
665
890
  };
666
891
  }
667
892
  case "list_cve_products": {
668
- const args = z.object({
669
- vendor: z.string()
670
- }).parse(request.params.arguments);
893
+ const args = z
894
+ .object({
895
+ vendor: z.string(),
896
+ })
897
+ .parse(request.params.arguments);
671
898
  const response = await cves.listCveProducts(args.vendor);
672
899
  return {
673
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
900
+ content: [
901
+ { type: "text", text: JSON.stringify(response, null, 2) },
902
+ ],
674
903
  };
675
904
  }
676
905
  case "search_cves": {
677
906
  const args = cves.searchCvesSchema.parse(request.params.arguments);
678
907
  const response = await cves.searchCves(args.vendor, args.product);
679
908
  return {
680
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
909
+ content: [
910
+ { type: "text", text: JSON.stringify(response, null, 2) },
911
+ ],
681
912
  };
682
913
  }
683
914
  case "get_cve": {
684
915
  const args = cves.getCveSchema.parse(request.params.arguments);
685
916
  const response = await cves.getCve(args.cveId);
686
917
  return {
687
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
918
+ content: [
919
+ { type: "text", text: JSON.stringify(response, null, 2) },
920
+ ],
688
921
  };
689
922
  }
690
923
  case "get_latest_30_cves": {
691
924
  const response = await cves.getLatest30Cves();
692
925
  return {
693
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
926
+ content: [
927
+ { type: "text", text: JSON.stringify(response, null, 2) },
928
+ ],
694
929
  };
695
930
  }
696
931
  // Inbox tools
@@ -698,21 +933,27 @@ async function newServer() {
698
933
  const args = inbox.MarkInboxItemAsFalsePositiveSchema.parse(request.params.arguments);
699
934
  const response = await inbox.markInboxItemAsFalsePositive(client, args.inbox_item_id, args.value, args.reason);
700
935
  return {
701
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
936
+ content: [
937
+ { type: "text", text: JSON.stringify(response, null, 2) },
938
+ ],
702
939
  };
703
940
  }
704
941
  case "list_inbox_items": {
705
942
  const args = inbox.ListInboxItemsSchema.parse(request.params.arguments);
706
943
  const response = await inbox.listInboxItems(client, args.limit, args.offset, args.filters_query);
707
944
  return {
708
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
945
+ content: [
946
+ { type: "text", text: JSON.stringify(response, null, 2) },
947
+ ],
709
948
  };
710
949
  }
711
950
  case "get_inbox_item_details": {
712
951
  const args = inbox.GetInboxItemDetailsSchema.parse(request.params.arguments);
713
952
  const response = await inbox.getInboxItemDetails(client, args.inbox_item_id);
714
953
  return {
715
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
954
+ content: [
955
+ { type: "text", text: JSON.stringify(response, null, 2) },
956
+ ],
716
957
  };
717
958
  }
718
959
  // Workflows tools
@@ -720,42 +961,54 @@ async function newServer() {
720
961
  workflows.ListWorkflowsSchema.parse(request.params.arguments);
721
962
  const response = await workflows.listWorkflows(client);
722
963
  return {
723
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
964
+ content: [
965
+ { type: "text", text: JSON.stringify(response, null, 2) },
966
+ ],
724
967
  };
725
968
  }
726
969
  case "get_workflow": {
727
970
  const args = workflows.GetWorkflowSchema.parse(request.params.arguments);
728
971
  const response = await workflows.getWorkflow(client, args.workflow_id);
729
972
  return {
730
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
973
+ content: [
974
+ { type: "text", text: JSON.stringify(response, null, 2) },
975
+ ],
731
976
  };
732
977
  }
733
978
  case "list_workflow_runs": {
734
979
  const args = workflows.ListWorkflowRunsSchema.parse(request.params.arguments);
735
980
  const response = await workflows.listWorkflowRuns(client, args.workflow_id);
736
981
  return {
737
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
982
+ content: [
983
+ { type: "text", text: JSON.stringify(response, null, 2) },
984
+ ],
738
985
  };
739
986
  }
740
987
  case "get_workflow_run": {
741
988
  const args = workflows.GetWorkflowRunSchema.parse(request.params.arguments);
742
989
  const response = await workflows.getWorkflowRun(client, args.workflow_id, args.run_id);
743
990
  return {
744
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
991
+ content: [
992
+ { type: "text", text: JSON.stringify(response, null, 2) },
993
+ ],
745
994
  };
746
995
  }
747
996
  case "run_workflow": {
748
997
  const args = workflows.RunWorkflowSchema.parse(request.params.arguments);
749
998
  const response = await workflows.runWorkflow(client, args.workflow_id, args.async, args.args);
750
999
  return {
751
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1000
+ content: [
1001
+ { type: "text", text: JSON.stringify(response, null, 2) },
1002
+ ],
752
1003
  };
753
1004
  }
754
1005
  case "list_workflow_schedules": {
755
1006
  const args = workflows.ListWorkflowSchedulesSchema.parse(request.params.arguments);
756
1007
  const response = await workflows.listWorkflowSchedules(client, args.workflow_id);
757
1008
  return {
758
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1009
+ content: [
1010
+ { type: "text", text: JSON.stringify(response, null, 2) },
1011
+ ],
759
1012
  };
760
1013
  }
761
1014
  // Knowledge Base tools
@@ -763,42 +1016,163 @@ async function newServer() {
763
1016
  const args = knowledgeBase.SearchKnowledgeBaseSchema.parse(request.params.arguments);
764
1017
  const response = await knowledgeBase.searchKnowledgeBase(client, args.query, args.top_k, args.min_score, args.thread_id, args.collections, args.document_ids);
765
1018
  return {
766
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1019
+ content: [
1020
+ { type: "text", text: JSON.stringify(response, null, 2) },
1021
+ ],
767
1022
  };
768
1023
  }
769
1024
  case "list_knowledge_base_collections": {
770
1025
  const args = knowledgeBase.ListCollectionsSchema.parse(request.params.arguments);
771
1026
  const response = await knowledgeBase.listCollections(client, args.limit, args.offset);
772
1027
  return {
773
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1028
+ content: [
1029
+ { type: "text", text: JSON.stringify(response, null, 2) },
1030
+ ],
774
1031
  };
775
1032
  }
776
1033
  case "list_knowledge_base_documents": {
777
1034
  const args = knowledgeBase.ListDocumentsSchema.parse(request.params.arguments);
778
1035
  const response = await knowledgeBase.listDocuments(client, args.limit, args.offset, args.filters);
779
1036
  return {
780
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1037
+ content: [
1038
+ { type: "text", text: JSON.stringify(response, null, 2) },
1039
+ ],
781
1040
  };
782
1041
  }
783
1042
  case "query_knowledge_base_document": {
784
1043
  const args = knowledgeBase.StructuredQueryDocumentSchema.parse(request.params.arguments);
785
1044
  const response = await knowledgeBase.structuredQueryDocument(client, args.document_id, args.query);
786
1045
  return {
787
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
1046
+ content: [
1047
+ { type: "text", text: JSON.stringify(response, null, 2) },
1048
+ ],
1049
+ };
1050
+ }
1051
+ // RadQL tools
1052
+ case "radql_list_data_types": {
1053
+ const response = await radql.executeListDataTypes(client);
1054
+ return {
1055
+ content: [
1056
+ { type: "text", text: JSON.stringify(response, null, 2) },
1057
+ ],
1058
+ };
1059
+ }
1060
+ case "radql_get_type_metadata": {
1061
+ const args = radql.RadQLGetTypeMetadataSchema.parse(request.params.arguments);
1062
+ const response = await radql.executeGetTypeMetadata(client, args);
1063
+ return {
1064
+ content: [
1065
+ { type: "text", text: JSON.stringify(response, null, 2) },
1066
+ ],
1067
+ };
1068
+ }
1069
+ case "radql_list_filter_values": {
1070
+ const args = radql.RadQLListFilterValuesSchema.parse(request.params.arguments);
1071
+ const response = await radql.executeListFilterValues(client, args);
1072
+ return {
1073
+ content: [
1074
+ { type: "text", text: JSON.stringify(response, null, 2) },
1075
+ ],
1076
+ };
1077
+ }
1078
+ case "radql_query": {
1079
+ const args = radql.RadQLQuerySchema.parse(request.params.arguments);
1080
+ const response = await radql.executeRadQLQuery(client, args);
1081
+ return {
1082
+ content: [
1083
+ { type: "text", text: JSON.stringify(response, null, 2) },
1084
+ ],
1085
+ };
1086
+ }
1087
+ case "radql_query_builder": {
1088
+ const args = radql.RadQLQueryBuilderSchema.parse(request.params.arguments);
1089
+ const response = radql.buildRadQLQuery(args);
1090
+ return {
1091
+ content: [
1092
+ { type: "text", text: JSON.stringify(response, null, 2) },
1093
+ ],
1094
+ };
1095
+ }
1096
+ case "radql_batch_query": {
1097
+ const args = radql.RadQLBatchQuerySchema.parse(request.params.arguments);
1098
+ const response = await radql.executeBatchQueries(client, args);
1099
+ return {
1100
+ content: [
1101
+ { type: "text", text: JSON.stringify(response, null, 2) },
1102
+ ],
1103
+ };
1104
+ }
1105
+ // Dashboards tools
1106
+ case "list_widget_templates": {
1107
+ const args = dashboards.ListWidgetTemplatesSchema.parse(request.params.arguments);
1108
+ const response = await dashboards.listWidgetTemplates(client, args.limit, args.offset, args.visualization_type, args.category);
1109
+ return {
1110
+ content: [
1111
+ { type: "text", text: JSON.stringify(response, null, 2) },
1112
+ ],
1113
+ };
1114
+ }
1115
+ case "get_widget_template": {
1116
+ const args = dashboards.GetWidgetTemplateSchema.parse(request.params.arguments);
1117
+ const response = await dashboards.getWidgetTemplate(client, args.widget_template_id);
1118
+ return {
1119
+ content: [
1120
+ { type: "text", text: JSON.stringify(response, null, 2) },
1121
+ ],
1122
+ };
1123
+ }
1124
+ case "list_dashboard_templates": {
1125
+ const args = dashboards.ListDashboardTemplatesSchema.parse(request.params.arguments);
1126
+ const response = await dashboards.listDashboardTemplates(client, args.limit, args.offset, args.category);
1127
+ return {
1128
+ content: [
1129
+ { type: "text", text: JSON.stringify(response, null, 2) },
1130
+ ],
1131
+ };
1132
+ }
1133
+ case "get_dashboard_template": {
1134
+ const args = dashboards.GetDashboardTemplateSchema.parse(request.params.arguments);
1135
+ const response = await dashboards.getDashboardTemplate(client, args.dashboard_template_id);
1136
+ return {
1137
+ content: [
1138
+ { type: "text", text: JSON.stringify(response, null, 2) },
1139
+ ],
1140
+ };
1141
+ }
1142
+ case "list_dashboards": {
1143
+ const args = dashboards.ListDashboardsSchema.parse(request.params.arguments);
1144
+ const response = await dashboards.listDashboards(client, args.limit, args.offset);
1145
+ return {
1146
+ content: [
1147
+ { type: "text", text: JSON.stringify(response, null, 2) },
1148
+ ],
1149
+ };
1150
+ }
1151
+ case "get_dashboard": {
1152
+ const args = dashboards.GetDashboardSchema.parse(request.params.arguments);
1153
+ const response = await dashboards.getDashboard(client, args.dashboard_id);
1154
+ return {
1155
+ content: [
1156
+ { type: "text", text: JSON.stringify(response, null, 2) },
1157
+ ],
788
1158
  };
789
1159
  }
790
1160
  default:
791
1161
  throw new Error(`Unknown tool: ${toolName}`);
792
1162
  }
1163
+ const duration = Date.now() - startTime;
1164
+ logger.info({ tool: toolName, duration_ms: duration }, "tool_execution_completed");
793
1165
  }
794
1166
  catch (error) {
795
- console.error("Error calling tool:", error);
1167
+ const duration = Date.now() - startTime;
1168
+ const errorMessage = error instanceof Error ? error.message : String(error);
1169
+ logger.error({ tool: toolName, error: errorMessage, duration_ms: duration }, "tool_execution_failed");
796
1170
  return {
797
1171
  content: [
798
1172
  {
799
1173
  type: "text",
800
1174
  text: JSON.stringify({
801
- error: error instanceof Error ? error.message : String(error),
1175
+ error: errorMessage,
802
1176
  }),
803
1177
  },
804
1178
  ],
@@ -809,36 +1183,39 @@ async function newServer() {
809
1183
  }
810
1184
  async function main() {
811
1185
  try {
812
- const transportType = process.env.TRANSPORT_TYPE || 'stdio';
813
- if (!['stdio', 'sse', 'streamable'].includes(transportType)) {
1186
+ const transportType = process.env.TRANSPORT_TYPE || "stdio";
1187
+ if (!["stdio", "sse", "streamable"].includes(transportType)) {
814
1188
  throw new Error("Transport type must be either 'stdio', 'sse' or 'streamable'");
815
1189
  }
816
- console.error(`RAD Security MCP server version: ${VERSION}`);
817
- console.error(`Node version: ${process.version}`);
1190
+ // Log server startup
1191
+ logger.info({
1192
+ version: VERSION,
1193
+ transport: transportType,
1194
+ node_version: process.version,
1195
+ }, "server_starting");
818
1196
  // Log toolkit filters if set
819
1197
  const filters = parseToolkitFilters();
820
1198
  if (filters.include && filters.include.length > 0) {
821
- console.error(`Toolkit filter: INCLUDE ONLY [${filters.include.join(', ')}]`);
1199
+ logger.info({ mode: "include", toolkits: filters.include }, "toolkit_filter_configured");
822
1200
  }
823
1201
  else if (filters.exclude && filters.exclude.length > 0) {
824
- console.error(`Toolkit filter: EXCLUDE [${filters.exclude.join(', ')}]`);
1202
+ logger.info({ mode: "exclude", toolkits: filters.exclude }, "toolkit_filter_configured");
825
1203
  }
826
1204
  else {
827
- console.error(`Toolkit filter: ALL toolkits enabled`);
1205
+ logger.info("toolkit_all_enabled");
828
1206
  }
829
- console.error(`Starting MCP server with transport type: ${transportType}...`);
830
- if (transportType === 'stdio') {
1207
+ if (transportType === "stdio") {
831
1208
  const transport = new StdioServerTransport();
832
1209
  const server = await newServer();
833
1210
  await server.connect(transport);
834
- console.error(`RAD Security MCP server started.`);
1211
+ logger.info({ transport: "stdio" }, "server_ready");
835
1212
  }
836
- else if (transportType === 'sse') {
1213
+ else if (transportType === "sse") {
837
1214
  const app = express();
838
1215
  app.use(cors({
839
- origin: '*',
840
- methods: ['GET', 'POST', 'OPTIONS', 'HEAD'],
841
- allowedHeaders: ['Content-Type'],
1216
+ origin: "*",
1217
+ methods: ["GET", "POST", "OPTIONS", "HEAD"],
1218
+ allowedHeaders: ["Content-Type"],
842
1219
  }));
843
1220
  const server = await newServer();
844
1221
  let transport;
@@ -857,23 +1234,23 @@ async function main() {
857
1234
  });
858
1235
  const port = process.env.PORT || 3000;
859
1236
  app.listen(port, () => {
860
- console.error(`RAD Security MCP Server started on http://localhost:${port}/sse`);
1237
+ logger.info({ transport: "sse", url: `http://localhost:${port}/sse` }, "server_ready");
861
1238
  });
862
1239
  }
863
- else if (transportType === 'streamable') {
1240
+ else if (transportType === "streamable") {
864
1241
  const app = express();
865
1242
  app.use(express.json());
866
1243
  app.use(cors({
867
- origin: '*',
868
- methods: ['GET', 'POST', 'OPTIONS', 'HEAD'],
869
- allowedHeaders: ['Content-Type'],
1244
+ origin: "*",
1245
+ methods: ["GET", "POST", "OPTIONS", "HEAD"],
1246
+ allowedHeaders: ["Content-Type"],
870
1247
  }));
871
1248
  // Map to store transports by session ID
872
1249
  const transports = {};
873
1250
  // Handle POST requests for client-to-server communication
874
- app.post('/mcp', async (req, res) => {
1251
+ app.post("/mcp", async (req, res) => {
875
1252
  // Check for existing session ID
876
- const sessionId = req.headers['mcp-session-id'];
1253
+ const sessionId = req.headers["mcp-session-id"];
877
1254
  let transport;
878
1255
  if (sessionId && transports[sessionId]) {
879
1256
  // Reuse existing transport
@@ -886,7 +1263,7 @@ async function main() {
886
1263
  onsessioninitialized: (sessionId) => {
887
1264
  // Store the transport by session ID
888
1265
  transports[sessionId] = transport;
889
- }
1266
+ },
890
1267
  });
891
1268
  // Clean up transport when closed
892
1269
  transport.onclose = () => {
@@ -901,10 +1278,10 @@ async function main() {
901
1278
  else {
902
1279
  // Invalid request
903
1280
  res.status(400).json({
904
- jsonrpc: '2.0',
1281
+ jsonrpc: "2.0",
905
1282
  error: {
906
1283
  code: -32000,
907
- message: 'Bad Request: No valid session ID provided',
1284
+ message: "Bad Request: No valid session ID provided",
908
1285
  },
909
1286
  id: null,
910
1287
  });
@@ -915,32 +1292,33 @@ async function main() {
915
1292
  });
916
1293
  // Reusable handler for GET and DELETE requests
917
1294
  const handleSessionRequest = async (req, res) => {
918
- const sessionId = req.headers['mcp-session-id'];
1295
+ const sessionId = req.headers["mcp-session-id"];
919
1296
  if (!sessionId || !transports[sessionId]) {
920
- res.status(400).send('Invalid or missing session ID');
1297
+ res.status(400).send("Invalid or missing session ID");
921
1298
  return;
922
1299
  }
923
1300
  const transport = transports[sessionId];
924
1301
  await transport.handleRequest(req, res);
925
1302
  };
926
1303
  // Handle GET requests for server-to-client notifications via SSE
927
- app.get('/mcp', handleSessionRequest);
1304
+ app.get("/mcp", handleSessionRequest);
928
1305
  // Handle DELETE requests for session termination
929
- app.delete('/mcp', handleSessionRequest);
1306
+ app.delete("/mcp", handleSessionRequest);
930
1307
  const port = process.env.PORT || 3000;
931
1308
  app.listen(port, () => {
932
- console.error(`MCP Stateless Streamable HTTP Server listening on port ${port}`);
1309
+ logger.info({ transport: "streamable", url: `http://localhost:${port}/mcp` }, "server_ready");
933
1310
  });
934
1311
  }
935
- console.error(`RAD Security MCP server started.`);
936
1312
  }
937
1313
  catch (error) {
938
- console.error("Error starting server:", error);
1314
+ const errorMessage = error instanceof Error ? error.message : String(error);
1315
+ logger.fatal({ error: errorMessage }, "server_startup_failed");
939
1316
  process.exit(1);
940
1317
  }
941
1318
  }
942
1319
  main().catch((error) => {
943
- console.error("Unhandled error:", error);
1320
+ const errorMessage = error instanceof Error ? error.message : String(error);
1321
+ logger.fatal({ error: errorMessage }, "server_error_unhandled");
944
1322
  process.exit(1);
945
1323
  });
946
1324
  //# sourceMappingURL=index.js.map