diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index c02fbd2f..945825eb 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -856,6 +856,21 @@ "description": "Manage Bitbucket repos, PRs, branches, issues, webhooks, and pipelines for Data Center and Cloud.", "icon": "./plugins/avivsinai/bitbucket-cli/assets/bkt-icon.svg" }, + { + "name": "burpsuite-mcp-bridge", + "displayName": "BurpSuite MCP Bridge", + "source": { + "source": "local", + "path": "./plugins/6jeffr3y/burpsuite-mcp-bridge" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Tools & Integrations", + "description": "Bridge Burp Suite traffic, replay, rewrite rules, and evidence export into Codex through MCP for WSL, Windows, and macOS workflows.", + "icon": "./plugins/6jeffr3y/burpsuite-mcp-bridge/assets/icon.svg" + }, { "name": "calle", "displayName": "Call-E", diff --git a/README.md b/README.md index 7ce8c43f..9f7b012f 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ Third-party plugins built by the community. [PRs welcome](#contributing)! - [Apple Productivity](https://github.com/matk0shub/apple-productivity-mcp) - Local Apple Calendar and Reminders tooling for macOS with Codex plugin adapters. - [AxonFlow](https://github.com/getaxonflow/axonflow-codex-plugin) - Runtime governance for Codex with policy enforcement on terminal commands, advisory checks for non-terminal tools via skills, PII/secret detection, and compliance-grade audit trails. Self-hosted via Docker. - [Bitbucket CLI](https://github.com/avivsinai/bitbucket-cli) - Manage Bitbucket repos, PRs, branches, issues, webhooks, and pipelines for Data Center and Cloud. +- [BurpSuite MCP Bridge](https://github.com/6jeffr3y/burpsuite-mcp-bridge) - Bridge Burp Suite traffic, replay, rewrite rules, and evidence export into Codex through MCP for WSL, Windows, and macOS workflows. - [Call-E](https://github.com/CALLE-AI/call-e-integrations) - Plan, run, and inspect Call-E phone call workflows from Codex through the calle CLI. - [Canvas Apps Plugin Codex](https://github.com/Ratnam-Mishra/canvas-apps-plugin-codex) - Build and edit Microsoft Power Apps Canvas Apps using natural language and Canvas Authoring MCP server. - [Chrome DevTools](https://github.com/win4r/chrome-devtools-codex-plugin) - One-click Codex plugin wrapper for chrome-devtools-mcp. diff --git a/plugins.json b/plugins.json index dfeebe7b..81a34f0e 100644 --- a/plugins.json +++ b/plugins.json @@ -2,8 +2,8 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "name": "awesome-codex-plugins", "version": "1.0.0", - "last_updated": "2026-06-04", - "total": 98, + "last_updated": "2026-06-06", + "total": 99, "categories": [ "Development & Workflow", "Tools & Integrations" @@ -589,6 +589,16 @@ "source": "awesome-codex-plugins", "install_url": "https://raw.githubusercontent.com/avivsinai/bitbucket-cli/HEAD/.codex-plugin/plugin.json" }, + { + "name": "BurpSuite MCP Bridge", + "url": "https://github.com/6jeffr3y/burpsuite-mcp-bridge", + "owner": "6jeffr3y", + "repo": "burpsuite-mcp-bridge", + "description": "Bridge Burp Suite traffic, replay, rewrite rules, and evidence export into Codex through MCP for WSL, Windows, and macOS workflows.", + "category": "Tools & Integrations", + "source": "awesome-codex-plugins", + "install_url": "https://raw.githubusercontent.com/6jeffr3y/burpsuite-mcp-bridge/HEAD/.codex-plugin/plugin.json" + }, { "name": "Call-E", "url": "https://github.com/CALLE-AI/call-e-integrations", diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/.codex-plugin/plugin.json b/plugins/6jeffr3y/burpsuite-mcp-bridge/.codex-plugin/plugin.json new file mode 100755 index 00000000..1f7bb513 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/.codex-plugin/plugin.json @@ -0,0 +1,45 @@ +{ + "name": "burpsuite-mcp-bridge", + "version": "1.1.0", + "description": "Burp Suite MCP bridge for target-centric traffic triage, replay, rewrite automation, UI selection handoff, BCheck/Bambda import, and evidence export across WSL/Windows/macOS clients.", + "author": { + "name": "BurpSuite MCP Bridge", + "email": "6jeffr3y@users.noreply.github.com", + "url": "https://github.com/6jeffr3y/burpsuite-mcp-bridge" + }, + "homepage": "https://github.com/6jeffr3y/burpsuite-mcp-bridge", + "repository": "https://github.com/6jeffr3y/burpsuite-mcp-bridge", + "license": "Proprietary Runtime Distribution", + "keywords": [ + "burp", + "mcp", + "codex", + "wsl", + "windows", + "montoya", + "proxy", + "agent-ai" + ], + "mcpServers": "./.mcp.json", + "interface": { + "displayName": "BurpSuite MCP Bridge", + "shortDescription": "Bridge Burp Suite traffic, replay, and rewrite workflows into MCP clients and IDEs", + "longDescription": "Expose Burp Suite proxy, history, logger-like internal tool traffic, and UI-selected messages to MCP clients. Supports target-centric overview, low-noise polling, replay with mutation, request/response rewrite rules with modify/drop/spoof actions, Repeater handoff, BCheck/Bambda import, and exportable raw evidence.", + "developerName": "BurpSuite MCP Bridge", + "category": "Coding", + "capabilities": [ + "Read", + "Write", + "Interactive" + ], + "websiteURL": "https://github.com/6jeffr3y/burpsuite-mcp-bridge", + "brandColor": "#f97316", + "composerIcon": "./assets/icon.svg", + "logo": "./assets/logo.svg", + "defaultPrompt": [ + "Use BurpSuite MCP Bridge to build a target overview for the current host and pick high-value candidate flows.", + "Use BurpSuite MCP Bridge to inspect a selected Burp message, replay one controlled mutation, and export the raw evidence bundle.", + "Use BurpSuite MCP Bridge to create a temporary scoped rewrite rule, verify its effect, then delete or disable it." + ] + } +} diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/.codexignore b/plugins/6jeffr3y/burpsuite-mcp-bridge/.codexignore new file mode 100755 index 00000000..8b8d8016 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/.codexignore @@ -0,0 +1,4 @@ +artifacts/ +**/__pycache__/ +*.pyc +*.log diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/.mcp.json b/plugins/6jeffr3y/burpsuite-mcp-bridge/.mcp.json new file mode 100755 index 00000000..56566f49 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "burpsuite-mcp-bridge": { + "command": "python3", + "args": [ + "./wsl-mcp/server.py" + ], + "env": { + "BURP_MCP_BRIDGE_URL": "http://127.0.0.1:9639" + } + } + } +} diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG.md b/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG.md new file mode 100755 index 00000000..9179d1f1 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +## 1.1.0 + +### Highlights +- Added target-centric traffic overview via `burp_target_overview`. +- Added staged MCP help via `burp_mcp_list`. +- Implemented real rewrite-rule actions: `modify`, `drop`, and `spoof`. +- Added rule scope control: `proxy`, `tool`, and `all`. +- Added Burp 2026.4.x runtime-detected integrations: + - command palette / HotKey selection capture + - official internal-tool request drop/spoof when available + - BCheck import + - Bambda import +- Added Burp UI diagnostics, command copy helpers, and improved rewrite-rule UX. +- Simplified configuration: examples directly start `wsl-mcp/server.py` and set `BURP_MCP_BRIDGE_URL`; wrapper scripts were removed from the release package. + +### Stability / Compatibility +- Compile baseline remains `montoya-api 2025.10`. +- Optional 2026.4.x features are detected at runtime. +- JSON detail responses keep preview-first body handling; full raw evidence is exported via bundle files. + +### Tested Baseline +- Burp Suite Professional 2026.4.2 + +## 1.0.0 + +### Highlights +- Initial release for Windows Burp ↔ WSL Codex / Agent AI / MCP CLI / IDE communication. +- Reads Burp Proxy traffic and logger-like internal HTTP tool traffic. +- Supports replay, rewrite rules, Repeater handoff, and raw bundle export. diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG_CN.md b/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG_CN.md new file mode 100755 index 00000000..e811d157 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/CHANGELOG_CN.md @@ -0,0 +1,31 @@ +# 更新日志 + +## 1.1.0 + +### 重点更新 +- 新增目标视角流量画像:`burp_target_overview`。 +- 新增分级 MCP 帮助:`burp_mcp_list`。 +- 改写规则动作真正落地:`modify`、`drop`、`spoof`。 +- 规则作用面支持:`proxy`、`tool`、`all`。 +- 接入 Burp 2026.4.x 运行时检测能力: + - command palette / HotKey selection 捕获 + - 可用时使用官方 internal-tool request drop/spoof + - BCheck 导入 + - Bambda 导入 +- 增强 Burp UI:自检、命令复制、规则 UX。 +- 简化配置:示例直接启动 `wsl-mcp/server.py` 并设置 `BURP_MCP_BRIDGE_URL`;release 包移除 wrapper 脚本。 + +### 稳定性 / 兼容性 +- 编译基线保持 `montoya-api 2025.10`。 +- 2026.4.x 可选能力运行时检测。 +- JSON 详情接口继续使用 preview-first body;完整原始证据通过 bundle 文件导出。 + +### 测试基线 +- Burp Suite Professional 2026.4.2 + +## 1.0.0 + +### 重点更新 +- 初始发布,支持 Windows Burp ↔ WSL Codex / Agent AI / MCP CLI / IDE 通信。 +- 支持 Burp Proxy 流量与 logger-like 内部工具流量读取。 +- 支持重放、改写规则、Repeater 联动和原始包导出。 diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE.txt b/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE.txt new file mode 100755 index 00000000..90ecd124 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE.txt @@ -0,0 +1,7 @@ +BurpSuite MCP Bridge + +Distribution notes: +- review homepage / repository / support URLs before public release +- review organization branding and contact information +- verify the default bridge URL matches your target release environment +- verify Python runtime availability for directly starting wsl-mcp/server.py diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE_CN.txt b/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE_CN.txt new file mode 100755 index 00000000..cd643a47 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/NOTICE_CN.txt @@ -0,0 +1,7 @@ +BurpSuite MCP Bridge + +发布前建议检查: +- homepage / repository / support URL 是否替换为正式地址 +- 品牌信息与联系方式是否符合你的发布环境 +- 默认 Bridge URL 是否符合目标环境 +- Python 运行环境是否可以直接启动 wsl-mcp/server.py diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/README.md b/plugins/6jeffr3y/burpsuite-mcp-bridge/README.md new file mode 100755 index 00000000..a398482a --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/README.md @@ -0,0 +1,190 @@ +# BurpSuite MCP Bridge + +English | [简体中文](./README_CN.md) + +**MCP bridge for Burp Suite traffic, replay, rewrite automation, UI selection handoff, and evidence export.** + +This release is designed for real mixed-environment workflows: Burp can run on Windows while Codex/AI agents run in WSL, Windows, or macOS. The default setup uses stdio MCP and points the Python MCP server at the Burp extension bridge with one explicit URL. + +--- + +## What's new in v1.1.0 + +- Unified MCP response/error shape. +- Rewrite rules now support real `modify`, `drop`, and `spoof` actions. +- Rule scope supports `proxy`, `tool`, and `all`. +- Burp 2026.4.x runtime-detected integrations: + - command palette / HotKey selection capture + - official internal-tool request drop/spoof when available + - BCheck import + - Bambda import +- New target-centric workflow: `burp_target_overview(host=...)`. +- New staged help: `burp_mcp_list(section=..., topic=...)`. +- Better Burp UI diagnostics, quick MCP command copy panel, and rewrite-rule UX. +- Simplified configuration: no wrapper scripts are required. + +Tested with **Burp Suite Professional 2026.4.2**. Compile baseline remains `montoya-api 2025.10`; newer features are runtime-detected for compatibility. + +--- + +## Included files + +```text +burp-plugin/ + burpsuite-mcp-bridge-1.1.0-all.jar + burpsuite-mcp-bridge-latest.jar +wsl-mcp/ + server.py +config-examples/ + codex-wsl-mirrored.toml + codex-wsl-nat.toml + codex-windows.toml + codex-macos.toml +requirements-wsl.txt +``` + +--- + +## Quick start + +### 1) Load the Burp extension + +In Burp Suite, load: + +```text +burp-plugin/burpsuite-mcp-bridge-latest.jar +``` + +Recommended bridge settings: + +```text +Bind host: 127.0.0.1 +Port: 9639 +Max live/logger entries: 1500 +Max body preview bytes: 32768 +Ignore static: on +``` + +For WSL NAT, bind Burp Bridge to the Windows LAN IP or `0.0.0.0`, then use that LAN IP in `BURP_MCP_BRIDGE_URL`. + +### 2) Install MCP runtime dependency + +```bash +python3 -m pip install -r requirements-wsl.txt +``` + +On Windows, use your normal Python installation and install the same requirement. + +### 3) Configure Codex / MCP + +Default stdio setup directly starts `wsl-mcp/server.py`; no wrapper script is required. + +WSL mirrored / local loopback: + +```toml +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://127.0.0.1:9639" +``` + +WSL NAT example: + +```toml +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://192.168.1.100:9639" +``` + +See `config-examples/` for WSL mirrored, WSL NAT, Windows, and macOS variants. + +--- + +## Codex plugin marketplace readiness + +This repository ships a valid `.codex-plugin/plugin.json`, icon assets, release JARs, and direct MCP configuration examples. It is suitable for submission to community Codex plugin directories such as `awesome-codex-plugins` for discovery, while remaining installable directly from this repository. + +--- + +## Core MCP tools + +### Status and help + +- `burp_bridge_status` +- `burp_config_get` +- `burp_mcp_list` + +### Target and traffic + +- `burp_target_overview` +- `burp_live_poll` +- `burp_live_overview` +- `burp_history_search` +- `burp_logger_poll` +- `burp_logger_overview` +- `burp_extension_activity_overview` +- `burp_selection_poll` +- `burp_flow_get` +- `burp_logger_flow_get` +- `burp_selection_get` + +### Replay and evidence + +- `burp_replay_flow` +- `burp_send_raw_request` +- `burp_send_to_repeater` +- `burp_export_flow` +- `burp_export_flow_bundle` + +### Automation + +- `burp_rules_list` +- `burp_rule_upsert` +- `burp_rule_delete` +- `burp_bcheck_import` +- `burp_bambda_import` + +--- + +## Recommended workflow + +1. Start with `burp_target_overview(host="target.example")`. +2. Inspect one candidate flow with the matching getter: + - `burp_flow_get(..., source="history" | "live")` + - `burp_logger_flow_get(...)` + - `burp_selection_get(...)` +3. Replay one controlled mutation with `burp_replay_flow`. +4. Export decisive raw evidence with `burp_export_flow_bundle`. +5. If a behavior is reusable, promote it to a rewrite rule, BCheck, or Bambda. + +--- + +## Body handling + +JSON detail calls inline body previews only. Large bodies are capped to avoid MCP context bloat and Burp/UI pressure. For full raw request/response bytes, use: + +```python +burp_export_flow_bundle(flow_id=123, source="history") +``` + +--- + +## Optional Streamable HTTP MCP + +The default release examples use stdio MCP. If you need Streamable HTTP, start it manually: + +```bash +BURP_MCP_BRIDGE_URL=http://127.0.0.1:9639 \ +python3 wsl-mcp/server.py --transport streamable-http --host 127.0.0.1 --port 9640 --path /mcp +``` + +Default URL: + +```text +http://127.0.0.1:9640/mcp +``` diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/README_CN.md b/plugins/6jeffr3y/burpsuite-mcp-bridge/README_CN.md new file mode 100755 index 00000000..cbbd95ff --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/README_CN.md @@ -0,0 +1,190 @@ +# BurpSuite MCP Bridge + +[English](./README.md) | 简体中文 + +**把 Burp Suite 的流量、重放、改写规则、UI 选中消息和证据导出能力接入 MCP / Codex / AI Agent。** + +本项目面向真实混合环境:Burp 可以跑在 Windows,Codex/AI Agent 可以跑在 WSL、Windows 或 macOS。默认使用 stdio MCP,并在 TOML 中用一个完整 URL 指向 Burp 扩展 bridge。 + +--- + +## v1.1.0 新增内容 + +- 统一 MCP 返回和错误结构。 +- 改写规则动作真正落地:`modify`、`drop`、`spoof`。 +- 规则作用面支持:`proxy`、`tool`、`all`。 +- 接入 Burp 2026.4.x 运行时能力: + - command palette / HotKey 捕获 UI selection + - 可用时使用官方 internal-tool request drop/spoof + - BCheck 导入 + - Bambda 导入 +- 新增目标视角工作流:`burp_target_overview(host=...)`。 +- 新增分级帮助:`burp_mcp_list(section=..., topic=...)`。 +- 增强 Burp UI:自检状态、MCP 命令速查复制、规则 UX。 +- 简化配置:不再需要 wrapper 脚本。 + +已用 **Burp Suite Professional 2026.4.2** 测试。编译基线仍保持 `montoya-api 2025.10`,新版能力运行时检测,尽量保持兼容。 + +--- + +## 包含文件 + +```text +burp-plugin/ + burpsuite-mcp-bridge-1.1.0-all.jar + burpsuite-mcp-bridge-latest.jar +wsl-mcp/ + server.py +config-examples/ + codex-wsl-mirrored.toml + codex-wsl-nat.toml + codex-windows.toml + codex-macos.toml +requirements-wsl.txt +``` + +--- + +## 快速开始 + +### 1)加载 Burp 扩展 + +在 Burp Suite 中加载: + +```text +burp-plugin/burpsuite-mcp-bridge-latest.jar +``` + +推荐设置: + +```text +Bind host: 127.0.0.1 +Port: 9639 +Max live/logger entries: 1500 +Max body preview bytes: 32768 +Ignore static: on +``` + +如果是 WSL NAT,需要把 Burp Bridge 绑定到 Windows 局域网 IP 或 `0.0.0.0`,然后在 `BURP_MCP_BRIDGE_URL` 里使用这个局域网 IP。 + +### 2)安装 MCP runtime 依赖 + +```bash +python3 -m pip install -r requirements-wsl.txt +``` + +Windows 环境使用本机 Python 安装同样依赖即可。 + +### 3)配置 Codex / MCP + +默认 stdio 配置直接启动 `wsl-mcp/server.py`,不需要 wrapper 脚本。 + +WSL mirrored / 本机 loopback: + +```toml +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://127.0.0.1:9639" +``` + +WSL NAT 示例: + +```toml +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://192.168.1.100:9639" +``` + +更多环境参考 `config-examples/`。 + +--- + +## Codex 插件市场准备状态 + +本仓库已经包含有效的 `.codex-plugin/plugin.json`、图标资源、发布版 JAR 和直接启动 MCP 的配置示例。它可以提交到 `awesome-codex-plugins` 这类社区 Codex 插件目录获取曝光,同时也可以直接从本仓库安装使用。 + +--- + +## 主要 MCP 工具 + +### 状态与帮助 + +- `burp_bridge_status` +- `burp_config_get` +- `burp_mcp_list` + +### 目标和流量 + +- `burp_target_overview` +- `burp_live_poll` +- `burp_live_overview` +- `burp_history_search` +- `burp_logger_poll` +- `burp_logger_overview` +- `burp_extension_activity_overview` +- `burp_selection_poll` +- `burp_flow_get` +- `burp_logger_flow_get` +- `burp_selection_get` + +### 重放和证据 + +- `burp_replay_flow` +- `burp_send_raw_request` +- `burp_send_to_repeater` +- `burp_export_flow` +- `burp_export_flow_bundle` + +### 自动化 + +- `burp_rules_list` +- `burp_rule_upsert` +- `burp_rule_delete` +- `burp_bcheck_import` +- `burp_bambda_import` + +--- + +## 推荐工作流 + +1. 先用 `burp_target_overview(host="target.example")` 做目标画像。 +2. 挑一个候选 flow 拉完整详情: + - `burp_flow_get(..., source="history" | "live")` + - `burp_logger_flow_get(...)` + - `burp_selection_get(...)` +3. 用 `burp_replay_flow` 一次只改一个变量验证。 +4. 用 `burp_export_flow_bundle` 导出决定性原始证据。 +5. 如果模式可复用,再沉淀成 rewrite rule、BCheck 或 Bambda。 + +--- + +## Body 处理 + +JSON 详情接口只内联 body preview,避免撑爆 MCP 上下文和拖慢 Burp/UI。完整原始请求/响应请使用: + +```python +burp_export_flow_bundle(flow_id=123, source="history") +``` + +--- + +## 可选 Streamable HTTP MCP + +默认示例使用 stdio MCP。如需 Streamable HTTP,可手工启动: + +```bash +BURP_MCP_BRIDGE_URL=http://127.0.0.1:9639 \ +python3 wsl-mcp/server.py --transport streamable-http --host 127.0.0.1 --port 9640 --path /mcp +``` + +默认 URL: + +```text +http://127.0.0.1:9640/mcp +``` diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/RELEASE_NOTES_v1.1.0.md b/plugins/6jeffr3y/burpsuite-mcp-bridge/RELEASE_NOTES_v1.1.0.md new file mode 100755 index 00000000..91579570 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/RELEASE_NOTES_v1.1.0.md @@ -0,0 +1,37 @@ +# BurpSuite MCP Bridge v1.1.0 + +## Highlights + +- Target-centric overview for real testing workflows: `burp_target_overview(host=...)`. +- Staged tool help: `burp_mcp_list(section=..., topic=..., detail=true)`. +- Rewrite rules now support real `modify`, `drop`, and `spoof` actions. +- Rule scope can target `proxy`, `tool`, or `all`. +- Burp 2026.4.x integrations: + - command palette / HotKey selection capture + - official internal-tool drop/spoof when available + - BCheck import + - Bambda import +- Improved Burp UI for diagnostics, MCP command copy, and rewrite-rule management. +- Simplified configs: direct `python3 wsl-mcp/server.py` plus `BURP_MCP_BRIDGE_URL`; no wrapper scripts in release. + +## Included assets + +- `burp-plugin/burpsuite-mcp-bridge-1.1.0-all.jar` +- `burp-plugin/burpsuite-mcp-bridge-latest.jar` +- `wsl-mcp/server.py` +- Four config examples: + - WSL mirrored + - WSL NAT + - Windows + - macOS + +## Tested baseline + +- Burp Suite Professional `2026.4.2` +- Compile baseline: `montoya-api 2025.10` + +## Upgrade notes + +- Replace the old Burp JAR with `burpsuite-mcp-bridge-latest.jar`. +- Update MCP config to start `wsl-mcp/server.py` directly. +- Use `BURP_MCP_BRIDGE_URL` instead of separate host/port values for clearer WSL NAT and multi-host setups. diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/icon.svg b/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/icon.svg new file mode 100755 index 00000000..f47d97e4 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/logo.svg b/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/logo.svg new file mode 100755 index 00000000..f47d97e4 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/assets/logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-1.1.0-all.jar b/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-1.1.0-all.jar new file mode 100755 index 00000000..fbeea11f Binary files /dev/null and b/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-1.1.0-all.jar differ diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-latest.jar b/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-latest.jar new file mode 100755 index 00000000..fbeea11f Binary files /dev/null and b/plugins/6jeffr3y/burpsuite-mcp-bridge/burp-plugin/burpsuite-mcp-bridge-latest.jar differ diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-macos.toml b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-macos.toml new file mode 100755 index 00000000..a537883b --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-macos.toml @@ -0,0 +1,9 @@ +# macOS local setup +# Burp and MCP server both run on macOS, so loopback is enough. +# Replace the path with your local clone path. +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/Users/you/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://127.0.0.1:9639" diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-windows.toml b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-windows.toml new file mode 100755 index 00000000..898a4748 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-windows.toml @@ -0,0 +1,8 @@ +# Native Windows Codex/MCP +# Burp and MCP server both run on Windows, so loopback is enough. +[mcp_servers.burpsuite-mcp-bridge] +command = "python" +args = ["D:\\AI_project\\burpsuite-mcp-bridge-release\\wsl-mcp\\server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://127.0.0.1:9639" diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-mirrored.toml b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-mirrored.toml new file mode 100755 index 00000000..e3b0833e --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-mirrored.toml @@ -0,0 +1,8 @@ +# WSL mirrored networking (recommended for current setup) +# Burp runs on Windows, Codex/MCP runs inside WSL, and 127.0.0.1 reaches Windows Burp. +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://127.0.0.1:9639" diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-nat.toml b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-nat.toml new file mode 100755 index 00000000..18a3080c --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/config-examples/codex-wsl-nat.toml @@ -0,0 +1,10 @@ +# WSL NAT networking +# Burp runs on Windows, Codex/MCP runs inside WSL, but 127.0.0.1 is WSL itself. +# Replace 192.168.1.100 with the Windows LAN IP reachable from WSL. +# In Burp MCP Bridge UI, bindHost should be that LAN IP or 0.0.0.0, not only 127.0.0.1. +[mcp_servers.burpsuite-mcp-bridge] +command = "python3" +args = ["/mnt/d/AI_project/burpsuite-mcp-bridge-release/wsl-mcp/server.py"] + +[mcp_servers.burpsuite-mcp-bridge.env] +BURP_MCP_BRIDGE_URL = "http://192.168.1.100:9639" diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/requirements-wsl.txt b/plugins/6jeffr3y/burpsuite-mcp-bridge/requirements-wsl.txt new file mode 100755 index 00000000..c413b608 --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/requirements-wsl.txt @@ -0,0 +1 @@ +mcp>=1.0.0 diff --git a/plugins/6jeffr3y/burpsuite-mcp-bridge/wsl-mcp/server.py b/plugins/6jeffr3y/burpsuite-mcp-bridge/wsl-mcp/server.py new file mode 100755 index 00000000..9f0d7e8f --- /dev/null +++ b/plugins/6jeffr3y/burpsuite-mcp-bridge/wsl-mcp/server.py @@ -0,0 +1,1251 @@ +from __future__ import annotations + +import argparse +import json +import os +import re +import time +import urllib.error +import urllib.parse +import urllib.request +from collections import Counter, defaultdict +from pathlib import Path +from typing import Any + +from mcp.server.fastmcp import FastMCP + +_NO_PROXY_OPENER = urllib.request.build_opener(urllib.request.ProxyHandler({})) +DEFAULT_HOST = os.environ.get("BURP_MCP_BRIDGE_HOST", "127.0.0.1") +DEFAULT_PORT = int(os.environ.get("BURP_MCP_BRIDGE_PORT", "9639")) +MCP_TRANSPORT = os.environ.get("BURP_MCP_TRANSPORT", "stdio") +MCP_SERVER_HOST = os.environ.get("BURP_MCP_SERVER_HOST", "127.0.0.1") +MCP_SERVER_PORT = int(os.environ.get("BURP_MCP_SERVER_PORT", "9640")) +MCP_SERVER_PATH = os.environ.get("BURP_MCP_SERVER_PATH", "/mcp") +PLUGIN_ROOT = Path(os.environ.get("BURP_MCP_PLUGIN_ROOT", Path(__file__).resolve().parent.parent)) +ARTIFACT_ROOT = PLUGIN_ROOT / "artifacts" + +mcp = FastMCP( + "BurpSuite MCP Bridge", + instructions=( + "Use these tools to read and operate Burp proxy traffic from Windows Burp in WSL mirrored mode. " + "Prefer burp_target_overview(host=...) when working one target, or burp_live_overview/burp_live_poll for incremental triage, then burp_flow_get for a decisive request/response pair. " + "Use burp_replay_flow or burp_send_raw_request when you need AI-driven request mutation and replay. " + "Use burp_rule_upsert to install automatic request/response rewrite rules for proxied traffic; rule action is modify, drop, or spoof." + ), + host=MCP_SERVER_HOST, + port=MCP_SERVER_PORT, + streamable_http_path=MCP_SERVER_PATH, +) + + +def resolve_bridge_base() -> str: + explicit = os.environ.get("BURP_MCP_BRIDGE_URL") + if explicit: + return explicit.rstrip("/") + return f"http://{DEFAULT_HOST}:{DEFAULT_PORT}" + + +def _request_json(path: str, method: str = "GET", payload: dict[str, Any] | None = None, query: dict[str, Any] | None = None) -> Any: + url = resolve_bridge_base() + path + if query: + filtered = {k: v for k, v in query.items() if v is not None} + if filtered: + url += "?" + urllib.parse.urlencode(filtered) + + body = None + headers = {"Accept": "application/json"} + if payload is not None: + body = json.dumps(payload).encode("utf-8") + headers["Content-Type"] = "application/json" + + request = urllib.request.Request(url, data=body, method=method, headers=headers) + try: + with _NO_PROXY_OPENER.open(request, timeout=30) as response: + data = json.loads(response.read().decode("utf-8")) + except urllib.error.HTTPError as exc: + response_body = exc.read().decode("utf-8", errors="ignore") + try: + parsed = json.loads(response_body) + error = parsed.get("error") if isinstance(parsed, dict) else None + if isinstance(error, dict): + detail = f"{error.get('code', 'error')}: {error.get('message', response_body)}" + else: + detail = parsed.get("message", response_body) if isinstance(parsed, dict) else response_body + except Exception: + detail = response_body + raise RuntimeError(f"bridge HTTP {exc.code}: {detail}") from exc + except urllib.error.URLError as exc: + raise RuntimeError( + "无法连接 Burp MCP Bridge。请确认:1) Windows Burp 已加载扩展;2) Burp 扩展已启用;" + f"3) WSL mirrored 下可访问 {resolve_bridge_base()}/health。底层错误:{exc.reason}" + ) from exc + + if isinstance(data, dict) and data.get("ok") is False: + error = data.get("error") + if isinstance(error, dict): + raise RuntimeError(f"{error.get('code', 'error')}: {error.get('message', 'bridge returned failure')}") + raise RuntimeError(data.get("message") or error or "bridge returned failure") + return data + + +def _timestamp() -> str: + return time.strftime("%Y%m%d-%H%M%S") + + +_STATIC_PATH_RE = re.compile(r"(?i)\.(?:css|js|mjs|map|png|jpe?g|gif|svg|ico|woff2?|ttf|eot|mp4|webm|mp3|wav|pdf|zip|gz|br|wasm)(?:$|[?#])") +_UUID_RE = re.compile(r"(?i)^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") +_HEX_ID_RE = re.compile(r"(?i)^[0-9a-f]{16,}$") +_LONG_TOKEN_RE = re.compile(r"(?i)^[a-z0-9_-]{24,}$") +_TARGET_SOURCE_ORDER = ("history", "live", "logger", "selection") + + +def _parse_source_csv(sources: str | None, default: tuple[str, ...] = _TARGET_SOURCE_ORDER) -> list[str]: + if not sources: + return list(default) + requested = [part.strip().lower() for part in sources.split(",") if part.strip()] + if not requested or "all" in requested: + return list(default) + allowed = set(default) + unknown = [source for source in requested if source not in allowed] + if unknown: + raise ValueError(f"sources 只支持 {', '.join(default)} 或 all;未知值:{', '.join(unknown)}") + return [source for source in default if source in requested] + + +def _path_no_query(path: Any) -> str: + value = str(path or "") + if not value: + return "" + return value.split("?", 1)[0] or "/" + + +def _is_static_path(path: Any) -> bool: + return bool(path and _STATIC_PATH_RE.search(str(path))) + + +def _normalize_endpoint_path(path: Any) -> str: + raw = _path_no_query(path) + if raw in {"", ""}: + return "" + leading = raw.startswith("/") + parts = [part for part in raw.split("/") if part] + normalized: list[str] = [] + for part in parts: + lower = part.lower() + if part.isdigit(): + normalized.append("{int}") + elif _UUID_RE.match(part): + normalized.append("{uuid}") + elif _HEX_ID_RE.match(part): + normalized.append("{hex}") + elif _LONG_TOKEN_RE.match(part) and any(ch.isdigit() for ch in part) and not any(ch in lower for ch in (".", "-")): + normalized.append("{id}") + else: + normalized.append(part) + if not normalized: + return "/" + return ("/" if leading else "") + "/".join(normalized) + + +def _counter_key(value: Any, default: str = "") -> str: + if value is None: + return default + text = str(value) + return text if text else default + + +def _flow_card(item: dict[str, Any], source: str | None = None, reasons: list[str] | None = None, score: int | None = None) -> dict[str, Any]: + actual_source = source or item.get("source") or "unknown" + flow_id = item.get("flowId") + if flow_id is None: + flow_id = item.get("historyId") + card = { + "source": actual_source, + "flowId": flow_id, + "method": item.get("method"), + "host": item.get("host"), + "path": item.get("path"), + "endpoint": _normalize_endpoint_path(item.get("path")), + "statusCode": item.get("statusCode"), + "mimeType": item.get("mimeType"), + "toolType": item.get("toolType"), + "tags": item.get("tags") or [], + } + if score is not None: + card["score"] = score + if reasons is not None: + card["reasons"] = reasons + return card + + +def _score_target_flow(item: dict[str, Any]) -> tuple[int, list[str]]: + score = 0 + reasons: list[str] = [] + + def add(delta: int, reason: str) -> None: + nonlocal score + score += delta + if reason not in reasons: + reasons.append(reason) + + method = str(item.get("method") or "").upper() + path = str(item.get("path") or "").lower() + url = str(item.get("url") or "").lower() + haystack = f"{path} {url}" + tags = {str(tag).lower() for tag in (item.get("tags") or [])} + status = item.get("statusCode") + try: + status_int = int(status) if status is not None else None + except Exception: + status_int = None + + if tags: + for tag in sorted(tags): + if tag in {"auth", "upload", "api"}: + add(8, f"tag:{tag}") + elif tag not in {"static"}: + add(3, f"tag:{tag}") + + if any(k in haystack for k in ("login", "auth", "oauth", "sso", "cas", "saml", "token", "session", "signin", "logout")): + add(12, "auth/session endpoint") + if any(k in haystack for k in ("/api/", "/apis/", "/graphql", "/rpc", "/v1/", "/v2/", "/rest/")): + add(8, "api/rpc endpoint") + if any(k in haystack for k in ("upload", "import", "attach", "avatar", "file", "multipart")): + add(10, "upload/import/file endpoint") + if any(k in haystack for k in ("download", "export", "report", "excel", "pdf", "template")): + add(7, "download/export endpoint") + if any(k in haystack for k in ("admin", "manage", "permission", "role", "user", "config", "setting")): + add(7, "admin/permission/config endpoint") + if any(k in haystack for k in ("swagger", "openapi", "api-docs", "actuator", "debug", "trace")): + add(9, "metadata/debug endpoint") + if any(k in haystack for k in ("redirect", "callback", "returnurl", "return_url", "next=", "url=")): + add(5, "redirect/callback parameter") + if method in {"POST", "PUT", "PATCH", "DELETE"}: + add(6, "state-changing method") + if status_int in {401, 403}: + add(5, "authz boundary status") + elif status_int is not None and 500 <= status_int <= 599: + add(6, "server error status") + elif status_int is not None and 300 <= status_int <= 399: + add(2, "redirect status") + if item.get("toolType"): + tool = str(item.get("toolType")) + if tool.lower() in {"scanner", "extensions", "intruder", "repeater"}: + add(3, f"burp tool:{tool}") + + return score, reasons + + +def _edits_payload( + method: str | None = None, + path: str | None = None, + target_host: str | None = None, + target_port: int | None = None, + use_https: bool | None = None, + headers: dict[str, str] | None = None, + add_headers: dict[str, str] | None = None, + remove_headers: list[str] | None = None, + body: str | None = None, + path_replace_from: str | None = None, + path_replace_to: str | None = None, + body_replace_from: str | None = None, + body_replace_to: str | None = None, + status_code: int | None = None, + reason_phrase: str | None = None, +) -> dict[str, Any]: + return { + "method": method, + "path": path, + "targetHost": target_host, + "targetPort": target_port, + "useHttps": use_https, + "headers": headers, + "addHeaders": add_headers, + "removeHeaders": remove_headers, + "body": body, + "pathReplaceFrom": path_replace_from, + "pathReplaceTo": path_replace_to, + "bodyReplaceFrom": body_replace_from, + "bodyReplaceTo": body_replace_to, + "statusCode": status_code, + "reasonPhrase": reason_phrase, + } + + +@mcp.tool() +def burp_bridge_status() -> dict[str, Any]: + """检查 Windows Burp 侧桥接状态、Burp 版本、实时缓冲区容量、规则数量与当前监听地址。""" + return _request_json("/health") + + +@mcp.tool() +def burp_config_get() -> dict[str, Any]: + """读取 Burp 扩展当前配置。适合确认端口、body 截断、scope-only、静态资源过滤和规则数量。""" + return _request_json("/api/config") + + +@mcp.tool() +def burp_live_poll( + after_seq: int = 0, + limit: int = 20, + text: str | None = None, + host: str | None = None, + path: str | None = None, + method: str | None = None, + has_response: bool | None = None, + in_scope: bool | None = None, + include_bodies: bool = False, +) -> dict[str, Any]: + """从实时 ring buffer 增量读取 Burp Proxy 流量。优先用这个做低噪声轮询,再按 flowId 拉详情。""" + return _request_json( + "/api/flows", + query={ + "afterSeq": after_seq, + "limit": limit, + "text": text, + "host": host, + "path": path, + "method": method, + "hasResponse": has_response, + "inScope": in_scope, + "includeBodies": include_bodies, + }, + ) + + +@mcp.tool() +def burp_flow_get(flow_id: int, source: str = "live", include_bodies: bool = True) -> dict[str, Any]: + """读取单条流量的完整细节。source=live/history/logger/selection。""" + if source not in {"live", "history", "logger", "selection"}: + raise ValueError("source 必须是 live、history、logger 或 selection") + if source == "live": + path = f"/api/flows/{flow_id}" + elif source == "history": + path = f"/api/history/{flow_id}" + elif source == "logger": + path = f"/api/logger/flows/{flow_id}" + else: + path = f"/api/selection/flows/{flow_id}" + return _request_json(path, query={"includeBodies": include_bodies}) + + +@mcp.tool() +def burp_logger_poll( + after_seq: int = 0, + limit: int = 20, + text: str | None = None, + host: str | None = None, + path: str | None = None, + method: str | None = None, + tool_type: str | None = None, + has_response: bool | None = None, + in_scope: bool | None = None, + include_bodies: bool = False, +) -> dict[str, Any]: + """读取 Burp 内部 HTTP 工具流量(logger-like)。适合看 Repeater/Intruder/Scanner/插件 fuzz 等非 Proxy 面板流量。""" + return _request_json( + "/api/logger/flows", + query={ + "afterSeq": after_seq, + "limit": limit, + "text": text, + "host": host, + "path": path, + "method": method, + "toolType": tool_type, + "hasResponse": has_response, + "inScope": in_scope, + "includeBodies": include_bodies, + }, + ) + + +@mcp.tool() +def burp_logger_flow_get(flow_id: int, include_bodies: bool = True) -> dict[str, Any]: + """读取单条 Burp 内部 HTTP 工具流量详情。""" + return _request_json(f"/api/logger/flows/{flow_id}", query={"includeBodies": include_bodies}) + + +@mcp.tool() +def burp_selection_poll( + after_seq: int = 0, + limit: int = 20, + text: str | None = None, + host: str | None = None, + path: str | None = None, + method: str | None = None, + has_response: bool | None = None, + include_bodies: bool = False, +) -> dict[str, Any]: + """读取 Burp HotKey/command-palette 捕获的选中流量。人在 Burp 中选中后触发 Capture selection for AI,再由 AI 拉取。""" + return _request_json( + "/api/selection/flows", + query={ + "afterSeq": after_seq, + "limit": limit, + "text": text, + "host": host, + "path": path, + "method": method, + "hasResponse": has_response, + "includeBodies": include_bodies, + }, + ) + + +@mcp.tool() +def burp_selection_get(flow_id: int, include_bodies: bool = True) -> dict[str, Any]: + """读取一条由 Burp HotKey/command-palette 捕获的 selection flow 详情。""" + return burp_flow_get(flow_id=flow_id, source="selection", include_bodies=include_bodies) + + +@mcp.tool() +def burp_history_search( + query: str | None = None, + regex: bool = False, + limit: int = 20, + offset: int = 0, + host_contains: str | None = None, + path_contains: str | None = None, + method: str | None = None, + in_scope: bool | None = None, + has_response: bool | None = None, + status_min: int | None = None, + status_max: int | None = None, + include_bodies: bool = False, + ignore_static: bool | None = True, +) -> dict[str, Any]: + """搜索 Burp 全量 Proxy 历史。适合查旧流量、按关键字回溯登录/API/upload 等关键链路。""" + return _request_json( + "/api/history/search", + method="POST", + payload={ + "query": query, + "regex": regex, + "limit": limit, + "offset": offset, + "hostContains": host_contains, + "pathContains": path_contains, + "method": method, + "inScope": in_scope, + "hasResponse": has_response, + "statusMin": status_min, + "statusMax": status_max, + "includeBodies": include_bodies, + "ignoreStatic": ignore_static, + }, + ) + + +@mcp.tool() +def burp_send_to_repeater(flow_id: int, source: str = "live", tab_name: str = "AI review") -> dict[str, Any]: + """把选中的请求发到 Burp Repeater,方便继续手工验证或配合 AI 给出的下一步变体。""" + if source not in {"live", "history", "logger", "selection"}: + raise ValueError("source 必须是 live、history、logger 或 selection") + return _request_json( + "/api/actions/send-to-repeater", + method="POST", + payload={"id": flow_id, "source": source, "tabName": tab_name}, + ) + + +@mcp.tool() +def burp_clear_live_buffer() -> dict[str, Any]: + """清空实时流量缓冲区,适合开始一个新的验证阶段前先降噪。""" + return _request_json("/api/actions/clear-buffer", method="POST", payload={}) + + +@mcp.tool() +def burp_clear_logger_buffer() -> dict[str, Any]: + """清空 Burp 内部工具/logger-like 流量缓冲区。适合在 fuzz、重放或规则联调前先降噪。""" + return _request_json("/api/actions/clear-logger-buffer", method="POST", payload={}) + + +@mcp.tool() +def burp_clear_selection_buffer() -> dict[str, Any]: + """清空 HotKey/command-palette selection buffer。""" + return _request_json("/api/actions/clear-selection-buffer", method="POST", payload={}) + + +@mcp.tool() +def burp_export_flow_bundle(flow_id: int, source: str = "history") -> dict[str, Any]: + """导出一条 flow 的完整原始 request/response 到 bridge 所在主机的临时目录。适合超大包场景下安全取证。""" + if source not in {"live", "history", "logger", "selection"}: + raise ValueError("source 必须是 live、history、logger 或 selection") + return _request_json( + "/api/actions/export-flow-bundle", + method="POST", + payload={"id": flow_id, "source": source}, + ) + + +@mcp.tool() +def burp_live_overview(after_seq: int = 0, limit: int = 80) -> dict[str, Any]: + """快速汇总最近实时流量,按主机、状态码、标签统计,便于 AI 先做渗透流量定向。""" + data = burp_live_poll(after_seq=after_seq, limit=limit, include_bodies=False) + items = data.get("items", []) + host_counter: Counter[str] = Counter() + status_counter: Counter[str] = Counter() + tag_counter: Counter[str] = Counter() + interesting_by_host: dict[str, list[dict[str, Any]]] = defaultdict(list) + + for item in items: + host = item.get("host") or "" + host_counter[host] += 1 + status = item.get("statusCode") + status_counter[str(status) if status is not None else "pending"] += 1 + for tag in item.get("tags", []): + tag_counter[tag] += 1 + if len(interesting_by_host[host]) < 5: + interesting_by_host[host].append( + { + "flowId": item.get("flowId"), + "method": item.get("method"), + "path": item.get("path"), + "statusCode": item.get("statusCode"), + "tags": item.get("tags"), + "requestRuleHits": item.get("requestRuleHits", []), + "responseRuleHits": item.get("responseRuleHits", []), + } + ) + + return { + "ok": True, + "item": { + "count": len(items), + "latestCursor": data.get("latestCursor"), + "byHost": host_counter.most_common(), + "byStatus": status_counter.most_common(), + "byTag": tag_counter.most_common(), + "interestingByHost": dict(interesting_by_host), + }, + } + + +@mcp.tool() +def burp_logger_overview(after_seq: int = 0, limit: int = 80) -> dict[str, Any]: + """快速汇总 Burp 内部工具流量,尤其适合看 fuzz 插件/Repeater/Intruder/Scanner 的请求响应。""" + data = burp_logger_poll(after_seq=after_seq, limit=limit, include_bodies=False) + items = data.get("items", []) + host_counter: Counter[str] = Counter() + status_counter: Counter[str] = Counter() + tool_counter: Counter[str] = Counter() + interesting_by_tool: dict[str, list[dict[str, Any]]] = defaultdict(list) + + for item in items: + host = item.get("host") or "" + host_counter[host] += 1 + status = item.get("statusCode") + status_counter[str(status) if status is not None else "pending"] += 1 + tool = item.get("toolType") or "" + tool_counter[tool] += 1 + if len(interesting_by_tool[tool]) < 5: + interesting_by_tool[tool].append( + { + "flowId": item.get("flowId"), + "method": item.get("method"), + "path": item.get("path"), + "statusCode": item.get("statusCode"), + "host": item.get("host"), + } + ) + + return { + "ok": True, + "item": { + "count": len(items), + "latestCursor": data.get("latestCursor"), + "byHost": host_counter.most_common(), + "byStatus": status_counter.most_common(), + "byToolType": tool_counter.most_common(), + "interestingByToolType": dict(interesting_by_tool), + }, + } + + +@mcp.tool() +def burp_target_overview( + host: str | None = None, + text: str | None = None, + path: str | None = None, + sources: str = "all", + limit: int = 80, + include_static: bool = False, + include_extensions: bool = True, + candidate_limit: int = 20, +) -> dict[str, Any]: + """按被测目标聚合 live/history/logger/selection 流量。适合 AI 围绕一个 host 做入口、接口、状态码和高价值候选请求画像。""" + selected_sources = _parse_source_csv(sources) + per_source_limit = max(1, min(limit, 200)) + max_candidates = max(1, min(candidate_limit, 50)) + collected: list[dict[str, Any]] = [] + source_errors: dict[str, str] = {} + + if "history" in selected_sources: + try: + data = burp_history_search( + query=text, + limit=per_source_limit, + host_contains=host, + path_contains=path, + include_bodies=False, + ignore_static=not include_static, + ) + collected.extend(data.get("items", [])) + except Exception as exc: + source_errors["history"] = str(exc) + + if "live" in selected_sources: + try: + data = burp_live_poll( + limit=per_source_limit, + text=text, + host=host, + path=path, + include_bodies=False, + ) + items = data.get("items", []) + if not include_static: + items = [item for item in items if not _is_static_path(item.get("path"))] + collected.extend(items) + except Exception as exc: + source_errors["live"] = str(exc) + + if "logger" in selected_sources: + try: + data = burp_logger_poll( + limit=per_source_limit, + text=text, + host=host, + path=path, + include_bodies=False, + ) + items = data.get("items", []) + if not include_extensions: + items = [item for item in items if str(item.get("toolType") or "").lower() != "extensions"] + if not include_static: + items = [item for item in items if not _is_static_path(item.get("path"))] + collected.extend(items) + except Exception as exc: + source_errors["logger"] = str(exc) + + if "selection" in selected_sources: + try: + data = burp_selection_poll( + limit=per_source_limit, + text=text, + host=host, + path=path, + include_bodies=False, + ) + items = data.get("items", []) + if not include_static: + items = [item for item in items if not _is_static_path(item.get("path"))] + collected.extend(items) + except Exception as exc: + source_errors["selection"] = str(exc) + + by_source: Counter[str] = Counter() + by_host: Counter[str] = Counter() + by_status: Counter[str] = Counter() + by_method: Counter[str] = Counter() + by_tool_type: Counter[str] = Counter() + by_tag: Counter[str] = Counter() + by_endpoint: Counter[str] = Counter() + endpoint_samples: dict[str, dict[str, Any]] = {} + interesting: list[dict[str, Any]] = [] + seen_candidate_keys: set[tuple[str, Any]] = set() + + for item in collected: + source = _counter_key(item.get("source")) + by_source[source] += 1 + by_host[_counter_key(item.get("host"))] += 1 + by_method[_counter_key(item.get("method"))] += 1 + by_status[_counter_key(item.get("statusCode"), "pending")] += 1 + tool_type = item.get("toolType") + if tool_type: + by_tool_type[str(tool_type)] += 1 + for tag in item.get("tags") or []: + by_tag[str(tag)] += 1 + + endpoint_key = f"{_counter_key(item.get('method'), '?')} {_normalize_endpoint_path(item.get('path'))}" + by_endpoint[endpoint_key] += 1 + endpoint_samples.setdefault(endpoint_key, _flow_card(item)) + + score, reasons = _score_target_flow(item) + if score > 0: + key = (source, item.get("flowId")) + if key in seen_candidate_keys: + continue + seen_candidate_keys.add(key) + interesting.append(_flow_card(item, source=source, reasons=reasons, score=score)) + + interesting.sort(key=lambda card: (-int(card.get("score") or 0), str(card.get("source")), int(card.get("flowId") or 0))) + top_endpoints = [ + { + "endpoint": endpoint, + "count": count, + "sample": endpoint_samples.get(endpoint), + } + for endpoint, count in by_endpoint.most_common(40) + ] + + next_steps = [ + "Pick a highValueCandidate and inspect exact request/response with the matching getter: source=history/live -> burp_flow_get, source=logger -> burp_logger_flow_get, source=selection -> burp_selection_get.", + "Replay only one variable at a time with burp_replay_flow(source=...) after confirming the baseline response.", + "If a pattern is reusable, promote it into burp_rule_upsert, burp_bcheck_import, or burp_bambda_import instead of keeping ad-hoc manual steps.", + ] + if not host: + next_steps.insert(0, "Pass host='target.example' for a tighter per-unit view; without host this is only a broad recent/history snapshot.") + + return { + "ok": True, + "item": { + "purpose": "Target-centric traffic map across Proxy history, live Proxy buffer, Burp tool/logger traffic and UI selection.", + "filters": { + "host": host, + "text": text, + "path": path, + "sources": selected_sources, + "perSourceLimit": per_source_limit, + "includeStatic": include_static, + "includeExtensions": include_extensions, + }, + "count": len(collected), + "sourceErrors": source_errors, + "bySource": by_source.most_common(), + "byHost": by_host.most_common(30), + "byStatus": by_status.most_common(), + "byMethod": by_method.most_common(), + "byToolType": by_tool_type.most_common(), + "byTag": by_tag.most_common(), + "topEndpoints": top_endpoints, + "highValueCandidates": interesting[:max_candidates], + "nextSteps": next_steps, + }, + } + + +@mcp.tool() +def burp_extension_activity_overview( + after_seq: int = 0, + limit: int = 80, + host: str | None = None, + path: str | None = None, + text: str | None = None, + include_sample_details: bool = False, + sample_limit: int = 5, +) -> dict[str, Any]: + """聚合其他 Burp 扩展产生的 HTTP 流量,帮助 AI 从已加载插件的探测手法中提取线索;不直接调用插件内部接口。""" + data = burp_logger_poll( + after_seq=after_seq, + limit=limit, + text=text, + host=host, + path=path, + tool_type="Extensions", + include_bodies=False, + ) + items = data.get("items", []) + host_counter: Counter[str] = Counter() + path_counter: Counter[str] = Counter() + status_counter: Counter[str] = Counter() + method_counter: Counter[str] = Counter() + candidates: list[dict[str, Any]] = [] + + for item in items: + item_host = item.get("host") or "" + item_path = item.get("path") or "" + host_counter[item_host] += 1 + path_counter[f"{item.get('method') or '?'} {item_path}"] += 1 + method_counter[item.get("method") or ""] += 1 + status = item.get("statusCode") + status_counter[str(status) if status is not None else "pending"] += 1 + if len(candidates) < max(1, min(sample_limit, 20)): + candidates.append( + { + "flowId": item.get("flowId"), + "method": item.get("method"), + "host": item_host, + "path": item_path, + "statusCode": item.get("statusCode"), + "mimeType": item.get("mimeType"), + "tags": item.get("tags"), + } + ) + + sample_details: list[dict[str, Any]] = [] + if include_sample_details: + for candidate in candidates[: max(1, min(sample_limit, 10))]: + flow_id = candidate.get("flowId") + if flow_id is None: + continue + try: + detail = burp_logger_flow_get(flow_id=int(flow_id), include_bodies=False).get("item", {}) + request = detail.get("request") or {} + response = detail.get("response") or {} + sample_details.append( + { + "flowId": flow_id, + "requestStartLine": request.get("startLine"), + "requestHeaderCount": request.get("headerCount"), + "requestBodyBytes": request.get("bodyBytes"), + "responseStartLine": response.get("startLine") if response else None, + "responseContentType": response.get("contentType") if response else None, + "responseBodyBytes": response.get("bodyBytes") if response else None, + } + ) + except Exception as exc: + sample_details.append({"flowId": flow_id, "error": str(exc)}) + + return { + "ok": True, + "item": { + "purpose": "Infer useful testing ideas from HTTP traffic generated by other loaded Burp extensions.", + "directPluginInvocationSupported": False, + "count": len(items), + "latestCursor": data.get("latestCursor"), + "byHost": host_counter.most_common(), + "byPath": path_counter.most_common(30), + "byStatus": status_counter.most_common(), + "byMethod": method_counter.most_common(), + "candidateFlows": candidates, + "sampleDetails": sample_details if include_sample_details else None, + "nextSteps": [ + "Use burp_logger_flow_get(flowId) on candidate flows to inspect exact plugin-generated requests.", + "Replay promising requests with burp_replay_flow(source='logger') or convert patterns into rewrite rules/BChecks/Bambdas.", + "For interface-level control, add a specific adapter only for plugins that expose a stable API or local endpoint." + ], + }, + } + + +@mcp.tool() +def burp_export_flow(flow_id: int, source: str = "live", include_bodies: bool = True, label: str | None = None) -> dict[str, Any]: + """导出一条关键流量为本地 JSON 证据文件,便于归档、复盘或拼接正式漏洞报告。""" + if source == "logger": + detail = burp_logger_flow_get(flow_id=flow_id, include_bodies=include_bodies) + elif source == "selection": + detail = burp_selection_get(flow_id=flow_id, include_bodies=include_bodies) + else: + detail = burp_flow_get(flow_id=flow_id, source=source, include_bodies=include_bodies) + ARTIFACT_ROOT.mkdir(parents=True, exist_ok=True) + safe_label = (label or f"{source}-{flow_id}").replace("/", "_").replace(" ", "-") + output_path = ARTIFACT_ROOT / f"burp-flow-{safe_label}-{_timestamp()}.json" + output_path.write_text(json.dumps(detail, ensure_ascii=False, indent=2), encoding="utf-8") + return {"ok": True, "message": "flow exported", "flowId": flow_id, "source": source, "path": str(output_path)} + + +@mcp.tool() +def burp_replay_flow( + flow_id: int, + source: str = "history", + method: str | None = None, + path: str | None = None, + target_host: str | None = None, + target_port: int | None = None, + use_https: bool | None = None, + headers: dict[str, str] | None = None, + add_headers: dict[str, str] | None = None, + remove_headers: list[str] | None = None, + body: str | None = None, + path_replace_from: str | None = None, + path_replace_to: str | None = None, + body_replace_from: str | None = None, + body_replace_to: str | None = None, + apply_rules: bool = False, + send_to_repeater: bool = False, + repeater_tab_name: str = "AI replay", + include_bodies: bool = True, +) -> dict[str, Any]: + """基于 live/history 里的已有请求进行改包重发。适合 AI 修改 token、body、header、path 后快速验证。""" + if source not in {"live", "history", "logger", "selection"}: + raise ValueError("source 必须是 live、history、logger 或 selection") + payload = { + "id": flow_id, + "source": source, + "applyRules": apply_rules, + "sendToRepeater": send_to_repeater, + "repeaterTabName": repeater_tab_name, + "includeBodies": include_bodies, + } + payload.update( + _edits_payload( + method=method, + path=path, + target_host=target_host, + target_port=target_port, + use_https=use_https, + headers=headers, + add_headers=add_headers, + remove_headers=remove_headers, + body=body, + path_replace_from=path_replace_from, + path_replace_to=path_replace_to, + body_replace_from=body_replace_from, + body_replace_to=body_replace_to, + ) + ) + return _request_json("/api/replay/flow", method="POST", payload=payload) + + +@mcp.tool() +def burp_send_raw_request( + raw_request: str, + target_host: str, + target_port: int, + use_https: bool = False, + apply_rules: bool = False, + send_to_repeater: bool = False, + repeater_tab_name: str = "AI raw replay", + include_bodies: bool = True, +) -> dict[str, Any]: + """直接发送原始 HTTP 请求文本。适合 AI 组合完整数据包后通过 Burp 内部 HTTP 栈重放。""" + return _request_json( + "/api/replay/raw", + method="POST", + payload={ + "rawRequest": raw_request, + "targetHost": target_host, + "targetPort": target_port, + "useHttps": use_https, + "applyRules": apply_rules, + "sendToRepeater": send_to_repeater, + "repeaterTabName": repeater_tab_name, + "includeBodies": include_bodies, + }, + ) + + +@mcp.tool() +def burp_rules_list() -> dict[str, Any]: + """列出当前启用/禁用的自动请求/响应改写规则,包含 actionSchema 说明 modify/drop/spoof 与 applyTo 语义。""" + return _request_json("/api/rules") + + +@mcp.tool() +def burp_rule_upsert( + direction: str, + action: str = "modify", + apply_to: str = "proxy", + name: str | None = None, + rule_id: str | None = None, + enabled: bool = True, + match_host_contains: str | None = None, + match_path_contains: str | None = None, + match_method: str | None = None, + match_body_contains: str | None = None, + match_status_min: int | None = None, + match_status_max: int | None = None, + method: str | None = None, + path: str | None = None, + target_host: str | None = None, + target_port: int | None = None, + use_https: bool | None = None, + headers: dict[str, str] | None = None, + add_headers: dict[str, str] | None = None, + remove_headers: list[str] | None = None, + body: str | None = None, + path_replace_from: str | None = None, + path_replace_to: str | None = None, + body_replace_from: str | None = None, + body_replace_to: str | None = None, + status_code: int | None = None, + reason_phrase: str | None = None, +) -> dict[str, Any]: + """新增或更新自动规则。action=modify 改写并放行;action=drop 丢弃匹配消息;action=spoof 用规则字段构造替代响应;apply_to=proxy/tool/all 控制作用面。""" + if direction not in {"request", "response"}: + raise ValueError("direction 必须是 request 或 response") + if action not in {"modify", "drop", "spoof"}: + raise ValueError("action 必须是 modify、drop 或 spoof") + if apply_to not in {"proxy", "tool", "all"}: + raise ValueError("apply_to 必须是 proxy、tool 或 all") + payload = { + "id": rule_id, + "name": name, + "direction": direction, + "action": action, + "applyTo": apply_to, + "enabled": enabled, + "matchHostContains": match_host_contains, + "matchPathContains": match_path_contains, + "matchMethod": match_method, + "matchBodyContains": match_body_contains, + "matchStatusMin": match_status_min, + "matchStatusMax": match_status_max, + } + payload.update( + _edits_payload( + method=method, + path=path, + target_host=target_host, + target_port=target_port, + use_https=use_https, + headers=headers, + add_headers=add_headers, + remove_headers=remove_headers, + body=body, + path_replace_from=path_replace_from, + path_replace_to=path_replace_to, + body_replace_from=body_replace_from, + body_replace_to=body_replace_to, + status_code=status_code, + reason_phrase=reason_phrase, + ) + ) + return _request_json("/api/rules", method="POST", payload=payload) + + +@mcp.tool() +def burp_rule_delete(rule_id: str) -> dict[str, Any]: + """删除一条自动改写规则。""" + return _request_json(f"/api/rules/{urllib.parse.quote(rule_id, safe='')}", method="DELETE") + + +@mcp.tool() +def burp_bcheck_import(content: str | None = None, path: str | None = None, replace_existing: bool = True) -> dict[str, Any]: + """导入 AI 生成或本地文件中的 BCheck,自定义扩展 Burp Scanner 检查项。content/path 二选一;导入后由 Burp Scanner 官方流程执行。""" + if not content and not path: + raise ValueError("content 或 path 必须提供一个") + if content and path: + raise ValueError("content 和 path 只能提供一个") + if path: + content = Path(path).read_text(encoding="utf-8") + return _request_json( + "/api/scanner/bchecks/import", + method="POST", + payload={"content": content, "replaceExisting": replace_existing}, + ) + + +@mcp.tool() +def burp_bambda_import(content: str | None = None, path: str | None = None) -> dict[str, Any]: + """导入 AI 生成或本地文件中的 Bambda 到 Burp Bambda Library。适合把自定义行动/列/过滤器脚本交给 Burp UI 管理和执行。""" + if not content and not path: + raise ValueError("content 或 path 必须提供一个") + if content and path: + raise ValueError("content 和 path 只能提供一个") + if path: + content = Path(path).read_text(encoding="utf-8") + return _request_json( + "/api/bambdas/import", + method="POST", + payload={"content": content}, + ) + + +@mcp.tool() +def burp_mcp_list(section: str = "index", topic: str | None = None, detail: bool = False) -> dict[str, Any]: + """分级列出 BurpSuite MCP Bridge 的工具用法。先 section=index 获取目录,再按 section/topic 拉取小块说明,避免一次性占满上下文。""" + catalog = _mcp_help_catalog() + if section in {"", "index", "root"}: + return { + "ok": True, + "item": { + "usage": "Call burp_mcp_list(section='
') for topics, then burp_mcp_list(section='
', topic='', detail=true) for focused usage.", + "sections": [ + {"section": key, "summary": value["summary"], "topics": list(value["topics"].keys())} + for key, value in catalog.items() + ], + }, + } + if section not in catalog: + return {"ok": False, "message": f"unknown section: {section}", "availableSections": list(catalog.keys())} + section_data = catalog[section] + if not topic: + return { + "ok": True, + "item": { + "section": section, + "summary": section_data["summary"], + "topics": [ + {"topic": key, "summary": value["summary"]} + for key, value in section_data["topics"].items() + ], + }, + } + topics = section_data["topics"] + if topic not in topics: + return {"ok": False, "message": f"unknown topic: {topic}", "availableTopics": list(topics.keys())} + topic_data = topics[topic] + item = {"section": section, "topic": topic, "summary": topic_data["summary"]} + if detail: + item.update(topic_data) + else: + item["hint"] = "Set detail=true for parameters and examples." + return {"ok": True, "item": item} + + +def _mcp_help_catalog() -> dict[str, Any]: + return { + "traffic": { + "summary": "Read low-noise target-centric, Proxy live/history/logger-like traffic.", + "topics": { + "target": { + "summary": "Target-centric map across history/live/logger/selection, grouped by host/status/method/tool/endpoint with high-value candidates.", + "tools": ["burp_target_overview", "burp_flow_get", "burp_logger_flow_get", "burp_selection_get"], + "params": {"host": "recommended target host filter", "sources": "all or comma-separated history,live,logger,selection", "include_extensions": "keep plugin-generated target traffic in the same target view"}, + "example": "burp_target_overview(host='example.com', sources='all', limit=80) -> inspect one highValueCandidate", + }, + "live": { + "summary": "Incremental Proxy buffer triage.", + "tools": ["burp_live_overview", "burp_live_poll", "burp_flow_get"], + "params": {"after_seq": "cursor from latestCursor", "include_bodies": "default false for triage"}, + "example": "burp_live_overview(after_seq=0, limit=80) -> burp_flow_get(flow_id, source='live')", + }, + "history": { + "summary": "Search persisted Proxy history without replaying traffic.", + "tools": ["burp_history_search", "burp_flow_get"], + "params": {"query": "plain text or regex", "host_contains/path_contains/status_min/status_max": "narrow filters"}, + "example": "burp_history_search(host_contains='example.com', path_contains='/api', limit=20)", + }, + "logger": { + "summary": "Read Burp internal HTTP tool traffic captured after extension load.", + "tools": ["burp_logger_overview", "burp_logger_poll", "burp_logger_flow_get"], + "params": {"tool_type": "Repeater/Intruder/Scanner/Extensions etc."}, + "example": "burp_logger_poll(tool_type='Repeater', limit=20)", + }, + "selection": { + "summary": "Read items captured from Burp UI selection via command palette/hotkey.", + "tools": ["burp_selection_poll", "burp_selection_get", "burp_flow_get"], + "params": {"source": "Use source='selection' with flow_get/replay/export/send_to_repeater."}, + "example": "Trigger 'Burp MCP Bridge: Capture selection for AI' in Burp, then burp_selection_poll(limit=20)", + }, + }, + }, + "replay": { + "summary": "Replay captured or raw requests with controlled mutations.", + "topics": { + "flow": { + "summary": "Replay live/history request with method/path/header/body edits.", + "tools": ["burp_replay_flow"], + "params": {"apply_rules": "run rewrite rules before/after replay", "send_to_repeater": "also open request in Repeater"}, + "example": "burp_replay_flow(flow_id=123, source='history', path_replace_from='id=1', path_replace_to='id=2')", + }, + "raw": { + "summary": "Send a fully assembled raw HTTP request through Burp HTTP stack.", + "tools": ["burp_send_raw_request"], + "params": {"target_host/target_port/use_https": "required target service"}, + "example": "burp_send_raw_request(raw_request='GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n', target_host='example.com', target_port=443, use_https=True)", + }, + }, + }, + "rules": { + "summary": "Automatic request/response controls with proxy/tool/all scope.", + "topics": { + "actions": { + "summary": "modify/drop/spoof semantics.", + "tools": ["burp_rules_list", "burp_rule_upsert", "burp_rule_delete"], + "params": {"action": "modify|drop|spoof", "direction": "request|response", "apply_to": "proxy|tool|all"}, + "example": "burp_rule_upsert(direction='request', action='spoof', apply_to='proxy', match_host_contains='example.com', body='mock')", + }, + "scope": { + "summary": "apply_to controls where rules run.", + "details": [ + "proxy: default; only browser/client traffic through Burp Proxy.", + "tool: Burp internal tools such as Repeater/Intruder/Scanner when Montoya runtime supports it; extension self-replay is skipped to avoid double-apply.", + "all: proxy + supported internal tools." + ], + }, + }, + }, + "evidence": { + "summary": "Export decisive flows without stuffing large bodies into MCP context.", + "topics": { + "export": { + "summary": "Write JSON or raw request/response bundles to disk.", + "tools": ["burp_export_flow", "burp_export_flow_bundle"], + "params": {"source": "live|history|logger", "include_bodies": "use false for huge flows unless needed"}, + }, + }, + }, + "official": { + "summary": "Runtime-detected Montoya 2026.4.x integrations.", + "topics": { + "selection": { + "summary": "Command palette / HotKey bridge from Burp UI selection to MCP.", + "tools": ["burp_selection_poll", "burp_selection_get"], + "details": [ + "Registers 'Burp MCP Bridge: Capture selection for AI' where supported.", + "Captured items become source=selection so existing replay/export/repeater tools can operate on them.", + "Default hotkey is Ctrl+Alt+M; command palette discovery is preferred if a shortcut conflicts." + ], + }, + "compat": { + "summary": "Check which optional official APIs are available in the loaded Burp runtime.", + "tools": ["burp_bridge_status", "burp_config_get"], + "details": [ + "montoyaCompat.runtimeOptionalFeatures.httpHandlerRequestDropSpoof: official internal-tool request drop/spoof.", + "hotKeyApi / hotKeySelectedRequestResponses: command-palette/hotkey groundwork for later selected-row workflows." + ], + }, + }, + }, + "scanner": { + "summary": "Lightweight Scanner control-plane helpers; leave heavy scanning to Burp official Scanner.", + "topics": { + "bcheck": { + "summary": "Import AI-generated or local BCheck content into Burp Scanner custom checks.", + "tools": ["burp_bcheck_import"], + "params": {"content": "BCheck source text", "path": "local MCP-side .bcheck file", "replace_existing": "default true"}, + "example": "burp_bcheck_import(path='/tmp/check.bcheck', replace_existing=True)", + }, + }, + }, + "bambda": { + "summary": "Import scripts into Burp Bambda Library; Burp UI remains the execution surface.", + "topics": { + "import": { + "summary": "Import AI-generated or local Bambda content for custom actions, columns, filters, match/replace, or scan checks.", + "tools": ["burp_bambda_import"], + "params": {"content": "Bambda script text", "path": "local MCP-side Bambda file"}, + "example": "burp_bambda_import(path='/tmp/custom-action.bambda')", + }, + "strategy": { + "summary": "Use MCP as a script delivery/control plane, not a duplicate Bambda executor.", + "details": [ + "Custom actions in Repeater are Bambda-backed workflows; import the script, then run it in Burp where the selected message/context exists.", + "For quick AI automation on captured traffic, prefer MCP replay/rules/selection. For reusable UI workflows, import a Bambda.", + "Bambda scripts can execute code, so keep imports explicit and review generated scripts before enabling them broadly." + ], + }, + }, + }, + "plugins": { + "summary": "Observe and learn from other loaded Burp extensions without pretending to call their private APIs.", + "topics": { + "activity": { + "summary": "Cluster HTTP traffic generated by other extensions and return candidate flowIds for AI analysis.", + "tools": ["burp_extension_activity_overview", "burp_logger_flow_get", "burp_replay_flow"], + "params": {"tool_type": "Internally uses logger-like ToolType=Extensions", "include_sample_details": "small metadata only, no large bodies"}, + "example": "burp_extension_activity_overview(limit=80, include_sample_details=True)", + }, + "adapters": { + "summary": "Interface-level plugin control is only realistic for known/cooperating plugins.", + "details": [ + "Generic Montoya does not expose a stable API to enumerate and call arbitrary extension internals.", + "Best generic path: observe extension-generated traffic and convert useful behavior into MCP replay/rules/BChecks/Bambdas.", + "For specific plugins that expose local HTTP/RPC/config files, add explicit adapter tools instead of unsafe reflection." + ], + }, + }, + }, + } + + +if __name__ == "__main__": + try: + parser = argparse.ArgumentParser(description="BurpSuite MCP Bridge server") + parser.add_argument("--transport", choices=["stdio", "streamable-http", "sse"], default=MCP_TRANSPORT) + parser.add_argument("--host", default=MCP_SERVER_HOST, help="Host for HTTP MCP transports") + parser.add_argument("--port", type=int, default=MCP_SERVER_PORT, help="Port for HTTP MCP transports") + parser.add_argument("--path", default=MCP_SERVER_PATH, help="Path for Streamable HTTP MCP transport") + args = parser.parse_args() + + mcp.settings.host = args.host + mcp.settings.port = args.port + mcp.settings.streamable_http_path = args.path + mcp.run(transport=args.transport) + except Exception as exc: # pragma: no cover + print(f"[burpsuite-mcp-bridge] fatal: {exc}", file=os.sys.stderr) + raise diff --git a/scripts/generate_plugins_json.py b/scripts/generate_plugins_json.py index 0923e96f..a739b1ad 100644 --- a/scripts/generate_plugins_json.py +++ b/scripts/generate_plugins_json.py @@ -52,6 +52,9 @@ "mturac/everything-openai-codex", } EXTRA_MIRROR_PATHS = { + # BurpSuite MCP Bridge needs its Python MCP runtime and Burp extension JARs + # mirrored with the plugin manifest so marketplace installs are usable. + "6jeffr3y/burpsuite-mcp-bridge": ("wsl-mcp", "burp-plugin"), # Staff Engineer Mode exposes one router skill and loads routed specialist # files from a top-level specialists/ directory at runtime. "sirmarkz/staff-engineer-mode": ("specialists",),