内容
# xdbg — 让 AI 为你的 PHP 进行调试,实时在 Docker 中
你在一个 Docker 容器中运行 PHP。API 端点出现了一个 bug — 一个带有 JSON 正文和 `Authorization: Bearer …` 头部的 POST 请求。你打开 PhpStorm,点击“监听调试连接”,从 Postman 发送一个请求,打断点,逐步调试,检查,评估,修复。现在想象所有这些操作**都不需要离开聊天窗口** — 你向 Claude Code、opencode、Cursor 或任何支持 MCP 的代理描述 bug,代理为你调试:设置断点,发送真实请求,读取堆栈,检查变量,逐步调试,评估,提出修复方案。这就是 xdbg。
**xdbg 是一个 MCP 服务器,连接到在 Docker 容器中运行的 Xdebug,并将其作为工具暴露** — 因此任何支持 MCP 的 AI 代理(Claude Code、opencode、Cursor)都可以发送 HTTP 请求(`curl` 风格)并在容器内运行 CLI 命令,然后驱动生成的调试会话:断点,堆栈,变量,评估,逐步调试。所有操作都来自聊天窗口。

## 为什么不使用 PhpStorm 的 MCP 工具?
PhpStorm 自带 Xdebug MCP 工具,但它们只支持发送 **GET** 请求,不允许设置 **头部**(cookie,身份验证令牌,`Content-Type`)。对于真正的 API 工作 — POST/PUT/PATCH 请求,带有 JSON 正文和 JWT/身份验证头部 — 你最终需要同时处理端口转发和第二个终端。xdbg 弥补了这一空白:提供相同的 MCP 驱动流程,但具有对方法,头部和正文的完全控制,以及 CLI/Symfony 命令调试和主机↔容器路径转换。
## 它解决了什么问题
| 痛点 | 之前 | 使用 xdbg |
|---|---|---|
| **AI 无法调试 POST/PUT/PATCH 请求** | PhpStorm MCP 仅支持 GET 请求 | `xdbg_request` 支持 `method`,`headers`,`body` |
| **身份验证 / cookie / JWT** | 没有地方放置头部 | 传递 `headers: {"Authorization": "Bearer …"}` (或从文件中读取以防止密钥暴露在聊天窗口中) |
| **CLI / Symfony 命令** | 没有 MCP 路径 | `xdbg_run_command "bin/console app:foo"` 在断点处暂停 |
| **主机↔容器路径** | 断点需要容器路径;堆栈显示容器路径 | 使用主机路径设置断点;堆栈返回主机路径 |
| **端口冲突** | 两个调试器争夺端口 9003 | 检测端口占用者(lsof),等待端口释放,通知你谁正在阻止 |
| **端口 9003 总是被占用** | 调试器占用端口,直到会话结束 | 仅在工具调用期间绑定端口(`xdbg_request`,`xdbg_run_command`,`xdbg_listen`);调用结束后立即释放 — PhpStorm,浏览器 Xdebug 和其他工具可以自由工作 |
## 它是如何工作的(30 秒版本)
1. 容器的 Xdebug 是一个 DBGp **引擎**。使用 `xdebug.start_with_request=yes`,它在每个请求时**向外**拨打 `host.docker.internal:9003` 并等待命令。
2. `xdbg` 监听 `0.0.0.0:9003` 并驱动引擎 — 设置断点,逐步调试,评估,读取堆栈。
3. MCP 服务器是一个长寿命进程,因此会话在工具调用之间保持活跃。你的代理设置断点,发送请求,检查变量,逐步调试 — 所有操作都在一个对话中。
4. DBGp 端口是**临时**的 — 仅在工具调用期间绑定,调用结束后立即释放。 在调用之间,端口 9003 可供 PhpStorm,浏览器 Xdebug 或其他调试器使用。

(手绘 SVG — 如果需要调整布局,请直接编辑 `docs/xdbg-overview.svg`。)
有关完整的请求往返过程和逐步序列,请参阅[架构图](docs/xdbg-architecture.svg)([源代码](docs/architecture.puml))。
## 安装
> **最快捷的方式:** 将以下内容粘贴到与 Claude Code 或 opencode 的聊天窗口中:
>
> ```
> 为我安装和配置这个 MCP 服务器:https://raw.githubusercontent.com/crazy-goat/xdbg/main/install.md
> ```
>
> 代理将读取指南并引导您完成每个步骤。
>
> xdbg 还提供了一个**专用的 AI 技能**(`skills/xdebug-docker-debug/SKILL.md`),为代理提供调试工作流、故障恢复和最佳实践的上下文,使采用过程更加顺畅。代理将在安装前向您询问。
>
> 完整指南:[install.md](install.md)
### `go install` (推荐 — 一行命令,无需克隆)
```bash
go install github.com/crazy-goat/xdbg@latest
```
> **注意:** `proxy.golang.org` 会缓存版本几分钟。如果命令因模块路径不匹配而失败,请直接安装最新提交:
> ```bash
> GOPROXY=direct go install github.com/crazy-goat/xdbg@main
> ```
这将 `xdbg` 安装在 `$(go env GOPATH)/bin` (默认为 `~/go/bin`)。将其添加到你的 `PATH` 中(一次性):
```bash
export PATH="$(go env GOPATH)/bin:$PATH" # add to ~/.zshrc / ~/.bashrc
```
验证:`xdbg --help`。
### 从本地克隆
```bash
git clone https://github.com/crazy-goat/xdbg.git
cd xdbg
make install # 构建并复制到 ~/.local/bin/xdbg
```
(`make build` 生成 `./xdbg` 而不安装。)
### 先决条件
- Go 1.21+ (仅 `go install` / `make` 需要)
- Docker(或运行 PHP 应用程序的任何容器运行时)
- Xdebug **在容器内**(引擎)安装,按需启用
## 配置
xdbg 是一个 MCP **stdio** 服务器:代理将其作为子进程启动,并通过 stdin/stdout 进行 JSON-RPC 通信。你在代理的 MCP 配置中注册它一次。
### opencode
在 `~/.config/opencode/opencode.json` (或项目的 `opencode.json`)中添加一个 `mcp` 下的条目:
```jsonc
{
"mcp": {
"xdbg": {
"enabled": true,
"type": "local",
"command": [
"xdbg", "mcp",
"--dbg-port", "9003",
"--local-root", "/Users/you/work/your-app",
"--docker-root", "/var/www/your-app",
"--xdebug-enable-cmd", "docker compose exec -T php set-xdebug-on",
"--xdebug-disable-cmd", "docker compose exec -T php set-xdebug-off",
"--xdebug-status-cmd", "docker compose exec -T php get-xdebug-status",
"--container-exec", "docker compose exec -T php"
]
}
}
}
```
重启 opencode(或重新连接 MCP)。工具显示为 `xdbg_*`。
### Claude Code (`.mcp.json`)
在项目根目录(或 `~/.claude.json` 用于全局)中添加一个 `.mcp.json` 文件:
```json
{
"mcpServers": {
"xdbg": {
"command": "xdbg",
"args": [
"mcp",
"--dbg-port", "9003",
"--local-root", "/Users/you/work/your-app",
"--docker-root", "/var/www/your-app",
"--xdebug-enable-cmd", "docker compose exec -T php set-xdebug-on",
"--xdebug-disable-cmd", "docker compose exec -T php set-xdebug-off",
"--xdebug-status-cmd", "docker compose exec -T php get-xdebug-status",
"--container-exec", "docker compose exec -T php"
]
}
}
}
```
在 Claude Code 中重新连接 MCP。工具显示为 `mcp__xdbg__*`。
### 标志参考
| 标志 | 默认值 | 用途 |
|---|---|---|
| `--dbg-port` | `9003` | Xdebug 拨打的端口(监听器绑定 `0.0.0.0:<port>`) |
| `--local-root` | — | 主机项目根目录(用于路径转换) |
| `--docker-root` | — | 容器项目根目录(用于路径转换) |
| `--xdebug-enable-cmd` | — | 在容器中启用 Xdebug 的 shell 命令 |
| `--xdebug-disable-cmd` | — | 在容器中禁用 Xdebug 的 shell 命令 |
| `--xdebug-status-cmd` | — | 在容器中检查 Xdebug 状态的 shell 命令 |
| `--container-exec` | `docker compose exec -T php` | 在容器内运行 CLI 命令的前缀 |
### 让代理为你切换 Xdebug
Xdebug 默认应关闭以提高性能。当你配置 `--xdebug-enable-cmd`、`--xdebug-disable-cmd` 和 `--xdebug-status-cmd` 时,代理会获得三个 MCP 工具 — `xdbg_container_enable`、`xdbg_container_disable` 和 `xdbg_container_status` — 并且可以在调试会话期间自行切换 Xdebug,无需 shell 访问。
## 工具(`xdbg_*`)
### `xdbg_status()`
返回当前调试器状态(`no session`、`started`、`break`、`stopping`),暂停的文件和行(或暂停时为 `-`),以及队列中的断点数。将其用作发送请求或运行命令后检查会话是否被采用以及引擎停止的位置的第一个调用。它随时都可以安全调用,有或没有活动会话。它不会推进执行或改变状态。
### `xdbg_set_breakpoint(string file, int line)`
`file` 是主机路径(绝对或项目相对路径,自动翻译到容器路径);`line` 是 1-based。设置行断点。如果已经有活动会话,则断点立即应用并返回引擎分配的 ID。如果没有活动会话,则断点排队并在下一个会话(下一个 `xdbg_request` 或 `xdbg_run_command`)时自动应用。可以设置多个断点,然后触发请求;所有断点在引擎连接时都会被应用。
### `xdbg_breakpoint_list()`
列出引擎已知的所有断点及其 ID、状态(`enabled`/`disabled`)、文件(翻译回主机路径)和行。如果没有活动会话,则列出排队的断点。用于验证在发送请求之前已设置了哪些断点。随时都可以安全调用。
### `xdbg_breakpoint_remove(string id)`
`id` 是 `xdbg_set_breakpoint` 返回的断点 ID 或 `xdbg_breakpoint_list` 显示的 ID。删除单个断点 — 如果有活动会话,则从引擎中删除(如果有);如果没有活动会话,则从本地队列中删除。成功时返回 `removed <id>`。
### `xdbg_breakpoint_clear()`
删除所有断点:排队(尚未应用)和应用(在引擎中)。如果有或没有活动会话,都可以安全调用。返回已清除的断点数。
### `xdbg_request(string url, string? method, map<string,string>? headers, string? body, int? timeoutMs)`
`url` 必需;`method` 默认为 `GET`;`headers` 是 string→string 对象;`body` 是原始字符串;`timeoutMs` 默认为 15000。向应用程序发送 HTTP 请求,对方法、头部和正文具有完全控制权。由于容器具有 `xdebug.start_with_request=yes`,请求使 php-fpm 拨打 DBGp 端口;xdbg 采用连接,应用排队的断点,并在第一个断点处暂停。当没有设置断点时,脚本运行到完成,请求返回。要交互式调试,请先设置断点,然后调用 `xdbg_request` — 工具返回会话暂停时,你可以通过 `xdbg_run` / `xdbg_step_*` 等进行驱动。
### `xdbg_request_from_files(string url, string? method, string? headers_file, string? body_file, int? timeoutMs)`
`url` 必需;`headers_file` 是 `Name: Value` 行的文件路径(空白行和 `#` 注释将被忽略 — 或 JSON 对象);`body_file` 是原始正文字节的文件路径;`timeoutMs` 默认为 15000。类似于 `xdbg_request`,但从磁盘读取头部和正文。用于头部包含敏感值(JWT 令牌、cookie、API 密钥)不应在聊天窗口或工具参数中显示的情况。文件在调用时一次性读取。
### `xdbg_listen(int? timeoutMs)`
`timeoutMs` 默认为 30000。启用 DBGp 监听器并阻塞,直到下一个引擎连接被采用,然后返回。用于 CLI / Symfony 命令调试:先调用 `xdbg_listen`,然后单独启动命令(例如 `docker compose exec -T php php bin/console app:cmd`)。一旦工具返回,会话在脚本开始时暂停,并应用断点 — 通过 `xdbg_run` / `xdbg_step_*` 等驱动。如果在超时时间内没有引擎连接,则返回错误。
### `xdbg_run_command(string command, int? timeoutMs)`
`command` 必需(例如 `"bin/console app:my-command --option=value"`);`timeoutMs` 默认为 30000。在容器内运行命令(前缀为 `--container-exec`)并等待生成的 Xdebug 连接。当没有断点时,脚本运行到完成,命令输出被返回。当设置断点时,会话在第一个断点处暂停,调用者通过 `xdbg_run` / `xdbg_step_*` 等驱动 — 命令输出直到脚本完成才可用。这是 CLI 等效于 `xdbg_request`.
### `xdbg_run()`
从断点处恢复执行 - 引擎运行直到下一个断点或脚本完成。返回新状态(`break`/`stopping`)和当前位置。当脚本完成时,状态变为 `stopping`,响应注明 `(script finished)`。重复调用它以逐步执行。
### `xdbg_step_into()`
进入下一行 — 如果下一行是函数调用,执行暂停在调用函数的第一行。返回新状态和位置。像 `xdbg_step_over` 一样,如果没有可进入的内容。
### `xdbg_step_over()`
跳过下一行 — 如果该行是函数调用,函数运行到完成,执行暂停在调用者的下一行。返回新状态和位置。像这样推进而不进入调用者。
### `xdbg_step_out()`
退出当前函数 — 执行运行直到当前函数返回,然后在调用者处暂停。返回新状态和位置。像这样逃离你错误地进入的函数。
### `xdbg_pause()`
立即中断(暂停)执行,就像在当前位置设置断点一样。返回新状态(`break`)和位置。仅在活动会话时有意义。
### `xdbg_stack()`
返回当前暂停点的调用栈,每个帧的深度、函数名、文件(翻译为主机路径)和行。返回 `(no stack — not paused?)` 时没有暂停的会话。随时都可以安全调用,但仅在暂停时有意义。
### `xdbg_context(int? stackDepth)`
`stackDepth` 默认为 0(顶层帧)。返回给定栈帧的作用域内的变量及其名称、类型和总结值。用于检查暂停点处的本地状态。对于嵌套结构,摘要显示类型和子计数(例如 `object {3 children}`)
使用它来了解你是如何到达当前位置的。随时都可以安全调用,但仅在暂停时有意义。
### `xdbg_eval(string expression)`
`expression` 是 PHP 表达式(例如 `$foo->bar()` 或 `count($items)`)。在当前作用域内评估表达式并返回结果。如果表达式抛出错误,则返回错误。
### `xdbg_property_get(string name, int? stackDepth)`
`name` 是变量名(例如 `$foo`);`stackDepth` 默认为 0。返回给定栈帧中单个变量或属性的值。用于深入查看 `xdbg_context` 中看到的变量 — 对于嵌套结构,它返回子属性。返回 `(not found)` 当名称在作用域内不存在时。
### `xdbg_property_set(string name, string value)`
`name` 是变量名;`value` 是 PHP 字面量(例如 `"bar"` 或 `42`)。在当前作用域内将变量设置为给定值。用于测试代码在不同输入的情况下表现如何,无需编辑源代码。值被 base64 编码,并通过 DBGp `property_set` 命令发送。返回 `<name> = <value>` 成功。
### `xdbg_detach()`
从引擎分离:让脚本自行完成并丢弃会话。DBGp 监听器关闭,释放端口 9003。用于调试完成但仍需要请求/命令完成的情况。返回 `detached`。
### `xdbg_stop()`
立即停止调试脚本 — 引擎终止 PHP 进程,会话结束。监听器关闭,释放端口 9003。用于中止卡住的请求或命令。返回 `stopped`。
### `xdbg_container_status()` / `xdbg_container_enable()` / `xdbg_container_disable()`
无参数。可选 `--xdebug-*-cmd` 标志配置时可用。 `_status` 运行状态命令并返回其输出(例如 Xdebug 当前开启或关闭)。 `_enable` / `_disable` 运行启用/禁用命令,在调试会话期间在容器内切换 Xdebug,无需 shell 访问。
## 典型流程
**Web(POST/GET/...)** — 工具发送请求本身:
1. `xdbg_container_status` — 检查 Xdebug 在容器中已启用
2. `xdbg_container_enable` — 如果已禁用则启用
3. `xdbg_set_breakpoint` `{file:"src/.../FooController.php", line:42}`
4. `xdbg_request` `{url:"http://127.0.0.1:8090/api/foo", method:"POST", headers:{"Content-Type":"application/json","Authorization":"Bearer …"}, body:"{…}"}` → 在 `FooController:42` 处中断
5. `xdbg_stack` / `_context` / `_eval` / `_step_*` / `_run` — 检查和逐步调试
6. `xdbg_detach` 或 `xdbg_stop` — 结束会话并释放端口 9003
7. `xdbg_container_disable` — 关闭 Xdebug(恢复容器性能)
**CLI / Symfony 命令(手动启动):**
1. `xdbg_container_status` — 检查 Xdebug 在容器中已启用
2. `xdbg_container_enable` — 如果已禁用则启用
3. `xdbg_set_breakpoint` …
4. `xdbg_listen` (启用;返回引擎连接时)
5. 单独启动:`docker compose exec -T php php bin/console app:cmd`
6. 使用 `_run` / `_step_*` / `_stack` / `_context` / `_eval` 驱动
7. `xdbg_detach` 或 `xdbg_stop` — 结束会话并释放端口 9003
8. `xdbg_container_disable` — 关闭 Xdebug(恢复容器性能)
**CLI / Symfony 命令(代理驱动):**
1. `xdbg_container_status` — 检查 Xdebug 在容器中已启用
2. `xdbg_container_enable` — 如果已禁用则启用
3. `xdbg_set_breakpoint` …
4. `xdbg_run_command` `{command:"bin/console app:my-command --option=value"}` → 在断点处暂停
5. `xdbg_stack` / `_context` / `_eval` / `_step_*` / `_run` — 检查和逐步调试
6. `xdbg_detach` 或 `xdbg_stop` — 结束会话并释放端口 9003
7. `xdbg_container_disable` — 关闭 Xdebug(恢复容器性能)
## 许可证
MIT — 参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器