textual-debugger 0.1.3__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/PKG-INFO +107 -7
- {textual_debugger-0.1.3/src/tdb → textual_debugger-0.1.5}/README.md +105 -6
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/pyproject.toml +3 -1
- {textual_debugger-0.1.3 → textual_debugger-0.1.5/src/tdb}/README.md +105 -6
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/__init__.py +1 -1
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_handlers/inspection.py +39 -123
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/cli.py +20 -8
- textual_debugger-0.1.5/src/tdb/mcp/__init__.py +10 -0
- textual_debugger-0.1.5/src/tdb/mcp/__main__.py +6 -0
- textual_debugger-0.1.5/src/tdb/mcp/server.py +306 -0
- textual_debugger-0.1.5/src/tdb/mcp/session.py +205 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/app.py +14 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/handlers.py +156 -105
- textual_debugger-0.1.5/src/tdb/session/inspect_service.py +265 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/source_analysis.py +20 -4
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/PKG-INFO +107 -7
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/SOURCES.txt +5 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/entry_points.txt +1 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/requires.txt +1 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/LICENSE +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/setup.cfg +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/__main__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/_timeouts.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_handlers/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_handlers/dap_events.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_handlers/routing.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_handlers/ui_panels.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/app_helpers.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/breakpoint_hook.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/dap/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/dap/client.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/dap/messages.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/dap/protocol.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/dap/types.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/inspection.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/inspection_full.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/keybindings/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/persist.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/post_mortem.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/processes_cache.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/event_handler.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/rpc_types.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/server/runner.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/child_processes.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/controller.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/event_bus.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/messages.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/post_mortem_loader.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/state.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/statement_stepper.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/terminal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/session/textual_handler.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/__init__.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/_inspection_modal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/async_tasks_modal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/breakpoint_view.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/code_view.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/console_view.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/evaluate_console.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/full_contents_modal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/menu_bar.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/modals.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/processes_modal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/stack_view.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/status_bar.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/threads_modal.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/tdb/widgets/variable_view.py +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
- {textual_debugger-0.1.3 → textual_debugger-0.1.5}/src/textual_debugger.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: textual-debugger
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A TUI Python debugger based on textual and debugpy
|
|
5
5
|
Author-email: Al Danial <al.danial@gmail.com>
|
|
6
6
|
License: textual-debugger is licensed to you under the MIT/X Consortium license:
|
|
@@ -51,6 +51,7 @@ Requires-Dist: debugpy>=1.8.0
|
|
|
51
51
|
Requires-Dist: pygments>=2.17.0
|
|
52
52
|
Requires-Dist: fastapi>=0.115.0
|
|
53
53
|
Requires-Dist: uvicorn>=0.30.0
|
|
54
|
+
Requires-Dist: mcp>=1.28.0
|
|
54
55
|
Provides-Extra: dev
|
|
55
56
|
Requires-Dist: pytest; extra == "dev"
|
|
56
57
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -90,7 +91,8 @@ It specifically supports modules
|
|
|
90
91
|
|
|
91
92
|
- supports remote attachment to debugpy-enabled Python programs
|
|
92
93
|
|
|
93
|
-
- includes a JSON-RPC server mode
|
|
94
|
+
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
95
|
+
programmatic debug control, making it suitable for
|
|
94
96
|
automated, headless debugging workflows and AI-assisted debugging
|
|
95
97
|
|
|
96
98
|
- can spawn the debuggee in an external terminal to enable debugging TUI applications
|
|
@@ -119,6 +121,9 @@ as open source.
|
|
|
119
121
|
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
120
122
|
`tdb` was made almost entirely with Claude Code.
|
|
121
123
|
|
|
124
|
+
- OpenAI, for providing access to Codex through the
|
|
125
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
126
|
+
|
|
122
127
|
## Gallery
|
|
123
128
|
<p align="center">
|
|
124
129
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -441,6 +446,17 @@ Snapshot depth / breadth is capped (5 levels, 50 children per container) to keep
|
|
|
441
446
|
capture cheap even for pathological object graphs; cycles are handled via identity
|
|
442
447
|
memoization.
|
|
443
448
|
|
|
449
|
+
### Post-Mortem within a Docker Container
|
|
450
|
+
|
|
451
|
+
The `textual-debugger` GitHub repository's `examples/` directory has three files
|
|
452
|
+
that show how to run a `tdb`-enabled Python program under `tmux` in a Docker
|
|
453
|
+
container so that you can attach to the container and inspect the program
|
|
454
|
+
in `tdb` post-mortem analysis mode if the program hits an unhandled exception:
|
|
455
|
+
|
|
456
|
+
- [post_mortem_example.py](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_example.py)
|
|
457
|
+
- [post_mortem_entrypoint.sh](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_entrypoint.sh)
|
|
458
|
+
- [Dockerfile.post_mortem](https://github.com/AlDanial/tdb/blob/main/examples/Dockerfile.post_mortem)
|
|
459
|
+
|
|
444
460
|
### Live Breakpoint Hook
|
|
445
461
|
|
|
446
462
|
`tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
|
|
@@ -738,11 +754,12 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
|
|
|
738
754
|
| `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
|
|
739
755
|
| `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
|
|
740
756
|
| `list_breakpoints` | `[]` | Show all breakpoints |
|
|
741
|
-
| `continue` | `[]` | Resume execution |
|
|
742
|
-
| `next` | `[]` | Step over |
|
|
743
|
-
| `step_in` | `[]` | Step into |
|
|
744
|
-
| `step_out` | `[]` | Step out |
|
|
745
|
-
| `pause` | `[]` | Pause execution |
|
|
757
|
+
| `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running — call pause or wait again"` (success) |
|
|
758
|
+
| `next` | `[]` or `[timeout_s]` | Step over |
|
|
759
|
+
| `step_in` | `[]` or `[timeout_s]` | Step into |
|
|
760
|
+
| `step_out` | `[]` or `[timeout_s]` | Step out |
|
|
761
|
+
| `pause` | `[]` | Pause execution; bypasses the dispatch lock so it can interrupt an in-flight blocking action |
|
|
762
|
+
| `wait_for_stop` | `[]` or `[timeout_s]` | Wait for the next stop without issuing a step (use after `continue` returns `"still running"` to keep waiting) |
|
|
746
763
|
| `inspect` | `["expr1", "expr2", ...]` | Evaluate multiple expressions |
|
|
747
764
|
| `evaluate` | `["expression"]` | Evaluate a single expression |
|
|
748
765
|
| `stack_up` | `[]` | Move up the call stack |
|
|
@@ -771,6 +788,89 @@ curl -N http://127.0.0.1:8150/events
|
|
|
771
788
|
Events: `initialized`, `stopped`, `continued`, `terminated`, `exited`, `output`.
|
|
772
789
|
Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
773
790
|
|
|
791
|
+
## MCP Integration
|
|
792
|
+
|
|
793
|
+
tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
|
|
794
|
+
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
795
|
+
server is a third in-process consumer of the same dispatch handlers the
|
|
796
|
+
TUI and the HTTP server use — so an agent gets the same lock semantics,
|
|
797
|
+
including the pause-during-continue bypass, and the same DAP-backed
|
|
798
|
+
inspection surface.
|
|
799
|
+
|
|
800
|
+
For a worked end-to-end example (prompting an agent to find two
|
|
801
|
+
runtime bugs in a sample program) see
|
|
802
|
+
[docs/tutorial-mcp-debugging.md](docs/tutorial-mcp-debugging.md) and
|
|
803
|
+
its companion `examples/sales_report_buggy.py`.
|
|
804
|
+
|
|
805
|
+
### Running the MCP server
|
|
806
|
+
|
|
807
|
+
Configure your MCP client (Claude Desktop, an IDE extension, etc.) to
|
|
808
|
+
launch `tdb-mcp` over stdio. Example `claude_desktop_config.json`:
|
|
809
|
+
|
|
810
|
+
```json
|
|
811
|
+
{
|
|
812
|
+
"mcpServers": {
|
|
813
|
+
"tdb": {
|
|
814
|
+
"command": "tdb-mcp"
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
Three equivalent invocation forms: `tdb-mcp` (the dedicated entry
|
|
821
|
+
point), `tdb --mcp` (the main CLI with the `--mcp` switch), and
|
|
822
|
+
`python -m tdb.mcp` (module form). Pick whichever matches how your MCP
|
|
823
|
+
client expects to launch servers.
|
|
824
|
+
|
|
825
|
+
### Tool surface (16 tools, curated)
|
|
826
|
+
|
|
827
|
+
| Cluster | Tools |
|
|
828
|
+
|---------|-------|
|
|
829
|
+
| Lifecycle | `debug_launch`, `debug_attach`, `quit` |
|
|
830
|
+
| Control | `control(action, timeout_s=30)` — `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
|
|
831
|
+
| Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
|
|
832
|
+
| Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
|
|
833
|
+
| Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
|
|
834
|
+
|
|
835
|
+
`control` is intentionally one tool that takes an action enum — the six
|
|
836
|
+
underlying RPC actions share a return shape, and agents perform
|
|
837
|
+
measurably better with a small surface than with one tool per action.
|
|
838
|
+
`threads` / `tasks` / `processes` overload list-vs-inspect via a
|
|
839
|
+
single optional argument for the same reason.
|
|
840
|
+
|
|
841
|
+
### Agent flow for a long-running step
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
agent → control(action="continue", timeout_s=30)
|
|
845
|
+
mcp → "still running — call pause or wait again"
|
|
846
|
+
agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
|
|
847
|
+
mcp → "<file>:<line>"
|
|
848
|
+
agent → inspect(["x", "len(buf)"])
|
|
849
|
+
mcp → "x = 7\nlen(buf) = 1024"
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
`pause` bypasses the dispatch lock so it can interrupt a `continue`
|
|
853
|
+
that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
|
|
854
|
+
policy — see `tdb/server/app.py`).
|
|
855
|
+
|
|
856
|
+
### Security caveat
|
|
857
|
+
|
|
858
|
+
`inspect` calls debugpy's `evaluate`, which is **arbitrary Python
|
|
859
|
+
execution in the debuggee process**. This is inherent to a debugger and
|
|
860
|
+
not a tdb-specific concern, but MCP clients (and the humans running
|
|
861
|
+
them) should apply appropriate permission models: don't auto-approve
|
|
862
|
+
`inspect` against untrusted expressions, and don't expose `tdb-mcp` on
|
|
863
|
+
a network — stdio transport only by design.
|
|
864
|
+
|
|
865
|
+
### Deferred / out of scope (v1)
|
|
866
|
+
|
|
867
|
+
- SSE-style event push — `control` and `wait_for_stop` make polling
|
|
868
|
+
efficient enough; events would also need uneven MCP-client support.
|
|
869
|
+
- HTTP / streamable-HTTP transports — would require auth (which the
|
|
870
|
+
HTTP RPC server also currently lacks); stdio inherits the trust of
|
|
871
|
+
the process that spawned it.
|
|
872
|
+
- Multi-session — one debug session per MCP process.
|
|
873
|
+
|
|
774
874
|
## CLI Reference
|
|
775
875
|
|
|
776
876
|
```
|
|
@@ -25,7 +25,8 @@ It specifically supports modules
|
|
|
25
25
|
|
|
26
26
|
- supports remote attachment to debugpy-enabled Python programs
|
|
27
27
|
|
|
28
|
-
- includes a JSON-RPC server mode
|
|
28
|
+
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
29
|
+
programmatic debug control, making it suitable for
|
|
29
30
|
automated, headless debugging workflows and AI-assisted debugging
|
|
30
31
|
|
|
31
32
|
- can spawn the debuggee in an external terminal to enable debugging TUI applications
|
|
@@ -54,6 +55,9 @@ as open source.
|
|
|
54
55
|
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
55
56
|
`tdb` was made almost entirely with Claude Code.
|
|
56
57
|
|
|
58
|
+
- OpenAI, for providing access to Codex through the
|
|
59
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
60
|
+
|
|
57
61
|
## Gallery
|
|
58
62
|
<p align="center">
|
|
59
63
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -376,6 +380,17 @@ Snapshot depth / breadth is capped (5 levels, 50 children per container) to keep
|
|
|
376
380
|
capture cheap even for pathological object graphs; cycles are handled via identity
|
|
377
381
|
memoization.
|
|
378
382
|
|
|
383
|
+
### Post-Mortem within a Docker Container
|
|
384
|
+
|
|
385
|
+
The `textual-debugger` GitHub repository's `examples/` directory has three files
|
|
386
|
+
that show how to run a `tdb`-enabled Python program under `tmux` in a Docker
|
|
387
|
+
container so that you can attach to the container and inspect the program
|
|
388
|
+
in `tdb` post-mortem analysis mode if the program hits an unhandled exception:
|
|
389
|
+
|
|
390
|
+
- [post_mortem_example.py](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_example.py)
|
|
391
|
+
- [post_mortem_entrypoint.sh](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_entrypoint.sh)
|
|
392
|
+
- [Dockerfile.post_mortem](https://github.com/AlDanial/tdb/blob/main/examples/Dockerfile.post_mortem)
|
|
393
|
+
|
|
379
394
|
### Live Breakpoint Hook
|
|
380
395
|
|
|
381
396
|
`tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
|
|
@@ -673,11 +688,12 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
|
|
|
673
688
|
| `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
|
|
674
689
|
| `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
|
|
675
690
|
| `list_breakpoints` | `[]` | Show all breakpoints |
|
|
676
|
-
| `continue` | `[]` | Resume execution |
|
|
677
|
-
| `next` | `[]` | Step over |
|
|
678
|
-
| `step_in` | `[]` | Step into |
|
|
679
|
-
| `step_out` | `[]` | Step out |
|
|
680
|
-
| `pause` | `[]` | Pause execution |
|
|
691
|
+
| `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running — call pause or wait again"` (success) |
|
|
692
|
+
| `next` | `[]` or `[timeout_s]` | Step over |
|
|
693
|
+
| `step_in` | `[]` or `[timeout_s]` | Step into |
|
|
694
|
+
| `step_out` | `[]` or `[timeout_s]` | Step out |
|
|
695
|
+
| `pause` | `[]` | Pause execution; bypasses the dispatch lock so it can interrupt an in-flight blocking action |
|
|
696
|
+
| `wait_for_stop` | `[]` or `[timeout_s]` | Wait for the next stop without issuing a step (use after `continue` returns `"still running"` to keep waiting) |
|
|
681
697
|
| `inspect` | `["expr1", "expr2", ...]` | Evaluate multiple expressions |
|
|
682
698
|
| `evaluate` | `["expression"]` | Evaluate a single expression |
|
|
683
699
|
| `stack_up` | `[]` | Move up the call stack |
|
|
@@ -706,6 +722,89 @@ curl -N http://127.0.0.1:8150/events
|
|
|
706
722
|
Events: `initialized`, `stopped`, `continued`, `terminated`, `exited`, `output`.
|
|
707
723
|
Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
708
724
|
|
|
725
|
+
## MCP Integration
|
|
726
|
+
|
|
727
|
+
tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
|
|
728
|
+
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
729
|
+
server is a third in-process consumer of the same dispatch handlers the
|
|
730
|
+
TUI and the HTTP server use — so an agent gets the same lock semantics,
|
|
731
|
+
including the pause-during-continue bypass, and the same DAP-backed
|
|
732
|
+
inspection surface.
|
|
733
|
+
|
|
734
|
+
For a worked end-to-end example (prompting an agent to find two
|
|
735
|
+
runtime bugs in a sample program) see
|
|
736
|
+
[docs/tutorial-mcp-debugging.md](docs/tutorial-mcp-debugging.md) and
|
|
737
|
+
its companion `examples/sales_report_buggy.py`.
|
|
738
|
+
|
|
739
|
+
### Running the MCP server
|
|
740
|
+
|
|
741
|
+
Configure your MCP client (Claude Desktop, an IDE extension, etc.) to
|
|
742
|
+
launch `tdb-mcp` over stdio. Example `claude_desktop_config.json`:
|
|
743
|
+
|
|
744
|
+
```json
|
|
745
|
+
{
|
|
746
|
+
"mcpServers": {
|
|
747
|
+
"tdb": {
|
|
748
|
+
"command": "tdb-mcp"
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Three equivalent invocation forms: `tdb-mcp` (the dedicated entry
|
|
755
|
+
point), `tdb --mcp` (the main CLI with the `--mcp` switch), and
|
|
756
|
+
`python -m tdb.mcp` (module form). Pick whichever matches how your MCP
|
|
757
|
+
client expects to launch servers.
|
|
758
|
+
|
|
759
|
+
### Tool surface (16 tools, curated)
|
|
760
|
+
|
|
761
|
+
| Cluster | Tools |
|
|
762
|
+
|---------|-------|
|
|
763
|
+
| Lifecycle | `debug_launch`, `debug_attach`, `quit` |
|
|
764
|
+
| Control | `control(action, timeout_s=30)` — `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
|
|
765
|
+
| Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
|
|
766
|
+
| Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
|
|
767
|
+
| Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
|
|
768
|
+
|
|
769
|
+
`control` is intentionally one tool that takes an action enum — the six
|
|
770
|
+
underlying RPC actions share a return shape, and agents perform
|
|
771
|
+
measurably better with a small surface than with one tool per action.
|
|
772
|
+
`threads` / `tasks` / `processes` overload list-vs-inspect via a
|
|
773
|
+
single optional argument for the same reason.
|
|
774
|
+
|
|
775
|
+
### Agent flow for a long-running step
|
|
776
|
+
|
|
777
|
+
```
|
|
778
|
+
agent → control(action="continue", timeout_s=30)
|
|
779
|
+
mcp → "still running — call pause or wait again"
|
|
780
|
+
agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
|
|
781
|
+
mcp → "<file>:<line>"
|
|
782
|
+
agent → inspect(["x", "len(buf)"])
|
|
783
|
+
mcp → "x = 7\nlen(buf) = 1024"
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
`pause` bypasses the dispatch lock so it can interrupt a `continue`
|
|
787
|
+
that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
|
|
788
|
+
policy — see `tdb/server/app.py`).
|
|
789
|
+
|
|
790
|
+
### Security caveat
|
|
791
|
+
|
|
792
|
+
`inspect` calls debugpy's `evaluate`, which is **arbitrary Python
|
|
793
|
+
execution in the debuggee process**. This is inherent to a debugger and
|
|
794
|
+
not a tdb-specific concern, but MCP clients (and the humans running
|
|
795
|
+
them) should apply appropriate permission models: don't auto-approve
|
|
796
|
+
`inspect` against untrusted expressions, and don't expose `tdb-mcp` on
|
|
797
|
+
a network — stdio transport only by design.
|
|
798
|
+
|
|
799
|
+
### Deferred / out of scope (v1)
|
|
800
|
+
|
|
801
|
+
- SSE-style event push — `control` and `wait_for_stop` make polling
|
|
802
|
+
efficient enough; events would also need uneven MCP-client support.
|
|
803
|
+
- HTTP / streamable-HTTP transports — would require auth (which the
|
|
804
|
+
HTTP RPC server also currently lacks); stdio inherits the trust of
|
|
805
|
+
the process that spawned it.
|
|
806
|
+
- Multi-session — one debug session per MCP process.
|
|
807
|
+
|
|
709
808
|
## CLI Reference
|
|
710
809
|
|
|
711
810
|
```
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"pygments>=2.17.0",
|
|
26
26
|
"fastapi>=0.115.0",
|
|
27
27
|
"uvicorn>=0.30.0",
|
|
28
|
-
|
|
28
|
+
"mcp>=1.28.0",
|
|
29
|
+
]
|
|
29
30
|
|
|
30
31
|
# extras needed to run the examples and tests
|
|
31
32
|
[project.optional-dependencies]
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
|
|
44
45
|
[project.scripts]
|
|
45
46
|
tdb = "tdb.cli:main"
|
|
47
|
+
tdb-mcp = "tdb.mcp.server:main"
|
|
46
48
|
|
|
47
49
|
[build-system]
|
|
48
50
|
requires = ["setuptools>=68.0"]
|
|
@@ -25,7 +25,8 @@ It specifically supports modules
|
|
|
25
25
|
|
|
26
26
|
- supports remote attachment to debugpy-enabled Python programs
|
|
27
27
|
|
|
28
|
-
- includes a JSON-RPC server mode
|
|
28
|
+
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
29
|
+
programmatic debug control, making it suitable for
|
|
29
30
|
automated, headless debugging workflows and AI-assisted debugging
|
|
30
31
|
|
|
31
32
|
- can spawn the debuggee in an external terminal to enable debugging TUI applications
|
|
@@ -54,6 +55,9 @@ as open source.
|
|
|
54
55
|
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
55
56
|
`tdb` was made almost entirely with Claude Code.
|
|
56
57
|
|
|
58
|
+
- OpenAI, for providing access to Codex through the
|
|
59
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
60
|
+
|
|
57
61
|
## Gallery
|
|
58
62
|
<p align="center">
|
|
59
63
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -376,6 +380,17 @@ Snapshot depth / breadth is capped (5 levels, 50 children per container) to keep
|
|
|
376
380
|
capture cheap even for pathological object graphs; cycles are handled via identity
|
|
377
381
|
memoization.
|
|
378
382
|
|
|
383
|
+
### Post-Mortem within a Docker Container
|
|
384
|
+
|
|
385
|
+
The `textual-debugger` GitHub repository's `examples/` directory has three files
|
|
386
|
+
that show how to run a `tdb`-enabled Python program under `tmux` in a Docker
|
|
387
|
+
container so that you can attach to the container and inspect the program
|
|
388
|
+
in `tdb` post-mortem analysis mode if the program hits an unhandled exception:
|
|
389
|
+
|
|
390
|
+
- [post_mortem_example.py](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_example.py)
|
|
391
|
+
- [post_mortem_entrypoint.sh](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_entrypoint.sh)
|
|
392
|
+
- [Dockerfile.post_mortem](https://github.com/AlDanial/tdb/blob/main/examples/Dockerfile.post_mortem)
|
|
393
|
+
|
|
379
394
|
### Live Breakpoint Hook
|
|
380
395
|
|
|
381
396
|
`tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
|
|
@@ -673,11 +688,12 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
|
|
|
673
688
|
| `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
|
|
674
689
|
| `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
|
|
675
690
|
| `list_breakpoints` | `[]` | Show all breakpoints |
|
|
676
|
-
| `continue` | `[]` | Resume execution |
|
|
677
|
-
| `next` | `[]` | Step over |
|
|
678
|
-
| `step_in` | `[]` | Step into |
|
|
679
|
-
| `step_out` | `[]` | Step out |
|
|
680
|
-
| `pause` | `[]` | Pause execution |
|
|
691
|
+
| `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running — call pause or wait again"` (success) |
|
|
692
|
+
| `next` | `[]` or `[timeout_s]` | Step over |
|
|
693
|
+
| `step_in` | `[]` or `[timeout_s]` | Step into |
|
|
694
|
+
| `step_out` | `[]` or `[timeout_s]` | Step out |
|
|
695
|
+
| `pause` | `[]` | Pause execution; bypasses the dispatch lock so it can interrupt an in-flight blocking action |
|
|
696
|
+
| `wait_for_stop` | `[]` or `[timeout_s]` | Wait for the next stop without issuing a step (use after `continue` returns `"still running"` to keep waiting) |
|
|
681
697
|
| `inspect` | `["expr1", "expr2", ...]` | Evaluate multiple expressions |
|
|
682
698
|
| `evaluate` | `["expression"]` | Evaluate a single expression |
|
|
683
699
|
| `stack_up` | `[]` | Move up the call stack |
|
|
@@ -706,6 +722,89 @@ curl -N http://127.0.0.1:8150/events
|
|
|
706
722
|
Events: `initialized`, `stopped`, `continued`, `terminated`, `exited`, `output`.
|
|
707
723
|
Each is JSON with `event`, `data`, and `timestamp` fields.
|
|
708
724
|
|
|
725
|
+
## MCP Integration
|
|
726
|
+
|
|
727
|
+
tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
|
|
728
|
+
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
729
|
+
server is a third in-process consumer of the same dispatch handlers the
|
|
730
|
+
TUI and the HTTP server use — so an agent gets the same lock semantics,
|
|
731
|
+
including the pause-during-continue bypass, and the same DAP-backed
|
|
732
|
+
inspection surface.
|
|
733
|
+
|
|
734
|
+
For a worked end-to-end example (prompting an agent to find two
|
|
735
|
+
runtime bugs in a sample program) see
|
|
736
|
+
[docs/tutorial-mcp-debugging.md](docs/tutorial-mcp-debugging.md) and
|
|
737
|
+
its companion `examples/sales_report_buggy.py`.
|
|
738
|
+
|
|
739
|
+
### Running the MCP server
|
|
740
|
+
|
|
741
|
+
Configure your MCP client (Claude Desktop, an IDE extension, etc.) to
|
|
742
|
+
launch `tdb-mcp` over stdio. Example `claude_desktop_config.json`:
|
|
743
|
+
|
|
744
|
+
```json
|
|
745
|
+
{
|
|
746
|
+
"mcpServers": {
|
|
747
|
+
"tdb": {
|
|
748
|
+
"command": "tdb-mcp"
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Three equivalent invocation forms: `tdb-mcp` (the dedicated entry
|
|
755
|
+
point), `tdb --mcp` (the main CLI with the `--mcp` switch), and
|
|
756
|
+
`python -m tdb.mcp` (module form). Pick whichever matches how your MCP
|
|
757
|
+
client expects to launch servers.
|
|
758
|
+
|
|
759
|
+
### Tool surface (16 tools, curated)
|
|
760
|
+
|
|
761
|
+
| Cluster | Tools |
|
|
762
|
+
|---------|-------|
|
|
763
|
+
| Lifecycle | `debug_launch`, `debug_attach`, `quit` |
|
|
764
|
+
| Control | `control(action, timeout_s=30)` — `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
|
|
765
|
+
| Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
|
|
766
|
+
| Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
|
|
767
|
+
| Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
|
|
768
|
+
|
|
769
|
+
`control` is intentionally one tool that takes an action enum — the six
|
|
770
|
+
underlying RPC actions share a return shape, and agents perform
|
|
771
|
+
measurably better with a small surface than with one tool per action.
|
|
772
|
+
`threads` / `tasks` / `processes` overload list-vs-inspect via a
|
|
773
|
+
single optional argument for the same reason.
|
|
774
|
+
|
|
775
|
+
### Agent flow for a long-running step
|
|
776
|
+
|
|
777
|
+
```
|
|
778
|
+
agent → control(action="continue", timeout_s=30)
|
|
779
|
+
mcp → "still running — call pause or wait again"
|
|
780
|
+
agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
|
|
781
|
+
mcp → "<file>:<line>"
|
|
782
|
+
agent → inspect(["x", "len(buf)"])
|
|
783
|
+
mcp → "x = 7\nlen(buf) = 1024"
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
`pause` bypasses the dispatch lock so it can interrupt a `continue`
|
|
787
|
+
that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
|
|
788
|
+
policy — see `tdb/server/app.py`).
|
|
789
|
+
|
|
790
|
+
### Security caveat
|
|
791
|
+
|
|
792
|
+
`inspect` calls debugpy's `evaluate`, which is **arbitrary Python
|
|
793
|
+
execution in the debuggee process**. This is inherent to a debugger and
|
|
794
|
+
not a tdb-specific concern, but MCP clients (and the humans running
|
|
795
|
+
them) should apply appropriate permission models: don't auto-approve
|
|
796
|
+
`inspect` against untrusted expressions, and don't expose `tdb-mcp` on
|
|
797
|
+
a network — stdio transport only by design.
|
|
798
|
+
|
|
799
|
+
### Deferred / out of scope (v1)
|
|
800
|
+
|
|
801
|
+
- SSE-style event push — `control` and `wait_for_stop` make polling
|
|
802
|
+
efficient enough; events would also need uneven MCP-client support.
|
|
803
|
+
- HTTP / streamable-HTTP transports — would require auth (which the
|
|
804
|
+
HTTP RPC server also currently lacks); stdio inherits the trust of
|
|
805
|
+
the process that spawned it.
|
|
806
|
+
- Multi-session — one debug session per MCP process.
|
|
807
|
+
|
|
709
808
|
## CLI Reference
|
|
710
809
|
|
|
711
810
|
```
|