Skip to content

Tools ​

Tools are functions that an LLM can call to perform actions. They are the primary mechanism for servers to expose executable functionality.

Registering a Tool ​

cpp
server->RegisterTool("get_weather",
    ToolOptions{}.Description("Get current weather for a location"),
    [](const RequestContext<CallToolRequestParams>& ctx) -> CallToolResult {
        std::string location;
        if (ctx.Params().arguments) {
            auto* v = ctx.Params().arguments->Find("location");
            if (v) location = v->GetString();
        }

        CallToolResult result;
        result.content.push_back(
            TextContent{"text", "The weather in " + location + " is sunny."});
        return result;
    });

Tool with Annotations ​

cpp
ToolOptions opts;
opts.title = "Delete File";
opts.description = "Permanently delete a file by path";
opts.destructive = true;   // marks as destructive_hint
opts.idempotent = false;

server->RegisterTool("delete_file", opts,
    [](const RequestContext<CallToolRequestParams>& ctx) -> CallToolResult {
        // ...
    });

McpServerTool (Reusable Tools) ​

For reusable tool logic, create an McpServerTool object explicitly:

cpp
auto tool = McpServerTool::Create("get_weather",
    [](const RequestContext<CallToolRequestParams>& ctx) -> CallToolResult {
        // ...
    },
    ToolOptions{}.Description("Get current weather"));
server->RegisterTool(tool);

The lambda-based RegisterTool overload is shorthand for this — both forms are equivalent.

ToolOptions Fields ​

All ToolOptions fields:

FieldTypeDefaultDescription
nameoptional<string>—The registered tool name comes from the first argument of RegisterTool (or the name argument of McpServerTool::Create); this field is currently not consumed
titleoptional<string>—Human-readable title
descriptionoptional<string>—Tool description
input_schemaoptional<JsonValue>{"type":"object","properties":{}}JSON Schema for input parameters
output_schemaoptional<JsonValue>—JSON Schema for structured output
use_structured_contentboolfalseOpt into structured output (sets output_schema to {"type":"object"})
iconsvector<Icon>—Tool icons
metaoptional<JsonValue>—Additional metadata

Annotation fields (propagated to ToolAnnotations):

FieldTypeDescription
destructiveoptional<bool>Marks the tool as destructive
idempotentoptional<bool>Calling multiple times has same effect as once
read_only_hintoptional<bool>Tool does not modify state
open_world_hintoptional<bool>Tool may interact with external systems

Structured Content ​

Tools can return structured JSON data instead of text:

cpp
CallToolResult result;
result.content.push_back(TextContent{"text", "Processed."});
result.structured_content = JsonValue::Parse(R"({
    "records_updated": 42,
    "duration_ms": 153
})");

The structured_content field is a 2026-07-28 protocol feature. Set use_structured_content = true on ToolOptions to opt in (sets output_schema to {"type": "object"}). You can also set output_schema directly for fine-grained schema control.

Input Schema ​

Set the tool's input_schema via ToolOptions::InputSchema() or directly:

cpp
auto schema = JsonValue::Parse(R"({
    "type": "object",
    "properties": {
        "location": {"type": "string"},
        "units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
    },
    "required": ["location"]
})");

server->RegisterTool("get_weather",
    ToolOptions{}.InputSchema(std::move(schema)),
    [](const RequestContext<CallToolRequestParams>& ctx) -> CallToolResult {
        // ...
    });

Multi-Round Tool Requests (input_required) ​

Tool handlers can set input_required on the CallToolResult to start an MRTR (multi round-trip request) collection round with the client; the client gathers input via elicitation and retries the call with requestState:

cpp
CallToolResult result;
InputRequiredResult ir;
ElicitRequestParams params;
params.message = "Please provide the API key";
ir.input_requests["api_key"] = MakeInputRequestForElicitation(params);
result.input_required = std::move(ir);
return result;  // the server auto-signs requestState (requires request_state_key)

Once ServerOptions::request_state_key is configured, the server automatically signs request_state on input_required results before returning them; tampered or expired states are rejected on retry before reaching the handler (-32602, data.reason="invalid_request_state"). See MRTR for the full protocol flow.

Task Execution ​

Declaring a tool in task mode makes tools/call return a task handle (taskId) immediately while execution continues in the background; the client follows up with GetTask / PollTaskToCompletion (available in both the 2025 and 2026 eras; in the 2026 era task status notifications are routed via subscriptions — dropped when there are no subscribers — so the client polls for the terminal state):

cpp
ToolExecution execution;
execution.mode = ToolExecutionMode::Task;

ToolOptions opts;
opts.execution = execution;
opts.description = "A long-running computation";

server->RegisterTool("slow_compute", opts,
    [](const RequestContext<CallToolRequestParams>& ctx) -> CallToolResult {
        // Poll ctx.IsCancellationRequested() for cooperative cancellation
        // ...
    });

This requires ServerOptions::task_store to be configured (the task persistence backend). tasks/update accepts inputResponses to answer input requests raised mid-execution (mutually exclusive with result). The task handle shape differs by era: flat top-level taskId/status in the 2026 era, nested task object retained for compatibility in the 2025 era (the client accepts both). Terminal-state semantics: a normal handler return (including is_error results) lands Completed; a thrown exception lands Failed with the error payload carried inline as error. See Extensions for extension declarations and wire semantics.