Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

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

GitHubEmbed

Описание

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 toolsevaluate, 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 --configuration shows --with-python) — a stripped gdb-minimal cannot load STL pretty-printers. Install e.g. apt install gdb.
  • ptrace permission to attach to the target. kernel.yama.ptrace_scope defaults to 1 on 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_scope to 0 (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")text is gdb-style; json returns 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.

from github.com/luckinbyte/gdbmcp

Установка Gdbmcp

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/luckinbyte/gdbmcp

FAQ

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

Compare Gdbmcp with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development