gdbmcp

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 access STL containers without modifying target code.

Category
访问服务器

README

gdbmcp

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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选