C++构建工具服务器

一个封装C++构建系统(CMake/Ninja/Make)的MCP服务器,为AI编码代理提供结构化、高效的构建错误诊断工具。

  • by danweinerdev
  • Go
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 danweinerdev/cpp-build-mcp

使用说明

cpp构建mcp

一个MCP服务器,它封装了C++构建系统(CMake/Ninga/Make),并为AI编码代理提供结构化、令牌高效的工具。编译器输出被预处理为结构化诊断,因此原始构建日志永远不会进入AI上下文窗口。

核心回路: 配置 -> 构建 -> get_errors ->修复-> 构建 --每个响应少于约500个令牌。

建筑

graph TD
    AI[AI Agent / Claude] -->|MCP stdio| MAIN[main.go<br/>MCP Server]

    MAIN --> BUILD[builder/]
    MAIN --> DIAG[diagnostics/]
    MAIN --> STATE[state/]
    MAIN --> GRAPH[graph/]
    MAIN --> CHANGES[changes/]
    MAIN --> CONFIG[config/]

    BUILD --> CMAKE[cmake.go<br/>CMake + Ninja]
    BUILD --> MAKE[make.go<br/>GNU Make]
    BUILD --> DETECT[detect.go<br/>Auto-detection]

    DIAG --> CLANG[clang.go<br/>JSON parser]
    DIAG --> GCC[gcc.go<br/>JSON parser]
    DIAG --> REGEX[regex.go<br/>Fallback parser]

    STATE --> STORE[store.go<br/>Thread-safe state<br/>sync.RWMutex]

    CONFIG --> JSON_CFG[.cpp-build-mcp.json<br/>+ env var overrides]

    GRAPH --> CC[compile_commands.json<br/>reader / summarizer]

    style AI fill:#e1f0ff,stroke:#4a90d9
    style MAIN fill:#f0f0f0,stroke:#666

构建流程

sequenceDiagram
    participant Agent as AI Agent
    participant MCP as MCP Server
    participant Builder as Builder
    participant Parser as Diagnostic Parser
    participant Store as State Store

    Agent->>MCP: build(targets, jobs)
    MCP->>Store: StartBuild()
    Store-->>MCP: ok / error

    MCP->>Builder: Build(ctx, targets, jobs)
    Builder-->>MCP: BuildResult{stdout, stderr, exit_code}

    MCP->>Parser: Parse(toolchain, stdout, stderr)
    Parser-->>MCP: []Diagnostic

    MCP->>Store: FinishBuild(exit_code, errors, warnings)

    MCP-->>Agent: {exit_code, error_count, warning_count, duration_ms, files_compiled}

    opt exit_code != 0
        Agent->>MCP: get_errors()
        MCP->>Store: Errors()
        Store-->>MCP: []Diagnostic
        MCP-->>Agent: {errors: [{file, line, severity, message, ...}]}
    end

状态机

stateDiagram-v2
    [*] --> Unconfigured
    Unconfigured --> Configured : configure()
    Configured --> Built : build()
    Built --> Configured : clean()
    Built --> Built : build()

    Built --> Dirty : timeout / kill
    Dirty --> Configured : next build auto-cleans

    note right of Dirty
        SIGTERM sent first,
        SIGKILL after 3s grace.
        Next build runs --clean-first.
    end note

安装

go install github.com/danweinerdev/cpp-build-mcp@latest

需要Go 1.22+和C++构建工具链(CMake+Ninja或GNU Make)。

使用Claude代码

添加一个 .mcp.json 将文件保存到C++项目根目录:

{
  "mcpServers": {
    "cpp-build": {
      "command": "cpp-build-mcp",
      "args": []
    }
  }
}

然后创建一个 .cpp-build-mcp.json config(可选——默认值适用于标准CMake项目):

{
  "build_dir": "build",
  "generator": "ninja",
  "cmake_args": ["-DCMAKE_BUILD_TYPE=Debug"]
}

配置后,Claude Code可以直接使用构建工具。一个典型的会话看起来像:

You: "Build the project and fix any errors"

Claude calls: configure()
  -> {success: true, error_count: 0, messages: []}

Claude calls: build()
  -> {exit_code: 1, error_count: 2, warning_count: 1, duration_ms: 1420, files_compiled: 5}

Claude calls: get_errors()
  -> {errors: [
       {file: "src/main.cpp", line: 42, column: 10, severity: "error",
        message: "no member named 'push' in 'std::vector<int>'"},
       {file: "src/util.cpp", line: 17, column: 5, severity: "error",
        message: "use of undeclared identifier 'result'"}
     ]}

Claude calls: suggest_fix(error_index: 0)
  -> {file: "src/main.cpp", start_line: 32, end_line: 52, source: "...", diagnostic: {...}}

Claude fixes the code, then:

Claude calls: build()
  -> {exit_code: 0, error_count: 0, warning_count: 0, duration_ms: 380, files_compiled: 2}

两步式设计(build 然后 get_errors)是故意的-- build 返回一个简洁的摘要,这样成功的构建(常见情况)只需要最少的令牌。仅在需要时才获取完整诊断。

克劳德桌面版

添加到您的 claude_desktop_config.json:

{
  "mcpServers": {
    "cpp-build": {
      "command": "cpp-build-mcp",
      "args": [],
      "cwd": "/path/to/your/cpp/project"
    }
  }
}

工具

工具 参数 响应
configure config?: string, cmake_args?: string[] {config, success, error_count, messages}
build config?: string, targets?: string[], jobs?: number {config, exit_code, error_count, warning_count, duration_ms, files_compiled}
get_errors config?: string {config, errors: [{file, line, column, severity, message, code}]}
get_warnings config?: string, filter?: string {config, warnings: [...], count}
suggest_fix config?: string, error_index: number {config, file, start_line, end_line, source, diagnostic}
clean config?: string, targets?: string[] {config, success, message}
get_changed_files config?: string {config, files, count, method}
get_build_graph config?: string {config, available, file_count, translation_units, include_dirs}
list_configs (无) {configs: [{name, build_dir, status}], default_config}

所有工具都接受可选 config 参数,以针对特定的命名配置。如果省略,则使用默认配置。每个回复都包含一个 config 标识对哪种配置进行了操作的字段。

资源: build://health --一行状态字符串。使用单个配置: OK: 0 errors, 2 warnings, last build 30s ago.具有多种配置:管道分离式骨料,如 debug: OK | release: FAIL(3 errors).

工具详情

build --运行增量构建。解析忍者 [N/M] 要报告的进度线 files_compiled。如果前一个构建已被终止(脏状态),则会自动首先进行清理。构建超时是可配置的(默认5分钟);在超时时,发送SIGTERM,在SIGKILL之前有3秒的宽限期。

get_errors --从上次构建中返回最多20个结构化诊断。每个条目包括文件路径、行、列、严重性、消息和诊断代码。错误是从JSON(Clang/GCC 10+)或正则表达式(MSVC/legacy)解析的。

suggest_fix --给定一个错误索引,读取源文件并返回错误位置周围+/-10行上下文。有助于在不读取整个文件的情况下理解诊断代码。

get_changed_files --使用检测自上次成功构建以来更改的文件 git diff (首选)或mtime比较(回退)。这 method 字段指示使用了哪种检测。

get_build_graph --总结 compile_commands.json:文件计数、翻译单位、包括目录。退货 available: false 用于Make项目或未配置的CMake项目。

配置

.cpp-build-mcp.json 在项目根中。所有可选字段:

字段 类型 默认值 描述
build_dir 字符串 "build" 构建输出目录
source_dir 字符串 "." 源目录
toolchain 字符串 "auto" "auto", "clang", "gcc", "msvc"
generator 字符串 "ninja" "ninja" 或 "make"
preset 字符串 "" CMake预设名称(空=无预设)
cmake_args string[] [] 额外的CMake配置参数
build_timeout 字符串 "5m" 最大构建持续时间(Go持续时间格式)
inject_diagnostic_flags bool的。 true 通过以下方式注入编译器诊断格式标志 CMAKE_项目_包括
diagnostic_serial_build bool的。 false 武力 -j1 更清晰的诊断输出
configs 对象 (无) 命名配置图(参见 多种构建配置)
default_config 字符串 (按字母顺序排列) 默认配置名称 configs 存在

环境变量覆盖

这些优先于配置文件:

  • CPP_BUILD_MCP_BUILD_DIR
  • CPP_BUILD_MCP_SOURCE_DIR
  • CPP_BUILD_MCP_TOOLCHAIN
  • CPP_BUILD_MCP_GENERATOR
  • CPP_BUILD_MCP_BUILD_TIMEOUT

多种构建配置

服务器支持同时管理多个命名的构建配置。每种配置都有自己的构建目录、状态机和诊断程序——它们彼此完全隔离。

具有多种配置的配置文件

使用 configs 映射到 .cpp-build-mcp.json 定义命名配置。顶级字段是每个配置继承并可以覆盖的默认值:

{
  "source_dir": ".",
  "toolchain": "auto",
  "generator": "ninja",
  "configs": {
    "debug": {
      "build_dir": "build/debug",
      "cmake_args": ["-DCMAKE_BUILD_TYPE=Debug"]
    },
    "release": {
      "build_dir": "build/release",
      "cmake_args": ["-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON"]
    },
    "asan": {
      "build_dir": "build/asan",
      "cmake_args": ["-DCMAKE_BUILD_TYPE=Debug", "-DCMAKE_CXX_FLAGS=-fsanitize=address"]
    }
  },
  "default_config": "debug"
}

每个配置都必须有一个唯一的 build_dirThe default_config 字段选择在否时使用哪种配置 config 参数在工具调用中指定。如果省略,则使用按字母顺序排列的第一个配置。

在多配置模式下禁用环境变量重写,以保持构建目录的唯一性。

多配置会话演练

与AI代理的典型多配置会话:

You: "Build in both debug and release, fix any errors"

Claude calls: list_configs()
  -> {configs: [
       {name: "debug", build_dir: "build/debug", status: "unconfigured"},
       {name: "release", build_dir: "build/release", status: "unconfigured"}
     ], default_config: "debug"}

Claude calls: configure(config: "debug")
  -> {config: "debug", success: true, error_count: 0, messages: []}

Claude calls: configure(config: "release")
  -> {config: "release", success: true, error_count: 0, messages: []}

Claude calls: build(config: "debug")
  -> {config: "debug", exit_code: 1, error_count: 2, warning_count: 0, duration_ms: 1420, files_compiled: 5}

Claude calls: build(config: "release")
  -> {config: "release", exit_code: 0, error_count: 0, warning_count: 1, duration_ms: 980, files_compiled: 5}

Claude calls: get_errors(config: "debug")
  -> {config: "debug", errors: [
       {file: "src/main.cpp", line: 42, severity: "error", message: "..."},
       {file: "src/util.cpp", line: 17, severity: "error", message: "..."}
     ]}

Claude reads: build://health
  -> "debug: FAIL(2 errors) | release: OK"

Claude fixes the errors, then:

Claude calls: build(config: "debug")
  -> {config: "debug", exit_code: 0, error_count: 0, warning_count: 0, duration_ms: 380, files_compiled: 2}

Claude reads: build://health
  -> "debug: OK | release: OK"

状态在配置之间完全隔离:构建调试不会影响版本的状态,一个配置的错误永远不会出现在另一个配置中 get_errors 输出。

CMake预设

如果你的项目有 CMakePresets.json 文件中,服务器会自动为每个非隐藏配置预设创建一个命名配置。预设的 binaryDir 和 generator 用于每个配置 build_dir 和 generator.中的任何字段 .cpp-build-mcp.json (除 build_dir, generator,以及 preset)在所有预设的派生配置中合并为默认值。

支持的工具链

graph LR
    AUTO[toolchain: auto] --> CC[compile_commands.json]
    CC -->|contains clang| CLANG[Clang JSON Parser]
    CC -->|contains gcc/g++| VER{GCC version?}
    VER -->|>= 10| GCC_JSON[GCC JSON Parser]
    VER -->|< 10| REGEX[Regex Fallback]
    CC -->|contains cl.exe| REGEX
    CC -->|not found| SYS[System compiler probe]
    SYS --> CLANG
    SYS --> GCC_JSON
    SYS --> REGEX

    style CLANG fill:#d4edda,stroke:#28a745
    style GCC_JSON fill:#d4edda,stroke:#28a745
    style REGEX fill:#fff3cd,stroke:#ffc107
工具链 解析器 格式 诊断源
Clang 14+ 萨里夫(-fdiagnostics-format=sarif) SARIF 2.1.0 标准输出或标准错误
Clang<14 JSON(-fdiagnostics-format=json) JSON数组 stdout
gcc10+ JSON(-fdiagnostics-format=json) JSON数组 stdout(<15)或stderr(15+)
GCC<10 正则表达式回退(自动检测) 基于行 stderr
MSVC 正则表达式回退 基于行 stderr

当 toolchain 是 "auto" (默认),服务器检查 compile_commands.json 并探索系统编译器以选择最佳解析器。GCC<10通过版本探测自动检测,并禁用诊断标志注入,以避免传递不受支持的标志。

Clang解析器通过检查输出的第一个非空白字符来自动检测格式: { 表示SARIF, [ 表示原生Clang JSON。这两个解析器首先检查stdout,然后回退到stderr,处理GCC 15+,将JSON输出移动到stderr。

诊断格式注入

当 inject_diagnostic_flags 是 true (默认),服务器会自动配置编译器,使其在以下过程中发出结构化诊断 configure这是一种无需手动设置编译器标志即可使一切正常工作的机制。

运作原理

在...期间 configure,服务器:

  1. 写 嵌入式CMake模块(builder/diagnostic_format.cmake)进入构建目录 <build_dir>/.cpp-build-mcp/DiagnosticFormat.cmake
  2. 通过 -DCMAKE_PROJECT_INCLUDE=<absolute_path> 到cmake,这会导致模块在每次 project() 呼叫
  3. 模块 探针 用于结构化诊断支持的主动C和C++编译器:
    • 尝试GCC风格 -fdiagnostics-format=json 第一个(GCC 10+)
    • 回归Clang风格 -fdiagnostics-format=sarif 随着 -Wno-sarif-format-unstable (部落14+)
  4. 如果支持某种格式,则标记为 附于 CMAKE_C_FLAGS 和 CMAKE_CXX_FLAGS 全球范围内

模块运行后,这些CMake变量可供项目使用:

变量 描述
CPP_BUILD_MCP_DIAG_SUPPORTED TRUE 如果结构化诊断可用
CPP_BUILD_MCP_DIAG_FORMAT "sarif", "json",或 ""
CPP_BUILD_MCP_DIAG_C_FLAGS 附加到C编译器的标志
CPP_BUILD_MCP_DIAG_CXX_FLAGS 附加到C++编译器的标志

何时禁用注射

集 inject_diagnostic_flags 到 false 如果您的项目本身已经配置了结构化诊断标志(例如,通过工具链文件或 CMakeLists.txt).项目可以检查 CPP_BUILD_MCP_DIAG_SUPPORTED 检测服务器是否已经处理了注入,并跳过自己的探测。

对于基于Make的项目

使项目不使用 CMAKE_PROJECT_INCLUDE相反,服务器将诊断标志直接附加到 CFLAGS 和 CXXFLAGS 调用前的环境变量 make根据配置的工具链选择标志: -fdiagnostics-format=sarif -Wno-sarif-format-unstable 对于Clang来说, -fdiagnostics-format=json GCC。

运作原理

服务器位于AI代理和构建系统之间。而不是代理看到原始编译器输出,如:

/home/user/project/src/main.cpp:42:10: error: no member named 'push' in 'std::vector<int>'
    vec.push(42);
        ^~~~
/home/user/project/src/main.cpp:42:10: note: did you mean 'push_back'?

它接收结构化JSON:

{"exit_code": 1, "error_count": 1, "warning_count": 0, "duration_ms": 820, "files_compiled": 3}

只获取它需要的细节:

{"errors": [{"file": "src/main.cpp", "line": 42, "column": 10, "severity": "error",
  "message": "no member named 'push' in 'std::vector<int>'"}]}

这使AI上下文窗口保持干净——没有多页构建日志,没有ANSI颜色代码,没有重复的模板实例化噪声(GCC子项的深度上限为3)。