Integrate Burp Suite with AI Clients using the Model Context Protocol (MCP).
For more information about the protocol visit: modelcontextprotocol.io
This fork extends the upstream PortSwigger/mcp-server extension with the ability to drive Burp's Scanner and target Scope from MCP, plus introspection tools so an MCP client can describe what an audit will do before launching it. Everything below is additive — all upstream tools and behaviour are preserved.
Registered inside the existing if (api.burpSuite().version().edition() == BurpSuiteEdition.PROFESSIONAL) block in Tools.kt::registerTools. Each Pro tool is absent from the tools/list reply on Community editions.
| Tool | Purpose |
|---|---|
start_crawl |
Start a Burp crawl with a list of seed URLs. Validates each URL has an http or https scheme and a non-blank host. Deduplicates approval prompts per unique (host, port) (case-insensitive on host). Aborts on first denial without registering the task. Returns a task_id. |
start_audit |
Start a Burp audit with a seed HTTP request. mode is ACTIVE (probe payloads sent) or PASSIVE (passive checks against the seed only — no probes). The audit is registered in the in-process registry before the seed addRequest call, so a failure during seeding can be cleaned up cleanly. Returns a task_id. |
add_request_to_audit |
Extend a running audit with another HTTP request without restarting it. Wraps Montoya's Audit.addRequest. The only mid-flight scan-extension capability the Montoya 2025.10 API exposes. |
get_scan_task_status |
Returns progress info for a crawl or audit: requestCount, errorCount, statusMessage (audits only — Montoya's Crawl.statusMessage() is documented as not implemented), insertionPointCount (audits), issuesFound (audits). Each Burp accessor is wrapped in runCatching{}.getOrNull() so a single accessor throwing "Currently unsupported." (which Burp does on some accessors mid-flight, and on audit.issues() even after completion) still returns partial status instead of failing the whole tool call. |
list_scan_tasks |
Paginated list of all in-process tracked scan tasks (task_id, kind, requestCount, errorCount). |
cancel_scan_task |
Cancels a task via ScanTask.delete() and removes it from the registry. If delete() throws, the task is left in the registry so the caller can retry, and the actual error is surfaced to the response (early implementations silently lied about success). |
| Tool | Purpose |
|---|---|
describe_audit_modes |
Pure-documentation tool, no Burp API call. Returns a structured JSON describing what each start_audit mode actually does, the underlying BuiltInAuditConfiguration enum value, what is not configurable from the Montoya API (per-check filtering, named scan configs, severity pre-filtering), the actual severity enum values (HIGH/MEDIUM/LOW/INFORMATION/FALSE_POSITIVE — there is no CRITICAL), and a seeAlso cross-reference. |
get_scanner_configuration |
Reads Burp's exportProjectOptionsAsJson(), slices out the scanner-relevant top-level keys, and returns them as JSON capped at 5000 chars. If the expected scanner subtree is absent, falls back to returning the full JSON with an explanatory note. |
| Tool | Purpose |
|---|---|
is_in_scope |
Wraps api.scope().isInScope(url). Returns "true" or "false". |
include_in_scope |
Wraps api.scope().includeInScope(url). Gated by the existing Enable tools that can edit your config setting in the MCP tab — no new toggle introduced. Returns the standard tooling-disabled message when off. |
exclude_from_scope |
Wraps api.scope().excludeFromScope(url). Same gate as include_in_scope. |
get_scanner_issues gained two optional parameters and per-item truncation:
host: String?— case-insensitive hostname filter.siteMap().issues()is project-cumulative across every audit ever run, which can return tens of MB on a real project; this filter restricts results to a single target.minSeverity: String?— one ofHIGH,MEDIUM,LOW,INFORMATION,FALSE_POSITIVE. Returns only issues at or above the threshold. Case-insensitive. Invalid values produce a clear error listing the valid options.- Each returned item is now truncated to 5000 chars to bound the per-item size, matching the existing
get_proxy_http_history*tools.
Both filters compose. The original no-filter behaviour is preserved by omitting them.
tools/ScanTaskRegistry.kt(new file) — singleton in-process registry holding scan tasks by UUID. Implemented as a sealed class soadd_request_to_auditandget_scan_task_statuspattern-match (when (val r = registry.get(id)) { is RegisteredScanTask.Crawl -> ...; is RegisteredScanTask.Audit -> ... }) instead of using unsafeascasts. Backed byConcurrentHashMapfor parallel SSE worker threads. Concurrency-tested with 200 parallel coroutine registrations.schema/serialization.kt— addedScanTaskStatusandScanTaskSummaryfor the status/list response shapes.ExtensionBase.kt— the existingregisterUnloadingHandlernow callsScanTaskRegistry.list().forEach { runCatching { it.task.delete() } }followed byScanTaskRegistry.clear()before shutting down the SSE server, so disabling or unloading the extension cancels any in-flight Burp scans rather than orphaning them.
| Issue | Fix | |
|---|---|---|
| 1 | start_crawl accepted file://, gopher:// etc. and asked the user to approve :80 with an empty host |
Validate scheme is http or https and host is non-blank before the approval check. |
| 2 | start_audit could leak the audit to Burp if addRequest threw, with no task_id returned to cancel it |
Register the audit immediately, then attempt addRequest in try/catch that calls audit.delete() and removes the registry entry on failure. |
| 3 | cancel_scan_task reported "Cancelled" even when Burp.delete() threw |
runCatching{}.fold(...) — keep the registry entry on failure, surface the underlying exception to the caller. |
| 4 | start_crawl dedupe key was case-sensitive on host (Example.com vs example.com → two prompts for what Burp considers one host) |
Lowercase the host before keying the dedupe map. |
| 5 | get_scan_task_status crashed mid-audit because Burp throws "Currently unsupported." on some accessors before completion |
Wrap each accessor in runCatching{}.getOrNull(); nullable fields are simply omitted from the JSON when unavailable. |
| 6 | get_scanner_issues could return tens of MB on a real project |
Per-item 5000-char truncation, plus the host and minSeverity filters above. |
| 7 | Test fixture had a dead getString("autoApproveTargets") stub — the actual McpConfig property is _autoApproveTargets (with leading underscore), so any test exercising the auto-approve list would have failed |
Corrected the stub key. |
| 8 | The persistedObject test mock was init-block-local, preventing tests from re-stubbing individual keys (e.g. flipping requireHttpRequestApproval to test denial paths) |
Promoted to a class field. |
JUnit 5 + MockK + Kotlin coroutines, integration-style (real KtorServerManager + TestSseMcpClient.callTool over SSE).
ScanTaskRegistryTest(new) — register/get/remove/list/clear plus a 200-coroutine concurrency test.ToolsKtTest's existingScannerToolsTestsandScopeToolsTests(new nested classes) cover happy paths, denial paths (with theHttpRequestSecurity.approvalHandlerswapped at test time), case-insensitive dedupe, defensive accessor wrapping, host/severity filters, and theconfigEditingToolinggate for scope writes.- The existing edition-gating test (
edition specific tools should only register in professional edition) was extended to assert all six new scanner tools and both introspection tools are absent on Community and present on Professional, while the three scope tools are available on both. - Build:
./gradlew test— 101 tests, all green../gradlew embedProxyJarproducesbuild/libs/burp-mcp-all.jaras before.
- Connect Burp Suite to AI clients through MCP
- Automatic installation for Claude Desktop
- Comes with packaged Stdio MCP proxy server
- Install the extension in Burp Suite
- Configure your Burp MCP server in the extension settings
- Configure your MCP client to use the Burp SSE MCP server or stdio proxy
- Interact with Burp through your client!
Ensure that the following prerequisites are met before building and installing the extension:
- Java: Java must be installed and available in your system's PATH. You can verify this by running
java --versionin your terminal. - jar Command: The
jarcommand must be executable and available in your system's PATH. You can verify this by runningjar --versionin your terminal. This is required for building and installing the extension.
-
Clone the Repository: Obtain the source code for the MCP Server Extension.
git clone https://github.com/PortSwigger/mcp-server.git -
Navigate to the Project Directory: Move into the project's root directory.
cd mcp-server -
Build the JAR File: Use Gradle to build the extension.
./gradlew embedProxyJarThis command compiles the source code and packages it into a JAR file located in
build/libs/burp-mcp-all.jar.
- Open Burp Suite: Launch your Burp Suite application.
- Access the Extensions Tab: Navigate to the
Extensionstab. - Add the Extension:
- Click on
Add. - Set
Extension TypetoJava. - Click
Select file ...and choose the JAR file built in the previous step. - Click
Nextto load the extension.
- Click on
Upon successful loading, the MCP Server Extension will be active within Burp Suite.
Configuration for the extension is done through the Burp Suite UI in the MCP tab.
- Toggle the MCP Server: The
Enabledcheckbox controls whether the MCP server is active. - Enable config editing: The
Enable tools that can edit your configcheckbox allows the MCP server to expose tools which can edit Burp configuration files. - Advanced options: You can configure the port and host for the MCP server. By default, it listens on
http://127.0.0.1:9876.
To fully utilize the MCP Server Extension with Claude, you need to configure your Claude client settings appropriately. The extension has an installer which will automatically configure the client settings for you.
-
Currently, Claude Desktop only support STDIO MCP Servers for the service it needs. This approach isn't ideal for desktop apps like Burp, so instead, Claude will start a proxy server that points to the Burp instance,
which hosts a web server at a known port (localhost:9876). -
Configure Claude to use the Burp MCP server
You can do this in one of two ways:-
Option 1: Run the installer from the extension This will add the Burp MCP server to the Claude Desktop config.
-
Option 2: Manually edit the config file
Open the file located at~/Library/Application Support/Claude/claude_desktop_config.json, and replace or update it with the following:{ "mcpServers": { "burp": { "command": "<path to Java executable packaged with Burp>", "args": [ "-jar", "/path/to/mcp/proxy/jar/mcp-proxy-all.jar", "--sse-url", "<your Burp MCP server URL configured in the extension>" ] } } }
-
-
Restart Claude Desktop - assuming Burp is running with the extension loaded.
If you want to install the MCP server manually you can either use the extension's SSE server directly or the packaged Stdio proxy server.
In order to use the SSE server directly you can just provide the url for the server in your client's configuration. Depending
on your client and your configuration in the extension this may be with or without the /sse path.
http://127.0.0.1:9876
or
http://127.0.0.1:9876/sse
The source code for the proxy server can be found here: MCP Proxy Server
In order to support MCP Clients which only support Stdio MCP Servers, the extension comes packaged with a proxy server for passing requests to the SSE MCP server extension.
If you want to use the Stdio proxy server you can use the extension's installer option to extract the proxy server jar. Once you have the jar you can add the following command and args to your client configuration:
/path/to/packaged/burp/java -jar /path/to/proxy/jar/mcp-proxy-all.jar --sse-url http://127.0.0.1:9876
Tools are defined in src/main/kotlin/net/portswigger/mcp/tools/Tools.kt. To define new tools, create a new serializable
data class with the required parameters which will come from the LLM.
The tool name is auto-derived from its parameters data class. A description is also needed for the LLM. You can return a string (or richer PromptMessageContents) to provide data back to the LLM.
Extend the Paginated interface to add auto-pagination support.