Skip to main content
Glama

Burpsuite MCP Server

index.ts19.9 kB
#!/usr/bin/env node /** * Burpsuite MCP Server * * This server provides an interface for interacting with Burpsuite Professional's * scanning and proxy functionality through the Model Context Protocol. * * It implements mock functionality that can later be connected to the * Burpsuite REST API for real-world usage. */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // Types for Burpsuite data structures /** * Represents a vulnerability issue found during scanning */ interface Issue { id: string; name: string; severity: 'high' | 'medium' | 'low' | 'info'; confidence: 'certain' | 'firm' | 'tentative'; host: string; path: string; description: string; remediation: string; request?: string; response?: string; } /** * Represents a scan job */ interface Scan { id: string; target: string; status: 'queued' | 'running' | 'completed' | 'failed'; startTime: string; endTime?: string; progress?: number; issues: Issue[]; } /** * Represents an HTTP request/response pair in the proxy history */ interface ProxyHistoryItem { id: string; host: string; method: string; url: string; statusCode: number; request: string; response: string; time: string; size: number; mimeType: string; } /** * Represents a site map entry */ interface SiteMapItem { id: string; url: string; method: string; statusCode: number; mimeType: string; size: number; parameters: boolean; } // Mock data storage const mockScans: { [id: string]: Scan } = {}; const mockProxyHistory: ProxyHistoryItem[] = [ { id: "1", host: "example.com", method: "GET", url: "https://example.com/", statusCode: 200, request: "GET / HTTP/1.1\nHost: example.com\nUser-Agent: Mozilla/5.0\nAccept: */*\n\n", response: "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: 1256\n\n<!DOCTYPE html><html><head><title>Example Domain</title></head><body><h1>Example Domain</h1><p>This domain is for use in illustrative examples in documents.</p></body></html>", time: new Date().toISOString(), size: 1256, mimeType: "text/html" }, { id: "2", host: "example.com", method: "GET", url: "https://example.com/assets/style.css", statusCode: 200, request: "GET /assets/style.css HTTP/1.1\nHost: example.com\nUser-Agent: Mozilla/5.0\nAccept: text/css\n\n", response: "HTTP/1.1 200 OK\nContent-Type: text/css\nContent-Length: 128\n\nbody { font-family: sans-serif; } h1 { color: #333; }", time: new Date().toISOString(), size: 128, mimeType: "text/css" } ]; const mockSiteMap: SiteMapItem[] = [ { id: "1", url: "https://example.com/", method: "GET", statusCode: 200, mimeType: "text/html", size: 1256, parameters: false }, { id: "2", url: "https://example.com/assets/style.css", method: "GET", statusCode: 200, mimeType: "text/css", size: 128, parameters: false }, { id: "3", url: "https://example.com/login", method: "GET", statusCode: 200, mimeType: "text/html", size: 2048, parameters: false }, { id: "4", url: "https://example.com/api/user", method: "POST", statusCode: 200, mimeType: "application/json", size: 512, parameters: true } ]; // Common vulnerability types for mock data const commonVulnerabilities = [ { name: "SQL Injection", description: "SQL injection vulnerability detected in parameter. The application appears to be vulnerable to SQL injection attacks, which could allow an attacker to manipulate database queries.", remediation: "Use parameterized queries or prepared statements instead of building SQL queries through string concatenation. Apply input validation and use an ORM if possible." }, { name: "Cross-Site Scripting (XSS)", description: "Cross-site scripting vulnerability detected. The application reflects user input without proper encoding, which could allow attackers to inject malicious scripts.", remediation: "Implement proper output encoding for all user-controlled data. Use Content-Security-Policy headers and consider using frameworks that automatically escape output." }, { name: "Insecure Direct Object Reference", description: "Insecure direct object reference vulnerability detected. The application exposes references to internal implementation objects, allowing attackers to manipulate these references to access unauthorized data.", remediation: "Implement proper access controls and use indirect reference maps. Validate that the user is authorized to access the requested object." }, { name: "Information Disclosure", description: "Information disclosure vulnerability detected. The application reveals sensitive information such as server versions, file paths, or database details in responses.", remediation: "Configure proper error handling to avoid leaking sensitive information. Remove unnecessary headers and implement security headers like X-Content-Type-Options." } ]; // Helper function to generate mock issues for a scan function generateMockIssues(host: string, count: number): Issue[] { const issues: Issue[] = []; const paths = ["/login", "/api/user", "/search", "/profile", "/admin", "/settings"]; const severities: Array<'high' | 'medium' | 'low' | 'info'> = ['high', 'medium', 'low', 'info']; const confidences: Array<'certain' | 'firm' | 'tentative'> = ['certain', 'firm', 'tentative']; for (let i = 0; i < count; i++) { const vulnType = commonVulnerabilities[Math.floor(Math.random() * commonVulnerabilities.length)]; const path = paths[Math.floor(Math.random() * paths.length)]; const severity = severities[Math.floor(Math.random() * severities.length)]; const confidence = confidences[Math.floor(Math.random() * confidences.length)]; issues.push({ id: `issue-${i + 1}`, name: vulnType.name, severity, confidence, host, path, description: vulnType.description, remediation: vulnType.remediation, request: `GET ${path} HTTP/1.1\nHost: ${host}\nUser-Agent: Mozilla/5.0\n\n`, response: `HTTP/1.1 200 OK\nContent-Type: text/html\n\n<html><body>Example response</body></html>` }); } return issues; } /** * Create an MCP server for Burpsuite functionality */ const server = new Server( { name: "burpsuite-server", version: "0.1.0", }, { capabilities: { resources: {}, tools: {}, }, } ); /** * Handler for listing available resources. * Exposes scan results and proxy history as resources. */ server.setRequestHandler(ListResourcesRequestSchema, async () => { const resources = []; // Add scan resources for (const [id, scan] of Object.entries(mockScans)) { resources.push({ uri: `burpsuite://scan/${id}`, mimeType: "application/json", name: `Scan of ${scan.target}`, description: `Vulnerability scan of ${scan.target} (${scan.status})` }); } // Add a proxy history resource resources.push({ uri: `burpsuite://proxy/history`, mimeType: "application/json", name: "Proxy History", description: "HTTP/HTTPS traffic captured by Burp Proxy" }); // Add a site map resource resources.push({ uri: `burpsuite://sitemap`, mimeType: "application/json", name: "Site Map", description: "Structure of discovered websites" }); return { resources }; }); /** * Handler for resource templates. * Defines templates for accessing specific scan results and proxy history items. */ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => { return { resourceTemplates: [ { uriTemplate: "burpsuite://scan/{scanId}", name: "Scan Results", mimeType: "application/json", description: "Results of a specific vulnerability scan" }, { uriTemplate: "burpsuite://scan/{scanId}/issue/{issueId}", name: "Issue Details", mimeType: "application/json", description: "Details of a specific vulnerability issue" }, { uriTemplate: "burpsuite://proxy/history/{itemId}", name: "Proxy History Item", mimeType: "application/json", description: "Details of a specific HTTP/HTTPS request/response pair" } ] }; }); /** * Handler for reading resources. * Retrieves scan results, issue details, or proxy history based on the URI. */ server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri; // Handle scan results if (uri.startsWith("burpsuite://scan/")) { const parts = uri.replace("burpsuite://scan/", "").split("/"); const scanId = parts[0]; if (!mockScans[scanId]) { throw new McpError(ErrorCode.InvalidRequest, `Scan ${scanId} not found`); } // Handle specific issue if (parts.length > 1 && parts[1] === "issue") { const issueId = parts[2]; const issue = mockScans[scanId].issues.find(i => i.id === issueId); if (!issue) { throw new McpError(ErrorCode.InvalidRequest, `Issue ${issueId} not found in scan ${scanId}`); } return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(issue, null, 2) }] }; } // Return full scan return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(mockScans[scanId], null, 2) }] }; } // Handle proxy history if (uri === "burpsuite://proxy/history") { return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(mockProxyHistory, null, 2) }] }; } // Handle specific proxy history item if (uri.startsWith("burpsuite://proxy/history/")) { const itemId = uri.replace("burpsuite://proxy/history/", ""); const item = mockProxyHistory.find(i => i.id === itemId); if (!item) { throw new McpError(ErrorCode.InvalidRequest, `Proxy history item ${itemId} not found`); } return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(item, null, 2) }] }; } // Handle site map if (uri === "burpsuite://sitemap") { return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(mockSiteMap, null, 2) }] }; } throw new McpError(ErrorCode.InvalidRequest, `Resource not found: ${uri}`); }); /** * Handler for listing available tools. * Exposes tools for scanning, retrieving scan status, and accessing proxy history. */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "start_scan", description: "Start a new vulnerability scan on a target URL", inputSchema: { type: "object", properties: { target: { type: "string", description: "Target URL to scan (e.g., https://example.com)" }, scan_type: { type: "string", enum: ["passive", "active", "full"], description: "Type of scan to perform" } }, required: ["target"] } }, { name: "get_scan_status", description: "Check the status of a running scan", inputSchema: { type: "object", properties: { scan_id: { type: "string", description: "ID of the scan to check" } }, required: ["scan_id"] } }, { name: "get_scan_issues", description: "Get vulnerability issues found in a scan", inputSchema: { type: "object", properties: { scan_id: { type: "string", description: "ID of the scan" }, severity: { type: "string", enum: ["high", "medium", "low", "info", "all"], description: "Filter issues by severity" } }, required: ["scan_id"] } }, { name: "get_proxy_history", description: "Get HTTP/HTTPS traffic captured by Burp Proxy", inputSchema: { type: "object", properties: { host: { type: "string", description: "Filter by host (optional)" }, method: { type: "string", description: "Filter by HTTP method (optional)" }, status_code: { type: "number", description: "Filter by HTTP status code (optional)" }, limit: { type: "number", description: "Maximum number of items to return (default: 10)" } } } }, { name: "get_site_map", description: "Get the site structure discovered during scanning and browsing", inputSchema: { type: "object", properties: { host: { type: "string", description: "Filter by host (optional)" }, with_parameters: { type: "boolean", description: "Only show URLs with parameters (optional)" }, limit: { type: "number", description: "Maximum number of items to return (default: 20)" } } } } ] }; }); /** * Handler for tool calls. * Implements the functionality for each tool. */ server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case "start_scan": { const target = String(request.params.arguments?.target); const scanType = String(request.params.arguments?.scan_type || "passive"); if (!target) { throw new McpError(ErrorCode.InvalidParams, "Target URL is required"); } // Create a new scan const scanId = `scan-${Date.now()}`; const scan: Scan = { id: scanId, target, status: "running", startTime: new Date().toISOString(), progress: 0, issues: [] }; mockScans[scanId] = scan; // Simulate scan completion after a delay (in a real implementation, this would be async) setTimeout(() => { const issueCount = scanType === "passive" ? 3 : scanType === "active" ? 8 : 15; mockScans[scanId].issues = generateMockIssues(new URL(target).hostname, issueCount); mockScans[scanId].status = "completed"; mockScans[scanId].endTime = new Date().toISOString(); mockScans[scanId].progress = 100; }, 5000); return { content: [{ type: "text", text: JSON.stringify({ scan_id: scanId, message: `Started ${scanType} scan on ${target}`, status: "running" }, null, 2) }] }; } case "get_scan_status": { const scanId = String(request.params.arguments?.scan_id); if (!scanId || !mockScans[scanId]) { throw new McpError(ErrorCode.InvalidRequest, `Scan ${scanId} not found`); } const scan = mockScans[scanId]; return { content: [{ type: "text", text: JSON.stringify({ scan_id: scanId, target: scan.target, status: scan.status, progress: scan.progress, start_time: scan.startTime, end_time: scan.endTime, issue_count: scan.issues.length }, null, 2) }] }; } case "get_scan_issues": { const scanId = String(request.params.arguments?.scan_id); const severity = String(request.params.arguments?.severity || "all"); if (!scanId || !mockScans[scanId]) { throw new McpError(ErrorCode.InvalidRequest, `Scan ${scanId} not found`); } const scan = mockScans[scanId]; let issues = scan.issues; // Filter by severity if specified if (severity !== "all") { issues = issues.filter(issue => issue.severity === severity); } return { content: [{ type: "text", text: JSON.stringify({ scan_id: scanId, target: scan.target, issue_count: issues.length, issues: issues.map(issue => ({ id: issue.id, name: issue.name, severity: issue.severity, confidence: issue.confidence, host: issue.host, path: issue.path })) }, null, 2) }] }; } case "get_proxy_history": { const host = request.params.arguments?.host as string | undefined; const method = request.params.arguments?.method as string | undefined; const statusCode = request.params.arguments?.status_code as number | undefined; const limit = Number(request.params.arguments?.limit || 10); let history = [...mockProxyHistory]; // Apply filters if (host) { history = history.filter(item => item.host.includes(host)); } if (method) { history = history.filter(item => item.method === method.toUpperCase()); } if (statusCode) { history = history.filter(item => item.statusCode === statusCode); } // Apply limit history = history.slice(0, limit); return { content: [{ type: "text", text: JSON.stringify({ total_items: history.length, items: history.map(item => ({ id: item.id, host: item.host, method: item.method, url: item.url, status_code: item.statusCode, time: item.time, size: item.size, mime_type: item.mimeType })) }, null, 2) }] }; } case "get_site_map": { const host = request.params.arguments?.host as string | undefined; const withParameters = request.params.arguments?.with_parameters as boolean | undefined; const limit = Number(request.params.arguments?.limit || 20); let siteMap = [...mockSiteMap]; // Apply filters if (host) { siteMap = siteMap.filter(item => new URL(item.url).hostname.includes(host)); } if (withParameters) { siteMap = siteMap.filter(item => item.parameters); } // Apply limit siteMap = siteMap.slice(0, limit); return { content: [{ type: "text", text: JSON.stringify({ total_items: siteMap.length, items: siteMap }, null, 2) }] }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); /** * Start the server using stdio transport. */ async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Burpsuite MCP server running on stdio"); // Handle errors server.onerror = (error) => { console.error("[MCP Error]", error); }; // Handle process termination process.on("SIGINT", async () => { await server.close(); process.exit(0); }); } main().catch((error) => { console.error("Server 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/Cyreslab-AI/burpsuite-mcp-server'

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