Supported versions
- Rust. Rust 1.88 or later. The minimum supported Rust version (MSRV) is the MSRV of
rmcp3, and it is never higher than the MSRV of thermcpversions that the crate supports. The MSRV is always a Rust release that is at least 6 months old. An MSRV increase is a minor release and has its own CHANGELOG section. rmcp.rmcp3.0.0 or a later 3.x release. CI tests the latest patch of each 3.x minor release.- tokio. A tokio runtime with the time driver (see The tokio runtime). The
localfeature ofrmcpis not supported.
Features
Turn on one TLS feature. To use
native-tls, turn off the default features (default-features = false) and turn on native-tls. Without a TLS feature, the SDK can send events only to an http: URL. It logs a warning when the URL is an https: URL.
What agents see
The SDK changes the tool list thattools/list returns:
- Each tool gets a
contextparameter, in which the agent says why it calls the tool (see Tool Call Context). - Each tool gets a
session_idparameter, which ties the calls of one task together (see Stateless Servers). - With
enable_agent_tracking(true), each tool gets anagent_idparameter, in which the agent names itself. - The list gets one more tool,
get_more_tools, through which agents report a tool that they miss (see Agent Feedback).
[session_id issued before the content of the tool’s own result. An error result gets the block too. A JSON-RPC error (Err(ErrorData)) has no content, so it gets no block. A session_id that is neither start nor a valid ses_ ID gets a text block that begins [session_id unrecognized, and the call then has no session. When a tool declares an output schema, the SDK adds an mcp_session property to the schema. A structured result of that tool then gets mcp_session as its first key, with the session ID, the agent ID when the agent sent one, and a status.
Hooks, errors and delivery
Hooks get a ToolCall
The hooks get a ToolCall, not the raw request and the handler context (see Rust: the ToolCall). They are async closures that return a Result: an Err gives no value, and the SDK logs it.
Hooks that do not complete
The SDK stops a call hook (identify, event_tags, event_properties or resolve_session_id) that has not completed within 30 seconds, and sends the event without its value.
Errors
- JSON-RPC errors. A tool that returns
Err(ErrorData)gives the client a JSON-RPC error, asrmcpsends it. The event gets the name of the JSON-RPC error code aserror.type. - No stack frames. The
errorof an event has the platformrustand no stack frames. - Cancelled calls. A tool call that is cancelled (its future dropped) gives an event with the error type
cancelled. - Panics. When a tool panics, the SDK records the panic as the error of the call, then lets the panic go on unchanged.
rmcp turns an error that a tool returns into the text of an error result, so the event gets only that text. Call capture_error before the tool returns the error. The event then also gets the type of the error and the texts of its source() chain:
capture_error more than once, the last error counts. It works in the tool’s own future only: in a task that the tool spawns, or in a thread that it starts, it does nothing. The result and the error of the tool reach the client unchanged.
Delivery and shutdown
- One queue for each handle. Each
AgentCathandle has its own queue and API URL. - No signal handlers.
AgentCat::flush,AgentCat::shutdownand the drain of the last drop deliver the queued events. The SDK installs no signal or exit handler (see Flush and shutdown).
Switches, tests and logging
AGENTCAT_ENABLEDturns the SDK off with no code (see Environment variables).testing::Fake, behind thetest-utilfeature, builds handles whose events stay in the process. It records each event as a real handle sends it, after the hooks and the redaction, andassert_tracked,assert_tracked_count,assert_custom_event_published,assert_nothing_trackedandeventscheck what it recorded. The Testing section of the crate guide has a complete test.LogDestination::Tracingsends the log lines of the SDK totracing(see Logging). Without it, the SDK writes~/agentcat.log.flush_blockingandshutdown_blockingwait for the delivery without a tokio runtime.
Not in this release
The Rust SDK has no exporters (OpenTelemetry, Datadog, Sentry, PostHog) and no SDK diagnostics yet, andOptions has no setter for either.
What the Rust SDK sends
- Tool calls and custom events. The SDK publishes one event for each
tools/callrequest, and the custom events that your code publishes. Tool listings, resources and prompts give no events. - Request headers and URL. On a streamable HTTP server, the event of a tool call carries every request header of that call,
authorizationandcookieincluded, underparameters.extra.requestInfo.headers, and the URL of the request underparameters.extra.requestInfo.url. Header names are in lower case. The values of a repeated header are joined with,(;forcookie), and a repeatedset-cookiekeeps its last value. A stdio server sends no headers. Remove what you do not want in aredact_eventhook (see Client-Side Redaction). - No diagnostics. The SDK sends none of its own log lines to AgentCat.
Known limits
- The tokio time driver. A tracked server needs a tokio runtime with the time driver: the SDK times a rebuild of its tool registry with
tokio::time::timeout, and tokio panics at such a timer on a runtime without the driver.rmcpuses timers too.#[tokio::main],#[tokio::test]andtokio::runtime::Runtime::newturn the time driver on. A runtime fromtokio::runtime::Builderneedsenable_timeorenable_all. - The
localfeature ofrmcp. The SDK does not support it. It spawns the hooks asSendtasks on the tokio runtime of the call. preserve_orderofserde_json. The SDK turns on thepreserve_orderfeature ofserde_json, so thatmcp_sessionstays the first key of a result. Cargo turns a feature on for the whole build, so everyserde_json::Mapin your program keeps the order of insertion and does not sort its keys.arbitrary_precisionandfloat_roundtrip. When another crate in your build turns on thearbitrary_precisionor thefloat_roundtripfeature ofserde_json, Cargo turns it on for the SDK too. These features change howserde_jsonreads and writes numbers: the text of some numbers in the events, the token estimates and the size checks can change. The SDK logs a warning that names the feature each time its delivery thread starts.- URLs behind
nest_service.parameters.extra.requestInfo.urlof an event is the URL that thermcpservice sees.axum::Router::nest_serviceremoves the path prefix of the route before that, so the event records the URL without the prefix.route_servicekeeps the full path. - The tool registry. The SDK keeps a registry of what the tool listings tell it, for each handle and each type of server handler, by tool name. It never removes an entry, so a server whose tool names differ per tenant grows the registry with each new name. A hand-written
ServerHandlerwithoutget_tooldepends on that registry (#[tool_handler]writesget_toolfor you). On such a server, the listing of one session decides which arguments the SDK removes from the calls of another session. - A hook that holds its handle. A hook that holds a clone of its
AgentCathandle makes a reference cycle. The handle then never drops, so the drain of the last drop never runs. Let the hook capture only what it needs, or callAgentCat::shutdownbefore the process ends. A tool reaches its handle throughCallContext::agentcatand needs no clone. - Redaction hooks have no time limit. The SDK gives the redaction hooks no time limit. A redaction hook that never completes holds one of the 5 delivery slots of its handle for good. Five such hooks stop the delivery of the handle: later events wait in the queue until a shutdown abandons them.
- The end of the process. The drain of the last drop does not run after
std::process::exit, or after a panic withpanic = "abort". On a current-thread runtime, callshutdownbefore the runtime ends (see Current-thread runtimes).