Getting Started

Build a C++17 MCP client or server from the SDK package.

Start with the public package targets, keep external tooling outside the SDK package path, and use Peer/Service as the first-choice SDK boundary for new code.

Install Locally

A source checkout can install the SDK targets into a local prefix. The SDK headers and public package targets are C++17-compatible.

cmake -S . -B build -DCXXMCP_BUILD_SDK=ON -DCXXMCP_BUILD_CLIENT=ON -DCXXMCP_BUILD_SERVER=ON
cmake --build build --config Release
cmake --install build --config Release --prefix out/install/cxxmcp

Consume From CMake

Downstream projects should link the narrowest target they need. Use cxxmcp::sdk only when one binary intentionally needs protocol, client, and server APIs together.

find_package(cxxmcp CONFIG REQUIRED)

add_executable(my_server server.cpp)
target_link_libraries(my_server PRIVATE cxxmcp::server)

add_executable(my_client client.cpp)
target_link_libraries(my_client PRIVATE cxxmcp::client)

Canonical Server Shape

Register server capabilities with the server builder, wrap the built server in mcp::ServerPeer, add a transport, and serve it.

#include <iostream>
#include <memory>
#include <utility>

#include <cxxmcp/peer.hpp>
#include <cxxmcp/server.hpp>
#include <cxxmcp/service.hpp>
#include <cxxmcp/transport/stdio_transport.hpp>

int main() {
    mcp::server::ServerBuilder builder;
    builder.name("demo-server")
        .version("1.0.0")
        .add_tool(
            mcp::protocol::ToolDefinition{
                .name = "echo",
                .description = "Echo the incoming payload",
                .input_schema = mcp::protocol::Json{{"type", "object"}},
            },
            [](const mcp::server::ToolContext& context) {
                mcp::protocol::ToolResult result;
                result.structured_content = context.arguments;
                return result;
            });

    auto server = builder.build();
    if (!server) {
        return 1;
    }

    mcp::ServerPeer peer(std::move(*server));
    peer.add_transport(
        std::make_unique<mcp::transport::ServerStdioTransport>(
            std::cin, std::cout));

    auto running = mcp::serve(std::move(peer));
    if (!running) {
        return 1;
    }

    return running->wait().has_value() ? 0 : 1;
}

Streamable HTTP Transport

HTTP transport requires CXXMCP_ENABLE_HTTP=ON at build time. The SDK bundles cpp-httplib as a header-only dependency.

# Build with HTTP transport
cmake -S . -B build -DCXXMCP_ENABLE_HTTP=ON -DCXXMCP_BUILD_SERVER=ON -DCXXMCP_BUILD_CLIENT=ON

Server and client builder both expose .streamable_http(...):

// Server — listen on HTTP
auto server = mcp::ServerPeer::builder()
    .name("my-server")
    .version("1.0.0")
    .streamable_http("127.0.0.1", 3000, "/mcp")  // host, port, path
    .tool(mcp::server::tool<Json, Json>("echo")
              .handler([](const Json& in) { return in; }))
    .build();

auto running = mcp::serve(std::move(*server));
running->wait_until_ready();
// Client — connect over HTTP
auto client = mcp::ClientPeer::builder()
    .streamable_http("http://127.0.0.1:3000/mcp")
    .build();

auto svc = mcp::serve(std::move(*client));
svc->peer().initialize();
svc->peer().call_tool("echo", Json{{"value", "hello"}});

OAuth 2.1 and Auth

Auth is opt-in. Set CXXMCP_ENABLE_AUTH=ON to export cxxmcp::auth with PKCE, DPoP, metadata endpoints, and bearer-token helpers.

# Auth contracts (no OpenSSL)
cmake -S . -B build -DCXXMCP_ENABLE_AUTH=ON

# With OpenSSL crypto backend
cmake -S . -B build -DCXXMCP_ENABLE_AUTH=ON -DCXXMCP_AUTH_CRYPTO=OpenSSL

vcpkg shortcut: vcpkg install "cxxmcp-sdk[auth]".

Add a bearer-token auth provider to the server builder:

#include <cxxmcp/peer.hpp>

auto auth = std::make_unique<mcp::server::StaticBearerAuthProvider>();
auth->add_token("my-secret",
                mcp::server::AuthIdentity{
                    "alice", {{"scope", "tools:call"}},
                });

auto server = mcp::ServerPeer::builder()
    .name("auth-demo")
    .version("1.0.0")
    .auth_provider(std::move(auth))
    .streamable_http("127.0.0.1", 3001, "/mcp")
    .tool(mcp::server::tool<Json, Json>("whoami")
              .handler([](const Json&, const mcp::server::ToolContext& ctx) {
                  return Json{{"subject",
                      ctx.auth_identity ? ctx.auth_identity->subject : "anon"}};
              }))
    .build();

Client side — pass the bearer token:

auto client = mcp::ClientPeer::builder()
    .streamable_http("http://127.0.0.1:3001/mcp")
    .bearer_token("my-secret")
    .build();

OpenSSL Crypto Backend

When CXXMCP_AUTH_CRYPTO=OpenSSL is set alongside CXXMCP_ENABLE_AUTH=ON, the SDK resolves OpenSSL via find_package(OpenSSL) and exports cxxmcp::auth_openssl. This enables:

  • SHA-256 / base64url for PKCE
  • JWK / JWS / JWT (RS256, ES256)
  • DPoP proof signing and verification
  • StaticJwksDpopBearerAuthProvider
  • FetchingJwksJwtVerifier for remote JWKS
#include <cxxmcp/auth/openssl/server_auth_provider.hpp>

mcp::auth::DpopAuthProviderOptions opts;
opts.require_dpop = true;
opts.issuer = "https://auth.example.com";
opts.audience = "https://resource.example/mcp";

auto auth =
    std::make_unique<mcp::auth::openssl::StaticJwksDpopBearerAuthProvider>(
        std::move(jwks), &replay_cache, opts);

OpenSSL is not vendored — it must be available on the system or through a package manager. Set OPENSSL_ROOT_DIR if CMake cannot find it automatically.

DTO Reflection

The CXXMCP_REFLECT macro gives your structs automatic JSON serialization, deserialization, and JSON Schema generation — no boilerplate.

#include <cxxmcp/protocol/reflect.hpp>

struct SearchArgs {
    std::string query;
    int limit = 3;
};

struct SearchResult {
    std::string session;
    std::vector<SearchHit> hits;
};

CXXMCP_REFLECT(SearchArgs, query, limit)
CXXMCP_REFLECT(SearchResult, session, hits)

Once reflected, use typed tool registration:

.tool(mcp::server::tool<SearchArgs, SearchResult>("search")
          .description("Search local documents.")
          .handler([](SearchArgs args, const mcp::server::ToolContext& ctx) {
              SearchResult result;
              result.session = ctx.session_id;
              // ...
              return result;
          }))

The SDK auto-generates inputSchema from SearchArgs fields and serializes SearchResult back to JSON. Use CXXMCP_REFLECT_CHECK(Type, N) to validate field count at compile time.

schema_for<T>() returns a nlohmann::json JSON Schema object for any reflected or primitive type.

Package Routes

vcpkg overlay

Use the repository-hosted overlay port until cxxmcp is accepted into a curated registry.

# Clone the repo first, then:
vcpkg install cxxmcp-sdk \
  --overlay-ports=packaging/vcpkg/ports

FetchContent or CPM

Prefer the SDK source release archive because it carries the bundled header-only SDK dependencies used by default builds.

Next Steps