Skip to content

Commit 64cccbe

Browse files
authored
Merge pull request #85 from koriym/claude/observation-suggestions-721dka
Clean up stray files, unify prepend filters, fix stale CLAUDE.md references
2 parents 88f69d7 + 410900d commit 64cccbe

9 files changed

Lines changed: 153 additions & 165 deletions

CLAUDE.md

Lines changed: 39 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,15 @@ php -dxdebug.mode=debug tests/fixtures/debug_test.php # Run PHP script with X
9090

9191
### Core Components
9292

93-
- **McpServer.php**: Main MCP protocol handler that processes JSON-RPC requests and delegates to XdebugClient
93+
- **McpServer.php**: Main MCP protocol handler that processes JSON-RPC requests and delegates to the debug/trace/profile/coverage components
9494
- Implements multiple MCP tools across debugging, profiling, and coverage categories
9595
- Handles JSON-RPC 2.0 protocol validation and routing
9696
- Supports debug mode via MCP_DEBUG environment variable
97-
- **XdebugClient.php**: Xdebug protocol client that communicates directly with Xdebug via sockets
98-
- Socket-based communication with Xdebug daemon
97+
- **DebugServer.php**: DBGp debug server that listens on a TCP socket and drives interactive step debugging with Xdebug
98+
- Socket-based DBGp communication with Xdebug
9999
- XML response parsing and transaction management
100-
- Connection lifecycle and error handling
100+
- Connection lifecycle and session management (see "Interactive Step Debugging Workflow" below)
101+
- **XdebugRunner.php / XdebugTracer.php / XdebugProfiler.php**: Spawn PHP with the appropriate Xdebug mode (trace/profile/coverage) for the non-interactive CLI tools
101102
- **bin/xdebug-mcp**: Executable entry point that instantiates and runs McpServer
102103
- CLI interface with argument parsing
103104
- Standard input/output handling for MCP protocol
@@ -136,8 +137,8 @@ The server exposes multiple tools via MCP across main categories:
136137

137138
### Architecture Flow
138139
1. MCP client sends JSON-RPC requests to McpServer
139-
2. McpServer validates and routes tool calls to XdebugClient methods
140-
3. XdebugClient communicates with Xdebug via socket protocol
140+
2. McpServer validates and routes tool calls to the relevant component (DebugServer for interactive debugging, XdebugRunner/Tracer/Profiler for trace/profile/coverage)
141+
3. That component communicates with Xdebug (DBGp socket for debugging, or a spawned PHP process with Xdebug enabled for trace/profile/coverage)
141142
4. Results are returned through MCP protocol back to client
142143

143144
### Testing Infrastructure
@@ -283,12 +284,11 @@ The Xdebug trace functionality enables AI assistants to analyze detailed executi
283284

284285
**Quick Trace Testing**
285286
```bash
286-
# Run comprehensive trace tests
287-
./bin/xtrace
287+
# Trace any PHP script (vendor code excluded by default)
288+
./bin/xtrace tests/fixtures/debug_test.php
288289

289-
# Individual trace testing methods
290-
php -dzend_extension=xdebug -dxdebug.mode=trace bin/simple-trace-test.php
291-
php -dzend_extension=xdebug -dxdebug.mode=trace bin/mcp-trace-test.php
290+
# Direct Xdebug invocation, equivalent to what xtrace runs under the hood
291+
php -dzend_extension=xdebug -dxdebug.mode=trace tests/fixtures/debug_test.php
292292
```
293293

294294
**MCP-based Trace Collection**
@@ -320,7 +320,7 @@ Trace files (`.xt` format) contain structured execution data:
320320
- **Parameters**: Function arguments and values
321321

322322
**Example Trace Output:**
323-
```
323+
```text
324324
Level Func ID Time Index Memory Function Name User Def Filename Line Params
325325
0 1 0.0001 384000 {main} 1 tests/fixtures/debug.php 1
326326
1 2 0.0002 384100 fibonacci 1 tests/fixtures/debug.php 15 $n = 8
@@ -479,7 +479,7 @@ When MCP tools exceed 10% of context, Claude Code's Tool Search feature dynamica
479479
**For Interactive Step Debugging:**
480480
- User: "Debug this code", "Set breakpoints", "Step through execution", "Inspect variables"
481481
- AI automatically runs: `./bin/xstep path/to/file.php`
482-
- **IMPORTANT**: Requires XdebugClient to be listening first (see Step Debugging Workflow below)
482+
- **IMPORTANT**: `./bin/xstep` is a single command that internally builds a DebugServer and spawns the target, so no separate listener is required (see Step Debugging Workflow below)
483483

484484
**For Execution Flow Analysis:**
485485
- User: "Trace execution", "Show function calls", "Analyze execution flow"
@@ -708,7 +708,7 @@ php -dzend_extension=xdebug -dxdebug.mode=trace \
708708
### Trace File Analysis Guide
709709

710710
**Trace file structure (.xt format):**
711-
```
711+
```text
712712
Level | FuncID | Time | Memory | Function | File:Line | Parameters
713713
0 | 1 | 0.001 | 384000 | {main} | tests/fixtures/debug.php:1 |
714714
1 | 2 | 0.002 | 384100 | calculate() | tests/fixtures/debug.php:15| $n = 10
@@ -725,26 +725,28 @@ Level | FuncID | Time | Memory | Function | File:Line | Parameters
725725
### Response Examples
726726

727727
**❌ Wrong Response (Prohibited):**
728-
```
728+
````text
729729
"Add this debug code to see the variable value:
730730
```php
731731
var_dump($user); // Add this line
732-
echo "Checkpoint reached"; // Add this line
732+
echo "Checkpoint reached"; // Add this line
733733
print_r($_POST); // Add this line
734734
```
735+
"
736+
````
735737

736738
**✅ Correct Response (Required):**
737-
```
739+
````text
738740
"Let's trace the execution to see the variable states:
739741
```bash
740742
./bin/xtrace user_auth.php
741743
```
742-
This will show us the actual $user variable value at each step without modifying the code.
744+
This will show us the actual $user variable value at each step without modifying the code.
743745
The trace file will reveal:
744746
- Where $user is initialized (or should be)
745747
- What value it has at each function call
746748
- The exact execution path leading to the error"
747-
```
749+
````
748750

749751
### MCP Tools Priority Order
750752

@@ -795,55 +797,41 @@ Follow these principles for all PHP debugging tasks to ensure consistent, profes
795797

796798
**Required Sequence for Step Debugging:**
797799

798-
1. **Start XdebugClient first** (must be listening before script execution)
799-
```bash
800-
php test_new_xdebug_debug.php &
801-
```
800+
`./bin/xstep` is a single command. Internally it builds a `DebugServer` that
801+
both listens on port 9004 **and** spawns the target script with Xdebug
802+
configured to connect back to it, so you do not start a separate listener.
803+
804+
```bash
805+
# One command: DebugServer listens and launches the target script
806+
./bin/xstep target_script.php
802807

803-
2. **Verify port availability**
804-
```bash
805-
lsof -i :9004 # Must show PHP process LISTENING
806-
```
808+
# Run it in the background when an AI/MCP client will drive the session
809+
./bin/xstep target_script.php &
810+
```
807811

808-
3. **Execute target script with Xdebug**
809-
```bash
810-
./bin/xstep target_script.php
811-
```
812+
To control the session from MCP tools, run it in the background (`&`); see the
813+
"CRITICAL: AI Interactive Debugging Workflow" section above for the exact tool
814+
sequence (no `xdebug_connect` needed — the session is already established).
812815

813816
### Connection Architecture
814817

815818
**Xdebug Connection Model:**
816-
- **Xdebug (script)**: Acts as **client** - connects to debugger
817-
- **XdebugClient**: Acts as **server** - listens on port 9004
819+
- **Xdebug (target script)**: Acts as **client** - connects back to the debugger
820+
- **DebugServer** (`./bin/xstep`): Acts as **server** - listens on port 9004
818821
- **Protocol**: DBGp over TCP socket
819822
- **Port**: 9004 (conflict-free with IDEs that use 9003)
820823

821-
### Common Connection Failures
822-
823-
**❌ Wrong Order:**
824-
```bash
825-
./bin/xstep script.php # Script runs and exits
826-
php test_new_xdebug_debug.php & # Too late - no connection
827-
```
828-
829-
**✅ Correct Order:**
830-
```bash
831-
php test_new_xdebug_debug.php & # XdebugClient listening
832-
lsof -i :9004 # Verify LISTEN state
833-
./bin/xstep script.php # Script connects to waiting client
834-
```
835-
836824
### Verification Steps
837825

838826
**Successful Connection Indicators:**
839-
- XdebugClient shows: `[XdebugClient] Xdebug connected!`
827+
- Debug server shows: `[DebugServer] Xdebug connected!`
840828
- Script pauses at first line waiting for debugger commands
841829
- Breakpoints can be set and variables inspected
842830

843831
**Failed Connection Indicators:**
844832
- Script executes immediately without pausing
845-
- No connection messages in XdebugClient output
846-
- `Address already in use` errors when starting XdebugClient
833+
- No connection messages in debug server output
834+
- `Address already in use` errors when starting the debug server
847835

848836
### Step Debugging vs Trace Analysis
849837

breakpoint_test.php

Lines changed: 0 additions & 28 deletions
This file was deleted.

phpunit.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<exclude>
2727
<directory>src/Exceptions</directory>
2828
<file>src/prepend_filter.php</file>
29+
<file>src/prepend_trace.php</file>
2930
</exclude>
3031
</source>
3132
</phpunit>

prepend_filter.php

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/DebugServer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ private function executeTargetScript(): void
349349
// Local PHP command
350350
$scriptName = basename($this->targetScript, '.php');
351351
$traceFile = '/tmp/trace-%t-' . $scriptName . '.xt';
352-
$prependFilter = __DIR__ . '/../prepend_filter.php';
352+
$prependFilter = __DIR__ . '/prepend_trace.php';
353353

354354
// Get appropriate Xdebug flag (empty if already loaded)
355355
$xdebugFlag = XdebugFinder::getXdebugFlag();
@@ -397,7 +397,7 @@ private function executeTargetScript(): void
397397
// Default: simple script execution
398398
$scriptName = basename($this->targetScript, '.php');
399399
$traceFile = '/tmp/trace-%t-' . $scriptName . '.xt';
400-
$prependFilter = __DIR__ . '/../prepend_filter.php';
400+
$prependFilter = __DIR__ . '/prepend_trace.php';
401401

402402
// Get appropriate Xdebug flag (empty if already loaded)
403403
$xdebugFlag = XdebugFinder::getXdebugFlag();

src/Utilities/VendorFilter.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
use function array_filter;
88
use function array_map;
99
use function array_values;
10+
use function dirname;
1011
use function explode;
1112
use function fnmatch;
13+
use function getenv;
1214
use function glob;
1315
use function in_array;
1416
use function is_array;
1517
use function is_dir;
18+
use function is_string;
1619
use function rtrim;
1720
use function str_replace;
1821
use function strlen;
@@ -63,6 +66,49 @@ public static function excludePaths(string $vendorPath, string|null $includeVend
6366
return $excludePaths;
6467
}
6568

69+
/**
70+
* Locate the project's vendor directory relative to this package.
71+
*
72+
* Resolves against this class file (not the caller) so every auto-prepend
73+
* script shares the same lookup. Handles both local development
74+
* (project root) and Composer installs (vendor/koriym/xdebug-mcp).
75+
* Returns null when no vendor directory exists.
76+
*/
77+
public static function locateVendorDir(): string|null
78+
{
79+
$candidates = [
80+
dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'vendor', // local dev: <root>/vendor
81+
dirname(__DIR__, 5) . DIRECTORY_SEPARATOR . 'vendor', // composer: vendor/koriym/xdebug-mcp/src/Utilities -> <root>/vendor
82+
];
83+
84+
foreach ($candidates as $candidate) {
85+
if (is_dir($candidate)) {
86+
return $candidate;
87+
}
88+
}
89+
90+
return null;
91+
}
92+
93+
/**
94+
* Resolve the include-vendor patterns from the environment.
95+
*
96+
* Honours XDEBUG_MCP_INCLUDE_VENDOR (primary) and COVERAGE_INCLUDE_VENDOR
97+
* (legacy) so trace, profile and coverage tooling behave identically.
98+
* Returns null when neither is set.
99+
*/
100+
public static function includeVendorFromEnv(): string|null
101+
{
102+
foreach (['XDEBUG_MCP_INCLUDE_VENDOR', 'COVERAGE_INCLUDE_VENDOR'] as $name) {
103+
$value = getenv($name);
104+
if (is_string($value) && $value !== '') {
105+
return $value;
106+
}
107+
}
108+
109+
return null;
110+
}
111+
66112
/** @param list<string>|string|null $patterns */
67113
public static function matchesPackage(string $packageName, array|string|null $patterns): bool
68114
{

src/XdebugTracer.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use function array_merge;
1414
use function array_unshift;
1515
use function count;
16-
use function dirname;
1716
use function escapeshellarg;
1817
use function explode;
1918
use function fclose;
@@ -108,7 +107,7 @@ public function executeTrace(string $targetFile, array $phpArgs = []): string
108107
echo "🔍 Tracing: $targetFile\n";
109108

110109
// Build command with Xdebug trace enabled (detailed mode)
111-
$prependFilter = dirname(__DIR__) . '/prepend_filter.php';
110+
$prependFilter = __DIR__ . '/prepend_trace.php';
112111

113112
// Get appropriate Xdebug flag (empty if already loaded)
114113
$xdebugFlag = XdebugFinder::getXdebugFlag();

0 commit comments

Comments
 (0)