Gdbmcp
БесплатноНе проверенAttaches to a running C++ process and exposes tools to inspect live memory via a resident GDB subprocess, enabling LLMs to read fields, follow pointers, and acc
Описание
Attaches to a running C++ process and exposes tools to inspect live memory via a resident GDB subprocess, enabling LLMs to read fields, follow pointers, and access STL containers without modifying target code.
README
A standalone MCP (Model Context Protocol) server for Linux that attaches
to a running C++ process and exposes three tools — evaluate,
map_get, enumerate — to inspect the target's live memory via a resident
GDB subprocess.
Zero source-level intrusion: no target code or build is modified. The AI writes the field/path it wants as a C++ expression; gdbmcp passes it verbatim to GDB, which resolves symbols from the target's DWARF debug info and returns the value.
MCP client ──stdio JSON-RPC──▶ gdbmcp (Python, single ELF) ──GDB/MI──▶ gdb ──ptrace──▶ target C++ process (-g)
What works (and what doesn't) — read this first
GDB's expression evaluator is powerful but has hard limits on a stock
libstdc++/libc++ target. evaluate surfaces these as error strings; prefer the
working forms.
| Want | Works? | How |
|---|---|---|
| Read a field / follow pointers | ✅ | obj->member.field |
| Call an out-of-line function (e.g. a singleton accessor) | ✅ | World::GetInstance()->FindPlayer(1001)->hp |
Index a std::vector element |
✅ via raw buffer | vec._M_impl._M_start[i].field |
| Index a raw array / pointer | ✅ | arr[i], ptr[i] |
Call inlined STL methods (map::find, map::at, map::operator[], vector::at, vector::operator[]) |
❌ | gdb can't call inlined functions — use an out-of-line accessor on the target |
Construct a custom key: EntityID(1001) (functional cast) |
❌ syntax error | use C-style cast (EntityID)1001, and only if that ctor is a callable out-of-line symbol |
print /r-style struct dump |
✅ (text format) | default |
Why: std::map::find / std::vector::operator[] are defined inline in the
STL headers; GDB cannot inferior-call an inlined function. The robust pattern on
a real target is to expose out-of-line accessor methods taking primitive
arguments (e.g. FindPlayer(int id) instead of players.find(EntityID(id))),
and to reach vector elements through _M_impl._M_start[i].
If you can't add an accessor (e.g. a third-party binary), use the dedicated container tools below — they walk the container's internal layout by direct memory reads, no inferior call:
| Want | Tool | How |
|---|---|---|
| Look up one element by key in a map/unordered_map | map_get |
walks the rb-tree / hash buckets, matches the key field-wise |
| List elements of any container | enumerate |
walks map/set (tree), unordered_* (hash), vector (buffer) |
These limitations come from GDB/libstdc++, not from gdbmcp — gdbmcp only passes the expression through.
Target pause model
GDB attaches via ptrace, which SIGSTOPs the target. gdbmcp keeps the target
running between queries and briefly interrupts it (all-stop) per evaluate
call — typically a few milliseconds for a pure data read, longer if the
expression triggers an inferior function call. A wall-clock watchdog (plus GDB
≥14.1's native direct-call-timeout) aborts any call that does not return.
On gdbmcp exit (stdin closed, SIGTERM, SIGINT), it detaches (never kills) the target, which resumes normal execution.
Requirements
- Linux only.
- gdb ≥ 14.1 recommended (native inferior-call timeout). Minimum gdb ≥ 10
(watchdog-only). Must be the full gdb package with Python support
(
gdb --configurationshows--with-python) — a strippedgdb-minimalcannot load STL pretty-printers. Install e.g.apt install gdb. - ptrace permission to attach to the target.
kernel.yama.ptrace_scopedefaults to1on most distros (only a parent may trace its children). Pick one:- run gdbmcp as root, or
sudo setcap cap_sys_ptrace+ep ./dist/gdbmcp, or- set
/proc/sys/kernel/yama/ptrace_scopeto0(system-wide; security trade-off). - in Docker:
--cap-add SYS_PTRACE.
- The target must be compiled with debug info (
-g, not stripped) for symbol resolution.
Build the single-file executable
./packaging/build.sh # uses .venv, installs deps + pyinstaller, builds dist/gdbmcp
The result dist/gdbmcp is a self-contained ELF — no Python needed on the host.
gdb itself is a separate system dependency (see above). Build on the oldest glibc
you intend to support for forward portability.
Run
gdbmcp speaks MCP over stdio (default) or HTTP (streamable-http).
stdio (default — for Claude Code / Cursor / cline / claude.ai)
The MCP client launches gdbmcp as a subprocess and talks over its stdin/stdout:
./dist/gdbmcp --pid <PID> [--config config.json] [--gdb-path /usr/bin/gdb]
Example client config:
{
"mcpServers": {
"gdbmcp": { "command": "/path/to/gdbmcp", "args": ["--pid", "12345"] }
}
}
HTTP (for remote / multi-client access)
./dist/gdbmcp --pid <PID> --transport http --host 0.0.0.0 --port 8000 [--auth-token SECRET]
Endpoint: POST http://<host>:<port>/mcp (MCP streamable-http). Bind defaults to
0.0.0.0:8000.
Authentication (HTTP only — important)
evaluate reads arbitrary target memory. Any HTTP exposure must be authenticated.
Set a bearer token via --auth-token or the GDBMCP_TOKEN env var; requests must
then carry Authorization: Bearer <token> or they get 401. Without a token on
the HTTP transport, gdbmcp prints a warning and runs unauthenticated — do not do
this on a reachable network.
This is a shared-secret scheme (constant-time compare). For production, put a
reverse proxy (nginx/caddy) in front to terminate TLS and bind gdbmcp to
127.0.0.1, so the token is never sent in cleartext. (gdbmcp does not bundle a
TLS terminator; OAuth is not implemented — see auth.py if you need either.)
config.json (optional)
See config.example.json. Notable fields: gdb_path,
timeouts.{attach,call,eval}_seconds, output.{default_format,max_depth, max_children,string_truncate}, stl_flavor.
The evaluate tool
expression(string, required) — a C++ expression.format("text" | "json", default "text") —textis gdb-style;jsonreturns a typed tree{name, type, value|children}(scalars coerced to int/float/bool/str).
On error, returns a plain-language diagnostic string (unknown symbol, null dereference, timeout, target exited, invalid expression). Failures never crash the server, GDB, or the target.
The map_get and enumerate tools (no-accessor container access)
When the target has no out-of-line accessor, these read containers directly:
map_get(map_expr, key_expr, format="text") — look up one element by key in
std::map / std::unordered_map / std::multimap / std::multiset. Walks the
red-black tree / hash buckets by memory reads and matches the key (deep,
field-wise, so struct keys compare by fields). Use C-style cast for struct keys:
map_get("world->m_players", "(EntityID)1002").
enumerate(container_expr, limit=100, format="text") — list up to limit
elements of any STL container (map/set/multimap/multiset, unordered_*,
vector). Map items carry both key and value; vector/set items carry the value.
Both never call inlined STL methods, so they work on any target regardless of accessors. Layout is libstdc++-specific (stable across its ABI versions).
Quick examples (MCP JSON-RPC)
All three tools are called the same way — a tools/call JSON-RPC over whichever
transport you started (stdio or HTTP). After the standard initialize /
notifications/initialized handshake:
evaluate — read a single value/expression (prefers an out-of-line accessor):
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "evaluate",
"arguments": { "expression": "GameWorld::GetInstance()->find_player(1001)->m_hp",
"format": "text" } } }
// -> { "result": { "isError": false, "content": [ { "type": "text", "text": "int = 100" } ] } }
map_get — look up one map element by key WITHOUT an accessor:
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "map_get",
"arguments": { "map_expr": "GameWorld::GetInstance()->m_players",
"key_expr": "(EntityID)1002", "format": "text" } } }
// -> key matched -> value is the Player struct dump (Bob ...)
enumerate — list elements of any container:
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": { "name": "enumerate",
"arguments": { "container_expr": "GameWorld::GetInstance()->find_player(1001)->m_items",
"limit": 5, "format": "text" } } }
// -> size=2, lists Sword / Potion
Use format: "json" on any tool for structured output. On any failure the tool
returns a plain-language diagnostic string (isError: false, the text describes
the problem); the server, GDB, and target never crash.
Test
.venv/bin/python -m pytest # unit + integration + full-stack e2e
Tests attach to a compiled fixture (tests/fixtures/target.cpp) via real gdb.
Architecture / code map
| File | Role |
|---|---|
src/gdbmcp/gdb_controller.py |
Resident gdb subprocess; GDB/MI send/recv with token correlation; attach/interrupt/continue/detach; watchdog abort path; init + libstdc++ printer registration |
src/gdbmcp/mi_parser.py |
GDB/MI stream parser (result/async/stream records; nested tuples & lists) |
src/gdbmcp/evaluator.py |
evaluate orchestration: validate → stop → watchdog → resolve (var-objects + data-evaluate) → render → resume; error mapping |
src/gdbmcp/container_query.py |
map_get / enumerate: gdb-Python walker that traverses map/unordered_map/vector internals with no inferior calls |
src/gdbmcp/formatters.py |
text/JSON rendering of the value tree; scalar coercion; depth/width caps |
src/gdbmcp/errors.py |
Error classification + human-friendly messages |
src/gdbmcp/watchdog.py |
Cancellable wall-clock timer that aborts runaway inferior calls |
src/gdbmcp/mcp_server.py |
FastMCP server registering the evaluate / map_get / enumerate tools; stdio + HTTP transports |
src/gdbmcp/auth.py |
Shared-secret bearer-token verifier for the HTTP transport |
src/gdbmcp/lifecycle.py |
atexit + signal handlers → clean detach on exit |
src/gdbmcp/config.py |
Config load (JSON + CLI overrides) |
src/gdbmcp/__main__.py |
CLI entry: parse args, attach, serve stdio |
Out of scope (v1)
- non-stop mode (smaller pause blast radius), process-name auto-discovery,
- a custom GDB Python pretty-printer that emits JSON (currently the JSON path parses GDB's var-object output), memory-write tooling, and bundling gdb into the executable.
Установка Gdbmcp
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/luckinbyte/gdbmcpFAQ
Gdbmcp MCP бесплатный?
Да, Gdbmcp MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Gdbmcp?
Нет, Gdbmcp работает без API-ключей и переменных окружения.
Gdbmcp — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Gdbmcp в Claude Desktop, Claude Code или Cursor?
Открой Gdbmcp на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Gdbmcp with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
