Adding MCP Tools
MCP tools follow a descriptor + handler pattern. Each tool has metadata in the catalog and an implementation in the handler.
Steps
1. Add Tool Descriptor in McpToolCatalog
Define the tool's metadata:
McpToolDescriptor(
id = "my_tool",
title = "My Tool Title",
description = "What this tool does.",
category = "MyCategory",
defaultEnabled = true, // whether it's enabled by default
proOnly = false, // true if it requires Burp Pro
unsafeOnly = false, // true if it modifies state or sends traffic
secTier = SecTier.AUTO, // explicit approval tier for model-emitted calls
nativeTool = false // see "Store Build vs Full Build" below
)secTier has no default: every new tool must make an explicit trust decision. Use AUTO only for read-only tools with bounded output, CONFIRM when one approval may be remembered for the chat session, and CONFIRM_EACH when every invocation needs a fresh decision. This confirmation applies to tool calls emitted by the extension's own model; it is separate from the MCP server's authentication, enablement, unsafe, edition, and scope gates.
Store Build vs Full Build
The nativeTool flag decides whether a tool ships in the BApp Store build. The catalog filters on it:
nativeTool = true— the tool is an extension-native AI tool and is registered in both builds. The BApp Store build (./gradlew shadowJar -PstoreBuild=true) exposes exactly these 8 tools:status,issue_create,ai_analyze,ai_passive_scan,ai_findings_recent,redact_preview,ai_audit_query,ai_backends_list.nativeTool = false(default) — the tool is a generic Montoya-API wrapper (proxy history, repeater, scanner, scope, site map, intruder, collaborator, utilities, …). It ships only in the full build (./gradlew shadowJar, the default GitHub-release artifact), which registers all 59 MCP tools.
BuildFlags.STORE_BUILD is a generated compile-time constant set from the -PstoreBuild Gradle property, so the same source compiles into either artifact. The store build intentionally omits the generic tools because PortSwigger's official Burp MCP Server already provides those.
2. Define the Input Model and Schema
Add an @Serializable input data class in McpToolModels.kt (or the owning native-tool file). McpToolExecutor.inputSchema() maps the tool ID to that class, and asInputSchema() derives the MCP schema from its Kotlin properties.
The current reflection helper emits property types and a required list; it does not emit descriptions or Kotlin default values. It marks every non-null property as required in the advertised schema, even when Kotlin deserialization has a default. Runtime decoding still accepts an omitted defaulted property. Keep the user-facing tools reference explicit about both the accepted default and any schema-level mismatch.
3. Implement the Executor Branch
Add the execution branch to McpToolExecutorImpl.executeToolResult(). Decode with the production serializer, use the supplied McpToolContext, and return text from inside the shared runTool path so concurrency, safety, output limits, telemetry, and read-time redaction remain active.
Then add the ID to the appropriate list in McpToolRegistrations (McpToolHandlers.kt). The category wrapper (RequestTools.kt, HistoryTools.kt, and so on) registers that list with the server. A catalog entry without this registration is visible in settings but is not callable; a registered ID missing from the catalog is skipped.
4. Add Safety Gating (If Needed)
If your tool is marked as unsafeOnly:
The global Enable Unsafe Tools switch (or an explicit per-tool allowance) must permit it before it can run.
defaultEnabledremains an independent catalog choice. For example, a tool may be catalog-enabled yet still blocked by the unsafe master switch.Add
McpScopeFilterenforcement before any network request or state-changing target operation; the descriptor does not add scope checks automatically.
5. Privacy Integration
If your tool returns sensitive data:
Use
McpToolContext.privacyModeandRedactionPolicy.fromMode(...)for typed decisions.Sanitize structured fields before serialization. Use the shared helpers where the carrier matches:
sanitizeHeaders,sanitizeParameters, andmaybeAnonymizeUrl.Keep the final result on the normal executor path so
McpToolContext.redactIfNeededalso applies the current read-time text policy.Do not rely on the generic text pass to infer a secret stored under keys such as
value,detail, orurl; it no longer has the producer's type information.Example:
cookie_jar_getredacts cookie values unless privacy mode isOFF, whilerequest_parsestrips parameters identified by Montoya asCOOKIEbefore producing JSON.
Tool Categories
Existing categories for reference:
Burp Control: Proxy intercept, task engine state.
Collaborator: Payload generation and polling.
Config: Project/user options.
Editor: Message editor get/set.
Extension: Status information.
History: Proxy HTTP/WebSocket history.
Issues: Issue creation.
Requests: HTTP requests, Repeater, Intruder, parsing.
Scanner: Audit, crawl, reports (Pro only).
Scope: URL scope management.
Site Map: Site map browsing and search.
Utilities: Encoding, hashing, JWT, compression.
Testing
Test your tool handler with mock
McpToolContextobjects.Verify catalog/registration/schema parity so the ID appears once in
McpToolCatalog, once in the correctMcpToolRegistrationsgroup, and ininputSchema().Verify the explicit
SecTierand the model-emitted approval behavior independently from unsafe gating.Verify safety gating: ensure an unsafe tool is blocked when unsafe tools are disabled.
Verify privacy integration in
STRICT,BALANCED, andOFF, including serialized output—not only the pre-serialization object.Add one positive removal fixture, one
OFFbyte-preservation control, and a nearby non-secret value to detect destructive over-redaction.If the tool introduces a new carrier or producer, add it to the carrier-ownership inventory so a later producer cannot bypass the sanitizer silently.
Test edge cases: empty input, invalid parameters, missing Burp Pro features.
Last updated
