Client Overview
The McpClient class connects to MCP servers, negotiates protocol versions, and invokes server capabilities.
Creating a Client
StdioClientTransportOptions transport_opts;
transport_opts.command = "path/to/server";
auto factory = std::make_shared<StdioClientTransport>(transport_opts);
auto transport = factory->Connect();
ClientOptions opts;
opts.client_info = Implementation{"MyClient", "1.0.0"};
auto client = McpClient::Create(transport, opts);ClientOptions
| Field | Type | Description |
|---|---|---|
client_info | Implementation | Client identity (default {"mcp-cpp-client", "0.3.8"}) |
capabilities | optional<ClientCapabilities> | Declared capabilities |
connect_mode | ConnectMode | Auto (discover → initialize), Legacy, Pin |
initialization_timeout | chrono::seconds | Handshake timeout (default 60s) |
pin_protocol_version | optional<string> | Pin to a specific protocol version (used with Pin mode) |
discover_probe_timeout | chrono::seconds | Server discovery probe timeout (default 5s) |
max_total_timeout | chrono::seconds | Total budget for a single request (default 0 = unlimited; progress extensions cannot push a pending request past this deadline) |
input_required_config | optional<InputRequiredConfig> | MRTR elicitation config: auto_fulfill=true, max_rounds=10, round_timeout=600s |
input_required_config.max_total_timeout | chrono::seconds | Hard budget for the whole MRTR flow (default 0 = unlimited; round_timeout applies per round) |
extensions | optional<JsonValue> | Protocol extension declarations (in the 2026 era, receiving task results requires "io.modelcontextprotocol/tasks", otherwise HandlerError is thrown) |
Making Requests
// List tools (with optional cursor for pagination)
auto tools = client->ListTools();
// Aggregate all pages in one call (until next_cursor is empty; throws McpError if pages do not converge within 64 pages)
auto all_tools = client->ListToolsAll();
auto all_resources = client->ListResourcesAll();
auto all_templates = client->ListResourceTemplatesAll();
auto all_prompts = client->ListPromptsAll();
// Call a tool (with optional arguments, RequestOptions, and MRTR support)
auto result = client->CallTool("echo",
JsonValue(JsonValue::Object{{"text", "Hello"}}));
// Taskified tool call: returns immediately with a task handle when the peer returns a task,
// then follow up with GetTask / PollTaskToCompletion / CancelTask
auto task = client->CallToolAsTask("long_running",
JsonValue(JsonValue::Object{{"input", "..."}}));
// Read a resource (with CacheableRequestOptions)
auto resource = client->ReadResource("file:///config.json");
// Get a prompt (with optional arguments and RequestOptions)
auto prompt = client->GetPrompt("code_review",
JsonValue(JsonValue::Object{{"diff", "..."}}));
// Complete a prompt/resource reference
auto completion = client->Complete(params);
// Ping (heartbeat, deprecated in 2026-07-28)
client->Ping();
// Discover server capabilities (re-negotiate)
auto discover = client->Discover();
// List resources and templates
auto resources = client->ListResources();
auto templates = client->ListResourceTemplates();
// List prompts
auto prompts = client->ListPrompts();
// Subscribe/unsubscribe to resource changes
client->SubscribeResource("file:///config.json");
client->UnsubscribeResource("file:///config.json");
// Task operations
auto status = client->GetTask("task-123");
client->UpdateTask("task-123", result_json);
client->UpdateTask("task-123", std::nullopt, input_responses_json); // answer the task's input request (mutually exclusive with result)
client->CancelTask("task-123", "no longer needed");
// Poll task until completion (with configurable interval and timeout)
auto completed = client->PollTaskToCompletion("task-123");
// Skill operations (2026-era; listing supports cursors, reads accept cache options)
auto skills = client->ListSkills();
auto skill = client->GetSkill("skill://conformance-weather/SKILL.md");
auto dir = client->ReadDirectory("skill://conformance-weather");Server-to-Client Handlers
Register handlers for server-initiated requests:
client->SetElicitationHandler(
[](const ElicitRequestParams& params) -> ElicitResult {
// Prompt user for input, return result
ElicitResult result;
result.content = JsonValue(JsonValue::Object{{"name", "Alice"}});
return result;
});
client->SetNotificationHandler("custom/notification",
[](const JsonRpcNotification& notif) {
// Handle server-sent notifications
});
// URL-mode elicitation: direct the user to an out-of-band URL to complete the action
client->SetUrlElicitationHandler(
[](const ElicitRequestParams& params) {
// params.mode == "url", with params.url and params.elicitation_id
});
client->SetLoggingHandler(
[](const LoggingMessageNotificationParams& params) {
// Handle logging messages from server
});Deprecated (SEP-2577): SetSamplingHandler and SetRootsHandler are no longer available (sampling/createMessage and roots/list only exist in the 2025 era); use SetElicitationHandler instead.
Enabling applyDefaults in the client capabilities (ClientOptions::capabilities elicitation declaration) makes missing fields auto-filled from the request schema's default values when the result is accept with object content (recursing into nested objects and anyOf/oneOf variants).
Subscriptions
// Subscribe to server notifications (2026-era)
SubscriptionsListenRequestParams subs;
subs.notifications.tools_list_changed = true;
subs.notifications.resources_list_changed = true;
subs.notifications.resource_subscriptions = {"file:///config.json"};
client->SubscribeAsync(subs);Version Negotiation
The client auto-negotiates the protocol version:
- Auto (default): Probe
server/discover, fallback toinitializehandshake - Pin: Force a specific version (via
pin_protocol_version, defaults to latestkLatestProtocolVersion) - Legacy: Only
initializehandshake
Auto falls back by transport class: stdio-like transports (InMemory/Stdio) fall back to initialize on probe failure while network transports throw; on -32022 with supported containing the latest version a single corrective retry is issued, a second rejection is a hard error.