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

- Use Auth for Streamable HTTP bearer token and OAuth scaffold behavior.
- Use Release and API Stability before depending on a release claim.
- Use API Reference for generated symbol-level documentation.