Expand description
Creating and managing sessions for multi-turn conversations.
A session represents a multi-turn conversation with an agent. Within a session, you can send prompts, receive responses, and the agent maintains context across turns.
The examples below use the stable protocol v1 SessionBuilder and
ActiveSession. With the unstable_protocol_v2 feature, callbacks created
through Client.v2() receive V2ConnectionTo and its build_session*,
V2SessionBuilder, resume_session*, V2ResumeSessionBuilder, and
command-only V2Session APIs. With unstable_session_fork, it also exposes
fork_session* and V2ForkSessionBuilder. The v2 resume and fork helpers
return builders and do not publish their requests until start_session or
on_proxy_session_start is called. V2 prompt responses acknowledge
acceptance independently; receive session-wide updates and interactive
requests through typed connection handlers.
§Creating a Session
Use the session builder to create a new session:
cx.build_session_cwd()? // Use current working directory
.block_task() // Mark as blocking
.run_until(async |session| {
// Use the session here
Ok(())
})
.await?;Or specify a custom working directory:
cx.build_session("/path/to/project")
.block_task()
.run_until(async |session| { Ok(()) })
.await?;§Restoring a Session
Stable protocol v1 direct clients can turn session/load and
session/resume into a RestoredSession without
manually installing session handlers. It contains both the ActiveSession
and the complete operation-specific response:
let restored = cx
.load_session("session-1", "/path/to/project")
.block_task()
.start_session()
.await?;
let (mut session, load_response) = restored.into_parts();
println!("load response: {load_response:?}");
session.send_prompt("Continue where we left off")?;Use load_session only when the agent advertises the top-level
loadSession capability. Use resume_session to continue without replay
when the agent advertises sessionCapabilities.resume.
The matching load_session_from and resume_session_from helpers preserve
requests assembled elsewhere. Non-blocking callers can use
on_session_start instead of block_task().start_session().
For session/load, the SDK acknowledges its local route before publishing
the request, so history updates sent before the load response remain queued
on the returned session. An error removes the provisional route before
later traffic is dispatched. Dropping an in-flight blocking start
immediately deactivates the provisional route and triggers the standard
SentRequest drop-time cancellation behavior.
§Sending Prompts
Inside run_until, you get an ActiveSession that lets you interact
with the agent:
.run_until(async |mut session| {
// Send a prompt
session.send_prompt("What is 2 + 2?")?;
// Read the complete response as a string
let response = session.read_to_string().await?;
println!("{}", response);
// Send another prompt in the same session
session.send_prompt("And what is 3 + 3?")?;
let response = session.read_to_string().await?;
Ok(())
})§Adding MCP Servers
You can attach MCP (Model Context Protocol) servers to a session to provide tools to the agent:
MCP attachment requires the unstable_mcp_over_acp feature. Standalone MCP
servers remain available without it. Draft protocol v2 per-session
attachment uses V2SessionBuilder::with_mcp_server for new sessions or
V2ResumeSessionBuilder::with_mcp_server for resumed sessions. With
unstable_session_fork, V2ForkSessionBuilder::with_mcp_server provides the
same attachment for forked sessions. These APIs additionally require
unstable_protocol_v2. The SDK installs the routes and initially polls the
runners before publishing the setup request, so the agent can use them
during setup or resume replay. Successful attachments remain active for the
connection lifetime; setup failures, including an error response after
cancellation, clean up the pending attachment.
cx.build_session_cwd()?
.with_mcp_server(my_mcp_server)?
.block_task()
.run_until(async |session| { Ok(()) })
.await?;See the cookbook for detailed MCP server examples.
§Non-Blocking Session Start
If you’re inside an on_receive_* callback and need to start a session,
use on_session_start instead of block_task().run_until():
Client.builder()
.on_receive_request(async |req: NewSessionRequest, responder, cx| {
cx.build_session_from(req)
.on_session_start(async |session| {
// Handle the session
Ok(())
})?;
Ok(())
}, agent_client_protocol::on_receive_request!())When the session response is routed during its original dispatch, session routing is installed before later messages are dispatched. The callback is invoked in a spawned task, so no user callback code has that ordering guarantee and the callback can wait for session traffic. A response interceptor that retains and routes the response later cannot retroactively order setup before messages already processed. See Ordering for details.
For a draft v2 proxy, use V2SessionBuilder::on_proxy_session_start or
V2ResumeSessionBuilder::on_proxy_session_start instead. The feature-gated
V2ForkSessionBuilder exposes the same helper. Each forwards the complete
operation-specific response and then spawns the callback with an
OpenedV2Session, so the callback keeps both the command-only session
handle and that exact response:
Proxy.v2()
.on_receive_request_from(
Client,
async |request: schema::v2::NewSessionRequest, responder, cx| {
cx.build_session_from(request)
.on_proxy_session_start(responder, async |opened| {
let (session, setup_response) = opened.into_parts();
track_session(session.session_id(), setup_response);
Ok(())
})
},
agent_client_protocol::on_receive_request!(),
);For session/new and feature-gated session/fork, the builder installs
routing with the newly allocated response session ID before later inbound
traffic is dispatched. For session/resume, the builder installs and
acknowledges session routing
before publishing the downstream request, allowing replay updates to be
forwarded before the resume response. The downstream request inherits
upstream cancellation. An unsuccessful downstream response drops pending
routing and MCP attachment; successful setup keeps those routes for the
connection lifetime. The cancellation signal itself remains advisory while
the helper awaits that response. User work runs outside the ordering
barrier. V2 session updates and interactive requests remain independent
traffic handled by typed connection callbacks.