A C++ implementation of the Model Context Protocol (MCP) server, conforming to MCP specification version 2025-03-26. Supports four transport modes: stdio, SSE, Streamable HTTP, and local pipe (Windows Named Pipe / Unix Domain Socket), using JSON-RPC 2.0 for protocol communication.
- Four transport modes: stdio, SSE (legacy HTTP), Streamable HTTP, local pipe
- Fully async I/O: asio unified event loop + llhttp HTTP parser, zero thread kidnapping, zero polling
- Full MCP protocol: Tools, Resources, Prompts, Completions, Logging
- Progress reporting: real-time progress notifications during tool calls
- Cross-platform: Windows (MSVC 2019+) and Linux (GCC 11+)
- C++17: no advanced feature dependencies, VS2019 compatible
- Lightweight deps: only nlohmann-json, asio, llhttp
- Full test coverage: 36 unit tests + 18 real I/O integration tests, verified on Windows & Linux
- CMake 3.15+
- C++17 compiler (MSVC 2019, GCC 11, Clang 14)
- vcpkg package manager
git clone https://github.com/your-org/cppmcp.git
cd cppmcp
vcpkg installcmake -B build \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
-DCPPMCP_BUILD_TESTS=ON \
-DCPPMCP_BUILD_EXAMPLES=ON
cmake --build build# Unit tests only (fast)
cd build && ctest -E integration --output-on-failure
# Integration tests (real I/O, slower)
cmake -B build -DCPPMCP_BUILD_INTEGRATION_TESTS=ON ...
cd build && ctest -L integration --output-on-failure
# All tests
cd build && ctest --output-on-failuredocker compose up build # Build and run tests
docker compose run dev bash # Interactive dev shellcppmcp/
├── include/cppmcp/ # Public headers
│ ├── server.hpp # McpServer core class
│ ├── transport.hpp # ITransport abstract interface
│ ├── types.hpp # MCP types (Tool, Resource, Prompt, etc.)
│ ├── jsonrpc.hpp # JSON-RPC 2.0 message parsing
│ ├── protocol.hpp # Protocol constants & method names
│ ├── context.hpp # RequestContext (progress, logging)
│ ├── exception.hpp # Exception types
│ ├── common.hpp # RequestId variant & utilities
│ ├── stdio_transport.hpp # StdioTransport
│ ├── http_transport.hpp # HttpTransport (SSE + Streamable HTTP)
│ └── local_pipe_transport.hpp # LocalPipeTransport
├── src/ # Implementation files
├── examples/ # Example servers
│ ├── simple_stdio_server/
│ ├── streamable_http_server/
│ ├── http_sse_server/
│ └── local_pipe_server/
├── tests/ # Unit & integration tests
│ ├── test_*.cpp # 36 unit tests (TestTransport mock)
│ └── integration/ # 18 real I/O integration tests
├── docs/zh-CN/ # Chinese documentation
├── Dockerfile # Docker multi-stage build
├── docker-compose.yml
├── vcpkg.json
└── CMakeLists.txt
#include <cppmcp/server.hpp>
#include <cppmcp/stdio_transport.hpp>
#include <cppmcp/types.hpp>
using namespace cppmcp;
int main() {
Implementation info{"my_server", "1.0.0"};
ServerCapabilities caps;
caps.tools = ToolsCapability{};
McpServer server(info, caps);
Tool echo_tool;
echo_tool.name = "echo";
echo_tool.description = "Echoes the input message";
echo_tool.input_schema = R"({
"type": "object",
"properties": {"message": {"type": "string"}},
"required": ["message"]
})"_json;
server.register_tool("echo", echo_tool,
[](const nlohmann::json& args, RequestContext&) -> CallToolResult {
CallToolResult result;
result.content.push_back(TextContent{"text", args["message"].get<std::string>()});
return result;
});
auto transport = std::make_shared<StdioTransport>();
server.connect(transport);
server.run();
}#include <cppmcp/server.hpp>
#include <cppmcp/http_transport.hpp>
HttpTransportConfig config;
config.mode = HttpTransportMode::StreamableHttp;
config.host = "127.0.0.1";
config.port = 3000;
config.path = "/mcp";
auto transport = std::make_shared<HttpTransport>(config);
server.connect(transport);
server.run();HttpTransportConfig config;
config.mode = HttpTransportMode::SSE;
config.host = "127.0.0.1";
config.port = 3001;
config.sse_path = "/sse";
config.message_path = "/messages";
auto transport = std::make_shared<HttpTransport>(config);
server.connect(transport);
server.run();#include <cppmcp/local_pipe_transport.hpp>
LocalPipeConfig config;
config.pipe_name = "my_mcp_pipe";
config.mode = PipeMode::SingleClient; // or PipeMode::MultiClient
auto transport = std::make_shared<LocalPipeTransport>(config);
server.connect(transport);
server.run();
// Windows: \\.\pipe\my_mcp_pipe
// Linux: /tmp/my_mcp_pipe.sockserver.register_tool("long_task", tool_def,
[](const nlohmann::json& args, RequestContext& ctx) -> CallToolResult {
for (int i = 0; i < 10; ++i) {
ctx.report_progress(i / 10.0, 1.0);
}
CallToolResult result;
result.content.push_back(TextContent{"text", "Done"});
return result;
});Resource doc;
doc.name = "readme";
doc.uri = "file:///readme.md";
doc.description = "Project documentation";
server.register_resource("file:///readme.md", doc,
[](const std::string& uri, RequestContext&) -> ReadResourceResult {
ReadResourceResult result;
ResourceContents rc;
rc.uri = uri;
rc.mime_type = "text/plain";
rc.text = "Documentation content...";
result.contents.push_back(rc);
return result;
});Prompt greet;
greet.name = "greet";
greet.description = "Generate a greeting";
PromptArgument name_arg;
name_arg.name = "name";
name_arg.required = true;
greet.arguments = {name_arg};
server.register_prompt("greet", greet,
[](const std::string&, const nlohmann::json& args, RequestContext&) -> GetPromptResult {
GetPromptResult result;
result.description = "Greeting";
result.messages.push_back(PromptMessage{
"user", TextContent{"text", "Hello, " + args["name"].get<std::string>()}
});
return result;
});Main server class, manages MCP protocol lifecycle and request processing.
| Method | Description |
|---|---|
McpServer(info, caps) |
Construct with server info & capability declaration |
register_tool(name, def, handler) |
Register a tool |
register_resource(uri, def, handler) |
Register a resource |
register_prompt(name, def, handler) |
Register a prompt |
register_completion(handler) |
Register completion handler |
connect(transport) |
Connect a transport |
run() |
Run server (blocking) |
stop() |
Stop server |
is_running() |
Check server running state |
notify_tools_list_changed() |
Notify tool list changed |
notify_resources_list_changed() |
Notify resource list changed |
notify_resources_updated(uri) |
Notify resource content updated |
notify_prompts_list_changed() |
Notify prompt list changed |
Abstract transport interface. All transport implementations inherit from this.
| Method | Description |
|---|---|
start() |
Start transport |
stop() |
Stop transport |
is_running() |
Check running state |
send_message(json) |
Send message |
set_message_handler(cb) |
Set message callback |
set_error_handler(cb) |
Set error callback |
set_io_context(io_ctx) |
Set asio io_context |
set_response_sender(sender) |
Set response sender |
| Method | Description |
|---|---|
get_port() |
Get actual listening port (supports port=0 random assignment) |
| Mode | Use case | Connection |
|---|---|---|
| stdio | Embedded processes, CLI tools | stdin/stdout, newline-delimited JSON-RPC |
| Streamable HTTP | Web services, remote calls | POST /mcp, GET SSE stream, DELETE disconnect |
| SSE | Legacy client compatibility | GET /sse for events, POST /messages for requests |
| Local pipe | High-performance local IPC | Windows: Named Pipe, Linux: Unix Domain Socket |
MCP-recommended HTTP mode. Single-endpoint architecture:
POST /mcp— send request, response in HTTP body or SSE streamGET /mcp— establish SSE stream for server-push notificationsDELETE /mcp— terminate session- Session management via
mcp-session-idheader
High-performance local inter-process communication:
- SingleClient: single connection mode, one-to-one communication
- MultiClient: multi-connection mode, up to
max_instancesconcurrent connections - Windows uses Overlapped I/O (
FILE_FLAG_OVERLAPPED+ asio stream_handle), Linux uses asio local::stream_protocol
The project contains 54 Google Test tests across two tiers:
Using TestTransport mock (no real I/O):
- JSON-RPC 2.0 parsing & serialization (10)
- MCP type serialization (9)
- Server core logic (8)
- Integration: Resources, Prompts, notifications (9)
Using real I/O transports, verified on Windows & Linux:
- LocalPipe real connections: ConnectAndInitialize, PingAfterInit, ToolsCall, MultipleSequentialRequests (4)
- Streamable HTTP real requests: Initialize, Ping, ToolsCall, SessionId, Notification202, Delete (6)
- SSE real streaming: ConnectGetEndpoint, InitializeViaPost, ToolsCallViaSse, ResourcesRead (4)
- Stdio subprocess pipes: InitializeAndPing, ToolsCall, ToolsList, MalformedJson (4)
| Option | Default | Description |
|---|---|---|
CPPMCP_BUILD_EXAMPLES |
ON | Build example servers |
CPPMCP_BUILD_TESTS |
OFF | Build unit tests |
CPPMCP_BUILD_INTEGRATION_TESTS |
OFF | Build integration tests (real I/O) |
| Library | Purpose |
|---|---|
| nlohmann-json | JSON serialization |
| asio | Async I/O (standalone) |
| llhttp | HTTP parsing |
| gtest | Testing (optional) |
- Namespace:
cppmcp - Classes: PascalCase (
McpServer,ITransport) - Functions/methods: snake_case (
register_tool,send_message) - Members: snake_case + trailing underscore (
running_,config_) - Types/structs: PascalCase (
Tool,Resource,CallToolResult) - Constants: UPPER_SNAKE_CASE (
LATEST_PROTOCOL_VERSION)
MIT License