cdbmcp

cdbmcp

Enables AI clients to inspect live memory of a running C++ process on Windows using cdb.exe, providing tools to evaluate expressions, retrieve map entries, and enumerate containers without modifying target code.

Category
访问服务器

README

cdbmcp

A standalone MCP (Model Context Protocol) server for Windows that attaches to a running C++ process and exposes three toolsevaluate, map_get, enumerate — to inspect the target's live memory via a resident cdb.exe subprocess.

It is a faithful Windows port of gdbmcp: zero source-level intrusion (no target code or build is modified). The AI writes the field/path it wants as a C++ expression; cdbmcp passes it verbatim to cdb, which resolves symbols from the target's PDB and returns the value.

MCP client ──stdio or HTTP──▶ cdbmcp (Python) ──text cmds──▶ cdb.exe ──debug attach──▶ target C++ process (+PDB)

What works (and what doesn't) — read this first

cdb's C++ expression evaluator is powerful but has the same hard limit as GDB on a stock MSVC STL target: inlined STL methods cannot be called.

Want Works? How
Read a field / follow pointers obj->member.field
Call an out-of-line function (singleton accessor) ⚠️ opt-in needs --allow-calls; Instance()->field (runs target code)
Dump a whole struct / container (??) ✅ (text) default evaluate
List elements of any container enumerate (STL walker first, dx/NatVis fallback)
Call inlined STL (map::find, map::operator[], vector::operator[]) use map_get / enumerate, or an out-of-line accessor

These limits come from the C++ evaluator / MSVC STL, not from cdbmcp — cdbmcp only passes the expression through.

Target pause model

cdb attaches with a debug attach, which stops the target at the initial breakpoint. cdbmcp keeps the target running between queries and briefly interrupts it (Ctrl-Break) per tool call — a few milliseconds for a pure data read, longer if the expression triggers an inferior function call. A wall-clock watchdog (which forces a break into the engine) aborts any call that does not return. On cdbmcp exit (stdin closed, Ctrl-C/Break, console close) it detaches (qd, never kills) the target, which resumes normal execution.

Set "pause_mode": "frozen" to instead break once at attach and keep the target stopped for the whole session (debug fallback; freezes the live process).

Requirements

  • Windows x64.
  • cdb.exe (Windows Debugging Tools / Windows Kits). Auto-detected from the usual Windows Kits\10\Debuggers\x64 paths, --cdb-path, or CDB_PATH.
  • A target built with debug info (a matching PDB) for symbol resolution.
  • Privilege to attach to the target (same user / same integrity, or run elevated if the target is elevated). Only one debugger may attach at a time.

Install

pip install -e .

(Adds the mcp dependency and an entry point cdbmcp.)

Run

cdbmcp is always a single standalone process that attaches to the target PID at startup and stays attached for its whole life (it never respawns cdb per query). The only difference between the two modes is the transport:

                       ┌──────────────────────────────────────────┐
   AI client ─stdio/──▶│  cdbmcp.exe (standalone, long-running)   │
   HTTP                │   · attaches to target PID at startup    │──▶ live C++ target (+PDB)
  (Claude Code /       │   · resident cdb, read-only              │
   Cursor / remote /   │   · stdio  OR  streamable-http + bearer  │
   many at once)       └──────────────────────────────────────────┘
  • stdio (default): the MCP client launches cdbmcp as its own subprocess and talks over stdin/stdout. Simplest for a local Claude Code / Cursor.
  • HTTP: you start cdbmcp yourself as a long-running service; many and/or remote clients then connect to http://<host>:<port>/mcp. This is the model when cdbmcp should be one shared process serving several AI clients.

Common flags (both modes): --pid <PID> (required), --cdb-path D:\Debuggers\cdb.exe, --symbol-path "srv*C:\Symbols;D:\your\app", --pause-mode per_query|frozen, --config config.json. Run python -m cdbmcp --help for the full list.

stdio

python -m cdbmcp --pid <PID> --cdb-path D:\Debuggers\cdb.exe

Client config (Claude Code / Cursor) — client launches cdbmcp:

{
  "mcpServers": {
    "cdbmcp": { "command": "python",
                "args": ["-m", "cdbmcp", "--pid", "12345",
                         "--cdb-path", "D:\\Debuggers\\cdb.exe"] }
  }
}

HTTP (standalone service, multi/remote client)

Start cdbmcp once as a service (it attaches immediately and stays up):

python -m cdbmcp --transport http --host 0.0.0.0 --port 8000 ^
    --pid <PID> --cdb-path D:\Debuggers\cdb.exe ^
    --symbol-path "srv*C:\Symbols;D:\your\app" ^
    --auth-token SECRET

Endpoint: POST http://<host>:8000/mcp (MCP streamable-http). Clients connect to the URL with the bearer token:

{
  "mcpServers": {
    "cdbmcp": { "url": "http://your-host:8000/mcp",
                "headers": { "Authorization": "Bearer SECRET" } }
  }
}

Security: evaluate reads arbitrary target memory, so any remote exposure must be authenticated. Without --auth-token / CDBMCP_TOKEN cdbmcp warns and forces 127.0.0.1-only. For production put a reverse proxy (nginx/caddy, TLS) in front and bind loopback — otherwise the token travels in cleartext. Queries are serialized through one cdb session, so concurrent clients queue cleanly rather than stepping on each other.

The tools

All three are called the same way — a tools/call JSON-RPC over stdio or HTTP, after the standard initialize / notifications/initialized handshake.

End-to-end demos (build the fixture first, see below): python tests/demo_ai_session.py drives cdbmcp over stdio and python tests/demo_http_session.py drives a standalone HTTP cdbmcp, each running real AI-style queries against the fixture.

evaluate(expression, format="text")

Evaluate a read-only C++ expression. text is a WinDbg-style recursive dump; json returns a typed tree {name, type, value|children} parsed from cdb's dx/NatVis — scalars coerced to int/float/bool, std::string shown as its content, structs/containers recursed (depth/width capped), STL bookkeeping ([Raw View]/size/capacity/allocator…) pruned. On error returns a plain diagnostic string; server, debugger and target never crash.

Inferior function calls (--allow-calls)

cdb's C++ evaluator (??) cannot call functions, so an expression like GameWorld::Instance()->players fails with "Extra character error". With --allow-calls (or "allow_inferior_calls": true in config) cdbmcp resolves a leading accessor call via cdb .call (it runs the function in the target, reads the returned pointer) and rewrites the rest to member access — so GameWorld::Instance()->players, map_get("GameWorld::Instance()->players", "1001") etc. work. Nested calls (A()->B()) are not flattened; prefer the leading accessor + member access + the container tools.

⚠️ .call runs target code (side effects, possible deadlock) and breaks the read-only guarantee — hence OFF by default. cdbmcp prints a warning when it is on. Only the leading call is executed; everything after it stays member-access-only.

map_get(map_expr, key_expr, format="text")

Look up one element by key in std::map / multimap without calling any inlined STL method. std::map/multimap (red-black tree) and std::unordered_map/unordered_multimap (_Hash element chain) are walked directly and the key is compared in-engine (first == key_expr). Struct keys without a usable == fall back to a dx dump. Use a 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 (tree), unordered_* (_Hash chain) and vector are all walked directly (no inferior calls); anything unrecognised falls back to dx/NatVis.

Test fixture

tests/fixtures/target.cpp is a tiny C++ process with a singleton GameWorld holding std::map<int,Player>, std::unordered_map<int,Item> and std::vector<Item>, plus out-of-line accessors.

cd tests\fixtures
build.bat               :: produces target.exe + target.pdb
target.exe              :: prints PID=..., then idles
:: in another shell:
python -m cdbmcp --pid <that PID>

Architecture / code map

File Role
cdbmcp/cdb_controller.py Resident cdb subprocess; attach / interrupt / go / detach; query serialization; watchdog break
cdbmcp/cdb_protocol.py Byte-oriented reader with sentinel-based delimiting (cdb has no GDB/MI)
cdbmcp/evaluator.py evaluate: validate → interrupt → ?? (text) / dx (json tree) → resume; resolve_calls (.call, opt-in); error mapping
cdbmcp/container_query.py map_get / enumerate: STL walker first, dx/NatVis fallback
cdbmcp/stl_walker.py Walk MSVC STL tree / unordered (_Hash) / vector internals via the C++ EE — no inferior calls
cdbmcp/formatters.py text/json rendering; prompt/banner cleanup; dx tree → typed JSON parser
cdbmcp/watchdog.py Cancellable wall-clock timer → forced break
cdbmcp/errors.py Error classification + friendly messages
cdbmcp/auth.py Shared-secret bearer auth + pure-ASGI middleware (HTTP transport)
cdbmcp/http_transport.py Streamable-HTTP transport (uvicorn) with bearer auth
cdbmcp/lifecycle.py atexit + console-control handlers → clean detach
cdbmcp/mcp_server.py FastMCP server registering the three tools
cdbmcp/config.py Config load (JSON + CLI + cdb discovery)
cdbmcp/__main__.py CLI entry: parse args, attach, serve stdio or HTTP

Differences from gdbmcp

  • Engine: GDB/MI subprocess → resident cdb.exe subprocess (text protocol with sentinel delimiting, since cdb has no MI).
  • STL layout: libstdc++/libc++ → MSVC STL. Tree (_Tree), unordered (_Hash chain) and vector internals are walked directly via the C++ EE (no inferior calls); dx/NatVis is the fallback for anything else.
  • Interrupt: -exec interruptGenerateConsoleCtrlEvent (Ctrl-Break to cdb's process group). A raw 0x03 byte on cdb's piped stdin does NOT break a running target — console control events don't flow through pipes — so cdbmcp launches cdb in its own process group and signals it (allocating a console when cdbmcp itself has none, e.g. under an MCP client).
  • Symbols: DWARF/-gPDB + _NT_SYMBOL_PATH / --symbol-path.
  • Attach overhead: a live C++ target raises first-chance exceptions constantly; cdbmcp runs sxn eh so the engine does not break on them.
  • Transports: stdio (default) and HTTP (streamable-http) with shared-secret bearer auth.

推荐服务器

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 模型以安全和受控的方式获取实时的网络信息。

官方
精选