Skip to main content
Glama

RAD Security

Official
by rad-security
index.ts38 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import cors from 'cors'; import { randomUUID } from "node:crypto"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js" import { zodToJsonSchema } from 'zod-to-json-schema'; import { z } from "zod"; import { CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import express from "express"; import { RadSecurityClient } from "./client.js"; import * as containers from "./operations/containers.js"; import * as audit from "./operations/audit.js"; import * as cloudInventory from "./operations/cloud-inventory.js"; import * as clusters from "./operations/clusters.js"; import * as identities from "./operations/identities.js"; import * as images from "./operations/images.js"; import * as kubeobject from "./operations/kubeobject.js"; import * as misconfigs from "./operations/misconfigs.js"; import * as runtime from "./operations/runtime.js"; import * as runtimeNetwork from "./operations/runtime_network.js"; import * as threats from "./operations/threats.js"; import * as findings from "./operations/findings.js"; import * as cves from "./operations/cves.js"; import * as inbox from "./operations/inbox.js"; import * as workflows from "./operations/workflows.js"; import { VERSION } from "./version.js"; // Toolkit type definitions type ToolkitType = | "containers" | "clusters" | "identities" | "audit" | "cloud_inventory" | "images" | "kubeobject" | "misconfigs" | "runtime" | "runtime_network" | "threats" | "findings" | "cves" | "inbox" | "workflows"; // Parse toolkit filters from environment variables function parseToolkitFilters(): { include?: ToolkitType[], exclude?: ToolkitType[] } { const includeEnv = process.env.INCLUDE_TOOLKITS; const excludeEnv = process.env.EXCLUDE_TOOLKITS; const result: { include?: ToolkitType[], exclude?: ToolkitType[] } = {}; if (includeEnv) { result.include = includeEnv.split(',').map(s => s.trim()) as ToolkitType[]; } if (excludeEnv) { result.exclude = excludeEnv.split(',').map(s => s.trim()) as ToolkitType[]; } return result; } // Check if a toolkit type should be enabled function isToolkitEnabled(toolkitType: ToolkitType, filters: { include?: ToolkitType[], exclude?: ToolkitType[] }): boolean { // If include list is specified, only those toolkits are enabled if (filters.include && filters.include.length > 0) { return filters.include.includes(toolkitType); } // If exclude list is specified, all except those are enabled if (filters.exclude && filters.exclude.length > 0) { return !filters.exclude.includes(toolkitType); } // By default, all toolkits are enabled return true; } async function newServer(): Promise<Server> { const client = RadSecurityClient.fromEnv(); const toolkitFilters = parseToolkitFilters(); const server = new Server( { name: "RAD Security MCP Server", version: VERSION, }, { capabilities: { prompts: {}, resources: {}, tools: {}, logging: {}, }, } ); server.setRequestHandler( ListToolsRequestSchema, async () => { const allTools = [ // Container tools ...(isToolkitEnabled("containers", toolkitFilters) ? [ { name: "list_containers", description: "List containers secured by RAD Security with optional filtering by image name, image digest, namespace, cluster_id, or free text search", inputSchema: zodToJsonSchema(containers.ListContainersSchema), }, { name: "get_container_details", description: "Get detailed information about a container secured by RAD Security", inputSchema: zodToJsonSchema(containers.GetContainerDetailsSchema), }, ] : []), // Cluster tools ...(isToolkitEnabled("clusters", toolkitFilters) ? [ { name: "list_clusters", description: "List Kubernetes clusters managed by RAD Security", inputSchema: zodToJsonSchema(clusters.ListClustersSchema), }, { name: "get_cluster_details", description: "Get detailed information about a specific Kubernetes cluster managed by RAD Security", inputSchema: zodToJsonSchema(clusters.GetClusterDetailsSchema), }, ] : []), // Identity tools ...(isToolkitEnabled("identities", toolkitFilters) ? [ { name: "list_identities", description: "Get list of identities for a specific Kubernetes cluster", inputSchema: zodToJsonSchema(identities.ListIdentitiesSchema), }, { name: "get_identity_details", description: "Get detailed information about a specific identity in a Kubernetes cluster", inputSchema: zodToJsonSchema(identities.GetIdentityDetailsSchema), }, ] : []), // Audit tools ...(isToolkitEnabled("audit", toolkitFilters) ? [ { name: "who_shelled_into_pod", description: "Get users who shelled into a pod with the given name and namespace around the given time", inputSchema: zodToJsonSchema(audit.WhoShelledIntoPodSchema), }, ] : []), // Cloud Inventory tools ...(isToolkitEnabled("cloud_inventory", toolkitFilters) ? [ { name: "list_cloud_resources", description: "List cloud resources for a specific provider with optional filtering", inputSchema: zodToJsonSchema(cloudInventory.ListCloudResourcesSchema), }, { name: "get_cloud_resource_details", description: "Get detailed information about a specific cloud resource", inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceDetailsSchema), }, { name: "get_cloud_resource_facets", description: "Get available facets for filtering cloud resources from a provider", inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetsSchema), }, { name: "get_cloud_resource_facet_value", description: "Get values for a specific facet from a cloud provider", inputSchema: zodToJsonSchema(cloudInventory.GetCloudResourceFacetValuesSchema), }, ] : []), // Image tools ...(isToolkitEnabled("images", toolkitFilters) ? [ { name: "list_images", description: "List container images with optional filtering by page, page size, sort, and search query", inputSchema: zodToJsonSchema(images.ListImagesSchema), }, { name: "list_image_vulnerabilities", description: "List vulnerabilities in a container image with optional filtering by severity", inputSchema: zodToJsonSchema(images.ListImageVulnerabilitiesSchema), }, { name: "get_top_vulnerable_images", description: "Get the most vulnerable images from your account", inputSchema: zodToJsonSchema(z.object({})), }, { name: "get_image_sbom", description: "Get the SBOM of a container image", inputSchema: zodToJsonSchema(images.GetImageSBOMSchema), }, ] : []), // Kubernetes Object tools ...(isToolkitEnabled("kubeobject", toolkitFilters) ? [ { name: "get_k8s_resource_details", description: "Get the latest manifest of a Kubernetes resource", inputSchema: zodToJsonSchema(kubeobject.GetKubernetesResourceDetailsSchema), }, { name: "list_k8s_resources", description: "List Kubernetes resources with optional filtering by namespace, resource types, and cluster", inputSchema: zodToJsonSchema(kubeobject.ListKubernetesResourcesSchema), }, ] : []), // Manifest Misconfigurations tools ...(isToolkitEnabled("misconfigs", toolkitFilters) ? [ { name: "list_k8s_resource_misconfigs", description: "Get manifest misconfigurations for a Kubernetes resource", inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationsSchema), }, { name: "get_k8s_resource_misconfig", description: "Get detailed information about a specific Kubernetes resource misconfiguration", inputSchema: zodToJsonSchema(misconfigs.GetKubernetesResourceMisconfigurationDetailsSchema), }, { name: "list_k8s_resource_misconfig_policies", description: "List available misconfiguration policies used by RAD Security to detect Kubernetes resource misconfigurations", inputSchema: zodToJsonSchema(misconfigs.ListKubernetesResourceMisconfigurationPoliciesSchema), }, ] : []), // Runtime tools ...(isToolkitEnabled("runtime", toolkitFilters) ? [ { name: "get_containers_process_trees", description: "Get process trees for multiple containers", inputSchema: zodToJsonSchema(runtime.GetContainersProcessTreesSchema), }, { name: "get_containers_baselines", description: "Get runtime baselines for multiple containers", inputSchema: zodToJsonSchema(runtime.GetContainersBaselinesSchema), }, { name: "get_container_llm_analysis", description: "Get LLM analysis of a container's process tree", inputSchema: zodToJsonSchema(runtime.GetContainerLLMAnalysisSchema), }, ] : []), // Runtime Network tools ...(isToolkitEnabled("runtime_network", toolkitFilters) ? [ { name: "list_http_requests", description: "List HTTP requests insights with optional filtering by method, path, source and destination workloads, and PII detection", inputSchema: zodToJsonSchema(runtimeNetwork.listHttpRequestsSchema), }, { name: "list_network_connections", description: "List network connections with optional filtering", inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionsSchema), }, { name: "list_network_connection_srcs", description: "List network connection sources with optional filtering by source and destination workloads", inputSchema: zodToJsonSchema(runtimeNetwork.listNetworkConnectionSourcesSchema), }, ] : []), // Threat Vectors tools ...(isToolkitEnabled("threats", toolkitFilters) ? [ { name: "list_threat_vectors", description: "List threat vectors", inputSchema: zodToJsonSchema(threats.listThreatVectorsSchema), }, ] : []), // Findings tools ...(isToolkitEnabled("findings", toolkitFilters) ? [ { name: "list_security_findings", description: "List security findings with optional filtering by types, severities, sources, and status", inputSchema: zodToJsonSchema(findings.listFindingsSchema), }, { name: "update_security_finding_status", description: "Update the status of a security finding", inputSchema: zodToJsonSchema(findings.updateFindingStatusSchema), }, ] : []), // CVE tools ...(isToolkitEnabled("cves", toolkitFilters) ? [ { name: "list_cve_vendors", description: "Get a list of all vendors in the CVE database. Source: cve-search.org", inputSchema: zodToJsonSchema(z.object({})), }, { name: "list_cve_products", description: "Get a list of all products associated with a vendor in the CVE database. Source: cve-search.org", inputSchema: zodToJsonSchema(z.object({ vendor: z.string().describe("Vendor name to list products for") })), }, { name: "search_cves", description: "Search CVEs by vendor and optionally product. Source: cve-search.org", inputSchema: zodToJsonSchema(cves.searchCvesSchema), }, { name: "get_cve", description: "Get details for a specific CVE ID. Source: cve-search.org", inputSchema: zodToJsonSchema(cves.getCveSchema), }, { name: "get_latest_30_cves", description: "Get the latest/newest 30 CVEs including CAPEC, CWE and CPE expansions. Source: cve-search.org", inputSchema: zodToJsonSchema(z.object({})), }, ] : []), // Inbox tools ...(isToolkitEnabled("inbox", toolkitFilters) ? [ { name: "mark_inbox_item_as_false_positive", description: "Mark an inbox item as a false positive with a reason", inputSchema: zodToJsonSchema(inbox.MarkInboxItemAsFalsePositiveSchema), }, { name: "list_inbox_items", description: "List inbox items with optional filtering by any field. Multiple filters can be combined eg. 'search:cve-2024-12345 and severity:high'", inputSchema: zodToJsonSchema(inbox.ListInboxItemsSchema), }, { name: "get_inbox_item_details", description: "Get detailed information about a specific inbox item", inputSchema: zodToJsonSchema(inbox.GetInboxItemDetailsSchema), }, ] : []), // Workflows tools ...(isToolkitEnabled("workflows", toolkitFilters) ? [ { name: "list_workflow_runs", description: "List workflow runs with optional filtering by workflow ID", inputSchema: zodToJsonSchema(workflows.ListWorkflowRunsSchema), }, { name: "get_workflow_run", description: "Get detailed information about a specific workflow run", inputSchema: zodToJsonSchema(workflows.GetWorkflowRunSchema), }, { name: "run_workflow", description: "Run a workflow", inputSchema: zodToJsonSchema(workflows.RunWorkflowSchema), }, { name: "list_workflow_schedules", description: "List workflow schedules with optional filtering by workflow ID", inputSchema: zodToJsonSchema(workflows.ListWorkflowSchedulesSchema), }, ] : []), ]; return { tools: allTools, }; } ); server.setRequestHandler( CallToolRequestSchema, async (request: CallToolRequest) => { try { if (!request.params.arguments) { throw new Error("Arguments are required"); } const toolName = request.params.name; switch (toolName) { // Container tools case "list_containers": { const args = containers.ListContainersSchema.parse(request.params.arguments); const response = await containers.listContainers(client, args.offset, args.limit, args.filters, args.q); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_container_details": { const args = containers.GetContainerDetailsSchema.parse(request.params.arguments); const response = await containers.getContainerDetails(client, args.container_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Cluster tools case "list_clusters": { const args = clusters.ListClustersSchema.parse(request.params.arguments); const response = await clusters.listClusters(client, args.page_size, args.page); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_cluster_details": { const args = clusters.GetClusterDetailsSchema.parse(request.params.arguments); const response = await clusters.getClusterDetails(client, args.cluster_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Identity tools case "list_identities": { const args = identities.ListIdentitiesSchema.parse(request.params.arguments); const response = await identities.listIdentities(client, args.identity_types, args.cluster_ids, args.page, args.page_size, args.q); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_identity_details": { const args = identities.GetIdentityDetailsSchema.parse(request.params.arguments); const response = await identities.getIdentityDetails(client, args.identity_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Audit tools case "who_shelled_into_pod": { const args = audit.WhoShelledIntoPodSchema.parse(request.params.arguments); const response = await audit.whoShelledIntoPod( client, args.name, args.namespace, args.cluster_id, args.from_time, args.to_time, args.limit, args.page ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Cloud Inventory tools case "list_cloud_resources": { const args = cloudInventory.ListCloudResourcesSchema.parse(request.params.arguments); const response = await cloudInventory.listCloudResources( client, args.provider, args.filters, args.offset, args.limit, args.q ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_cloud_resource_details": { const args = cloudInventory.GetCloudResourceDetailsSchema.parse(request.params.arguments); const response = await cloudInventory.getCloudResourceDetails( client, args.provider, args.resource_type, args.resource_id ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_cloud_resource_facets": { const args = cloudInventory.GetCloudResourceFacetsSchema.parse(request.params.arguments); const response = await cloudInventory.getCloudResourceFacets( client, args.provider ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_cloud_resource_facet_value": { const args = cloudInventory.GetCloudResourceFacetValuesSchema.parse(request.params.arguments); const response = await cloudInventory.getCloudResourceFacetValues( client, args.provider, args.facet_id ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Image tools case "list_images": { const args = images.ListImagesSchema.parse(request.params.arguments); const response = await images.listImages( client, args.page, args.page_size, args.sort, args.search ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_image_vulnerabilities": { const args = images.ListImageVulnerabilitiesSchema.parse(request.params.arguments); const response = await images.listImageVulnerabilities( client, args.digest, args.severities, args.page, args.page_size ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_top_vulnerable_images": { const response = await images.getTopVulnerableImages(client); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_image_sbom": { const args = images.GetImageSBOMSchema.parse(request.params.arguments); const response = await images.getImageSBOM(client, args.digest); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Kubernetes Objects tools case "get_k8s_resource_details": { const args = kubeobject.GetKubernetesResourceDetailsSchema.parse(request.params.arguments); const response = await kubeobject.getKubernetesResourceDetails( client, args.cluster_id, args.resource_uid ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_k8s_resources": { const args = kubeobject.ListKubernetesResourcesSchema.parse(request.params.arguments); const response = await kubeobject.listKubernetesResources( client, args.kinds, args.namespace, args.cluster_id, args.page, args.page_size ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Kubernetes Resource Misconfigurations tools case "list_k8s_resource_misconfigs": { const args = misconfigs.ListKubernetesResourceMisconfigurationsSchema.parse(request.params.arguments); const response = await misconfigs.listKubernetesResourceMisconfigurations( client, args.resource_uid ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_k8s_resource_misconfig": { const args = misconfigs.GetKubernetesResourceMisconfigurationDetailsSchema.parse(request.params.arguments); const response = await misconfigs.getKubernetesResourceMisconfigurationDetails( client, args.cluster_id, args.misconfig_id ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_k8s_resource_misconfig_policies": { const response = await misconfigs.listKubernetesResourceMisconfigurationPolicies(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Runtime tools case "get_containers_process_trees": { const args = runtime.GetContainersProcessTreesSchema.parse(request.params.arguments); const response = await runtime.getContainersProcessTrees( client, args.container_ids, args.processes_limit ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_containers_baselines": { const args = runtime.GetContainersBaselinesSchema.parse(request.params.arguments); const response = await runtime.getContainersBaselines( client, args.container_ids ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_container_llm_analysis": { const args = runtime.GetContainerLLMAnalysisSchema.parse(request.params.arguments); const response = await runtime.getContainerLLMAnalysis( client, args.container_id ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Runtime Network tools case "list_http_requests": { const args = runtimeNetwork.listHttpRequestsSchema.parse(request.params.arguments); const response = await runtimeNetwork.listHttpRequests(client, args); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_network_connections": { const args = runtimeNetwork.listNetworkConnectionsSchema.parse(request.params.arguments); const response = await runtimeNetwork.listNetworkConnections(client, args); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_network_connection_srcs": { const args = runtimeNetwork.listNetworkConnectionSourcesSchema.parse(request.params.arguments); const response = await runtimeNetwork.listNetworkConnectionSources(client, args); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Threat Vectors tools case "list_threat_vectors": { const args = threats.listThreatVectorsSchema.parse(request.params.arguments); const response = await threats.listThreatVectors(client, args.clustersIds, args.namespaces, args.resource_uid, args.page, args.page_size); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Findings tools case "list_security_findings": { const args = findings.listFindingsSchema.parse(request.params.arguments); 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 ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "update_security_finding_status": { const args = findings.updateFindingStatusSchema.parse(request.params.arguments); await findings.updateFindingGroupStatus(client, args.id, args.status); return { content: [{ type: "text", text: JSON.stringify({ success: true, message: `Finding ${args.id} status updated to ${args.status}` }, null, 2) }], }; } // CVE tools case "list_cve_vendors": { const response = await cves.listCveVendors(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_cve_products": { const args = z.object({ vendor: z.string() }).parse(request.params.arguments); const response = await cves.listCveProducts(args.vendor); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "search_cves": { const args = cves.searchCvesSchema.parse(request.params.arguments); const response = await cves.searchCves(args.vendor, args.product); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_cve": { const args = cves.getCveSchema.parse(request.params.arguments); const response = await cves.getCve(args.cveId); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_latest_30_cves": { const response = await cves.getLatest30Cves(); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Inbox tools case "mark_inbox_item_as_false_positive": { const args = inbox.MarkInboxItemAsFalsePositiveSchema.parse(request.params.arguments); const response = await inbox.markInboxItemAsFalsePositive( client, args.inbox_item_id, args.value, args.reason ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_inbox_items": { const args = inbox.ListInboxItemsSchema.parse(request.params.arguments); const response = await inbox.listInboxItems( client, args.limit, args.offset, args.filters_query, ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_inbox_item_details": { const args = inbox.GetInboxItemDetailsSchema.parse(request.params.arguments); const response = await inbox.getInboxItemDetails( client, args.inbox_item_id ); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } // Workflows tools case "list_workflow_runs": { const args = workflows.ListWorkflowRunsSchema.parse(request.params.arguments); const response = await workflows.listWorkflowRuns(client, args.workflow_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "get_workflow_run": { const args = workflows.GetWorkflowRunSchema.parse(request.params.arguments); const response = await workflows.getWorkflowRun(client, args.workflow_id, args.run_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "run_workflow": { const args = workflows.RunWorkflowSchema.parse(request.params.arguments); const response = await workflows.runWorkflow(client, args.workflow_id, args.async); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } case "list_workflow_schedules": { const args = workflows.ListWorkflowSchedulesSchema.parse(request.params.arguments); const response = await workflows.listWorkflowSchedules(client, args.workflow_id); return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }], }; } default: throw new Error(`Unknown tool: ${toolName}`); } } catch (error) { console.error("Error calling tool:", error); return { content: [ { type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), }), }, ], }; } } ); return server; } async function main() { try { const transportType = process.env.TRANSPORT_TYPE || 'stdio'; if (!['stdio', 'sse', 'streamable'].includes(transportType)) { throw new Error("Transport type must be either 'stdio', 'sse' or 'streamable'"); } console.error(`RAD Security MCP server version: ${VERSION}`); console.error(`Node version: ${process.version}`); // Log toolkit filters if set const filters = parseToolkitFilters(); if (filters.include && filters.include.length > 0) { console.error(`Toolkit filter: INCLUDE ONLY [${filters.include.join(', ')}]`); } else if (filters.exclude && filters.exclude.length > 0) { console.error(`Toolkit filter: EXCLUDE [${filters.exclude.join(', ')}]`); } else { console.error(`Toolkit filter: ALL toolkits enabled`); } console.error(`Starting MCP server with transport type: ${transportType}...`); if (transportType === 'stdio') { const transport = new StdioServerTransport(); const server = await newServer(); await server.connect(transport); console.error(`RAD Security MCP server started.`); } else if (transportType === 'sse') { const app = express(); app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS', 'HEAD'], allowedHeaders: ['Content-Type'], })); const server = await newServer(); let transport: SSEServerTransport; app.head("/sse", async (req, res) => { res.sendStatus(200); }); app.head("/messages", async (req, res) => { res.sendStatus(200); }); app.get("/sse", async (req, res) => { transport = new SSEServerTransport("/messages", res); await server.connect(transport); }); app.post("/messages", async (req, res) => { await transport.handlePostMessage(req, res); }); const port = process.env.PORT || 3000; app.listen(port, () => { console.error(`RAD Security MCP Server started on http://localhost:${port}/sse`); }); } else if (transportType === 'streamable') { const app = express(); app.use(express.json()); app.use(cors({ origin: '*', methods: ['GET', 'POST', 'OPTIONS', 'HEAD'], allowedHeaders: ['Content-Type'], })); // Map to store transports by session ID const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {}; // Handle POST requests for client-to-server communication app.post('/mcp', async (req, res) => { // Check for existing session ID const sessionId = req.headers['mcp-session-id'] as string | undefined; let transport: StreamableHTTPServerTransport; if (sessionId && transports[sessionId]) { // Reuse existing transport transport = transports[sessionId]; } else if (!sessionId && isInitializeRequest(req.body)) { // New initialization request transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sessionId) => { // Store the transport by session ID transports[sessionId] = transport; } }); // Clean up transport when closed transport.onclose = () => { if (transport.sessionId) { delete transports[transport.sessionId]; } }; const server = await newServer(); // Connect to the MCP server await server.connect(transport); } else { // Invalid request res.status(400).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: No valid session ID provided', }, id: null, }); return; } // Handle the request await transport.handleRequest(req, res, req.body); }); // Reusable handler for GET and DELETE requests const handleSessionRequest = async (req: express.Request, res: express.Response) => { const sessionId = req.headers['mcp-session-id'] as string | undefined; if (!sessionId || !transports[sessionId]) { res.status(400).send('Invalid or missing session ID'); return; } const transport = transports[sessionId]; await transport.handleRequest(req, res); }; // Handle GET requests for server-to-client notifications via SSE app.get('/mcp', handleSessionRequest); // Handle DELETE requests for session termination app.delete('/mcp', handleSessionRequest); const port = process.env.PORT || 3000; app.listen(port, () => { console.error(`MCP Stateless Streamable HTTP Server listening on port ${port}`); }); } console.error(`RAD Security MCP server started.`); } catch (error) { console.error("Error starting server:", error); process.exit(1); } } main().catch((error) => { console.error("Unhandled error:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rad-security/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server