Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Attach MCP to an Existing C++ gRPC Server

This is a C++ project built with Make. The developer writes pure C++ (gRPC server, business logic). The MCP protocol layer is generated by protoc-gen-mcp, which produces the entire Rust project, C++ adapter, and Makefile. You run make to build.

What the Developer Writes vs. What Is Generated

graph LR
    subgraph "src/ — YOUR CODE"
        direction TB
        A["grpc_server.h / grpc_server.cc"]
        B["todo_store.h"]
    end

    subgraph "proto/generated/cpp/ — ALL GENERATED"
        direction TB
        C["todo/v1/*.pb.h/cc · *.grpc.pb.h/cc<br/><i>protoc + grpc_cpp_plugin</i>"]
        D["todo/v1/todo_service.mcp.h/cc<br/><i>protoc-gen-mcp — C++ gRPC client</i>"]
        E["rust/Cargo.toml · build.rs · lib.rs · mcp_handler.rs<br/><i>protoc-gen-mcp — full Rust project</i>"]
        F["rust/mcp_include.h<br/><i>protoc-gen-mcp — C++ include for MCP FFI</i>"]
        G["main.cc · Makefile<br/><i>protoc-gen-mcp — entry point + build</i>"]
    end

    A -- "uses" --> B
    G -- "includes" --> F
    G -- "includes" --> A
    E -- "cxx FFI" --> D
    D -- "gRPC call" --> A
Loading

Architecture

graph TB
    subgraph Clients
        GC["gRPC Client<br/><i>grpcurl, Go, Python, …</i>"]
        MC["MCP Client<br/><i>Claude, Cursor, …</i>"]
    end

    subgraph "Single Binary — built with make"
        direction TB

        subgraph "User C++ (src/)"
            GRPC["TodoServiceGrpcImpl<br/><code>grpc_server.cc</code>"]
            STORE[("TodoStore<br/><code>todo_store.h</code>")]
        end

        subgraph "Generated"
            MAIN["main.cc<br/><i>starts gRPC + MCP</i>"]
            ADAPTER["TodoServiceMcpImpl<br/><code>todo_service.mcp.cc</code><br/><i>C++ gRPC client — JSON ↔ protobuf</i>"]
            BRIDGE["cxx::bridge<br/><code>rust/lib.rs</code>"]
            HANDLER["rmcp ServerHandler<br/><code>rust/mcp_handler.rs</code>"]
        end
    end

    GC -- "protobuf / HTTP/2<br/>:50051" --> GRPC
    MC -- "JSON-RPC / streamable-http<br/>:8082" --> HANDLER

    MAIN -- "starts" --> GRPC
    MAIN -- "attaches MCP" --> BRIDGE

    BRIDGE --> HANDLER
    HANDLER -- "cxx FFI (JSON)" --> ADAPTER
    ADAPTER -- "gRPC channel<br/>localhost:50051" --> GRPC
    GRPC --> STORE
Loading

Build Flow

sequenceDiagram
    participant User
    participant Make
    participant Cargo
    participant CXX

    User->>Make: make
    Make->>Cargo: cd rust && cargo build --release
    Cargo->>Cargo: build.rs compiles C++ bridge + proto stubs
    Cargo->>Cargo: Exports cxx headers to rust/target/cxx_include
    Make->>CXX: Compile main.cc, grpc_server.cc, *.mcp.cc, *.pb.cc
    Make->>CXX: Link with libtodo_v1_mcp_cpp.a + gRPC + protobuf
    Make->>User: server binary
Loading

Build & Run

# Prerequisites (macOS)
brew install grpc protobuf

# Regenerate (from examples/)
buf generate --path proto/todo

# First time: copy C++ templates to src/
cp template/grpc_server.h template/grpc_server.cc template/todo_store.h src/

# Build (from cpp/)
make

# Run — gRPC on :50051, MCP on :8082
# Server binary is at cpp/server (built in cpp/)
./server

# Run — stdio transport (for MCP Inspector)
MCP_TRANSPORT=stdio ./server

# Run with MCP Inspector
MCP_TRANSPORT=stdio npx @modelcontextprotocol/inspector -- ./server

Troubleshooting

"MCP HTTP server failed" / "Address already in use" — Port 8082 is already in use (e.g. another server instance). Either:

  • Kill the process: lsof -i :8082 then kill <PID>
  • Use a different port: MCP_PORT=8083 ./server

Environment Variables

Variable Default Description
GRPC_ADDR [::]:50051 gRPC server listen address
MCP_TRANSPORT http http or stdio
MCP_HOST 0.0.0.0 MCP HTTP bind address
MCP_PORT 8082 MCP HTTP port
MCP_BASE_PATH /todo/v1/todoservice/mcp MCP HTTP base path

Project Layout

graph TD
    subgraph "examples/"
        direction TB
        CPP["cpp/ — C++ project"]
        GEN["proto/generated/cpp/ — generated sources only"]
    end

    subgraph "cpp/"
        TPL["template/ — C++ reference templates"]
        SRC["src/ — USER CODE (copy from template/)"]
        MF["Makefile — build root, binary outputs here"]
    end

    subgraph "template/ — C++ only"
        TPL_GRPC["grpc_server.h/cc"]
        TPL_STORE["todo_store.h"]
    end

    subgraph "src/ — pure C++"
        GRPC_S["grpc_server.h/cc — your gRPC service"]
        STORE_H["todo_store.h — your data store"]
        MAIN_OVERRIDE["main.cc — optional override"]
    end

    subgraph "proto/generated/cpp/"
        MF_GEN["Makefile — delegates to cpp/"]
        MAIN_GEN["main.cc — entry point"]
        PROTO["todo/v1/*.pb.h/cc · *.grpc.pb.h/cc"]
        MCP_CPP["todo/v1/todo_service.mcp.h/cc"]
        RUST["rust/ — Cargo.toml, build.rs, lib.rs, mcp_handler.rs, mcp_include.h"]
    end

    TPL --> TPL_GRPC & TPL_STORE
    SRC --> GRPC_S & STORE_H
    GEN --> MF_GEN & MAIN_GEN & PROTO & MCP_CPP & RUST
Loading
Path What Who writes it
template/ Reference C++ templates. Copy to src/ to start. Reference
src/grpc_server.h/cc Your gRPC service. Pure C++, zero FFI. You
src/todo_store.h Your data store. You
src/main.cc Optional. If present, overrides generated main.cc. You (optional)
proto/generated/cpp/main.cc C++ entry point. Starts gRPC, attaches MCP. protoc-gen-mcp
proto/generated/cpp/Makefile Delegates to cpp/. protoc-gen-mcp
cpp/Makefile Build root. Invokes cargo, compiles C++, links. Binary → cpp/server Project
proto/generated/cpp/rust/* Full Rust project (Cargo.toml, build.rs, lib.rs, mcp_handler.rs, mcp_include.h). protoc-gen-mcp
proto/generated/cpp/todo/v1/*.mcp.h/cc C++ gRPC-client adapter. JSON ↔ protobuf. protoc-gen-mcp

Customizing main.cc

The generated main.cc reads env vars and starts gRPC + MCP. To customize, copy it to src/main.cc and edit. The Makefile uses src/main.cc if it exists, otherwise proto/generated/cpp/main.cc.