pub struct SessionBuilder<Counterpart, Run: RunWithConnectionTo<Counterpart> = NullRun, BlockState: SessionBlockState = NonBlocking>{ /* private fields */ }Expand description
Stable protocol v1 session builder for a new session request. Allows you to add MCP servers or set other details for this session.
The BlockState type parameter tracks whether blocking methods are available:
NonBlocking(default): Onlyon_session_startis availableBlocking(after callingblock_task):run_untilandstart_sessionbecome available
Implementations§
Source§impl<Counterpart, R, BlockState> SessionBuilder<Counterpart, R, BlockState>where
Counterpart: HasPeer<Agent>,
R: RunWithConnectionTo<Counterpart>,
BlockState: SessionBlockState,
impl<Counterpart, R, BlockState> SessionBuilder<Counterpart, R, BlockState>where
Counterpart: HasPeer<Agent>,
R: RunWithConnectionTo<Counterpart>,
BlockState: SessionBlockState,
Sourcepub fn with_mcp_server<McpRun>(
self,
mcp_server: McpServer<Counterpart, McpRun>,
) -> Result<SessionBuilder<Counterpart, ChainRun<R, McpRun>, BlockState>, Error>where
McpRun: RunWithConnectionTo<Counterpart>,
Available on crate feature unstable_mcp_over_acp only.
pub fn with_mcp_server<McpRun>(
self,
mcp_server: McpServer<Counterpart, McpRun>,
) -> Result<SessionBuilder<Counterpart, ChainRun<R, McpRun>, BlockState>, Error>where
McpRun: RunWithConnectionTo<Counterpart>,
unstable_mcp_over_acp only.Attach an MCP server to this new session.
The declaration is an acp transport (McpServer::Acp). The agent must
advertise agentCapabilities.mcpCapabilities.acp, or a compatibility
proxy such as McpOverAcpPolyfill must sit in front of it; otherwise a
conforming agent rejects the setup request with -32602. This method
does not check the agent’s capabilities.
Sourcepub fn on_session_start<F, Fut>(self, op: F) -> Result<(), Error>where
R: 'static,
F: FnOnce(SessionSetup<'static, Counterpart, NewSessionResponse>) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), Error>> + Send,
pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), Error>where
R: 'static,
F: FnOnce(SessionSetup<'static, Counterpart, NewSessionResponse>) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), Error>> + Send,
Spawn a task that runs the provided closure once the session starts.
Unlike start_session, this method returns immediately
without blocking the current task. The session handshake and closure execution
happen in a spawned background task.
The closure receives a SessionSetup with an
ActiveSession<'static, _> and the NewSessionResponse, and runs in
a spawned task. If it returns an error, the error propagates to the
connection’s task handling.
§Example
cx.build_session_cwd()?
.with_mcp_server(mcp)?
.on_session_start(async |SessionSetup { mut session, .. }| {
// Do something with the session
session.send_prompt("Hello")?;
let response = session.read_to_string().await?;
Ok(())
})?;
// Returns immediately, session runs in background§Ordering
Session runners are scheduled and routing setup is installed before the dispatch loop processes the next message when the session response is routed during its original dispatch. No user callback code runs under that ordering guarantee: the callback is invoked in a spawned task, so it may wait for later session traffic without deadlocking the connection. A response interceptor that retains the response and routes it later cannot retroactively order session setup before messages the dispatch loop has already processed.
Sourcepub fn on_proxy_session_start<F, Fut>(
self,
responder: Responder<NewSessionResponse, V1>,
op: F,
) -> Result<(), Error>
pub fn on_proxy_session_start<F, Fut>( self, responder: Responder<NewSessionResponse, V1>, op: F, ) -> Result<(), Error>
Spawn a proxy session and run a closure with the session/new response.
A proxy session starts the session with the agent and then automatically proxies all session updates (prompts, tool calls, etc.) from the agent back to the client. You don’t need to handle any messages yourself - the proxy takes care of forwarding everything. This is useful when you want to inject and/or filter prompts coming from the client but otherwise not be involved in the session.
Unlike start_session_proxy, this method returns
immediately without blocking the current task. The session handshake, client
response, and proxy setup all happen in a spawned background task.
The closure receives a clone of the complete NewSessionResponse, which was also
forwarded to the client, once the session is established. Read the session ID from its
session_id field. Use it for logging
or eventual tracking; it runs concurrently with later connection traffic. Register
ID-independent state that later handlers must observe before calling this helper. For
ID-keyed bookkeeping, install a gate or placeholder first, make later handlers await it,
and populate it from the closure.
§Example
Proxy.v1()
.on_receive_request_from(Client, async |request: NewSessionRequest, responder, cx| {
let mcp = McpServer::<Conductor, _>::builder("tools").build();
cx.build_session_from(request)
.with_mcp_server(mcp)?
.on_proxy_session_start(responder, async |response| {
// Session started
Ok(())
})
}, agent_client_protocol::on_receive_request!())
.connect_to(transport)
.await?;§Ordering
The client response is queued, proxy routing is installed, and session runners are scheduled before the dispatch loop processes the next message when the session response is routed during its original dispatch. This is a local ordering guarantee, not a guarantee that the response reaches the client before later wire traffic. No user callback code runs under the barrier: the callback is invoked in a spawned task, so it may wait for later connection traffic. A response interceptor that retains the response and routes it later cannot retroactively order this setup before messages the loop already processed.
Source§impl<Counterpart, R> SessionBuilder<Counterpart, R, NonBlocking>
impl<Counterpart, R> SessionBuilder<Counterpart, R, NonBlocking>
Sourcepub fn block_task(self) -> SessionBuilder<Counterpart, R, Blocking>
pub fn block_task(self) -> SessionBuilder<Counterpart, R, Blocking>
Mark this session builder as being able to block the current task.
After calling this, you can use run_until or
start_session which block the current task.
This should not be used from inside a message handler like
Builder::on_receive_request or HandleDispatchFrom
implementations.
Source§impl<Counterpart, R> SessionBuilder<Counterpart, R, Blocking>
impl<Counterpart, R> SessionBuilder<Counterpart, R, Blocking>
Sourcepub async fn run_until<T>(
self,
op: impl for<'runner> AsyncFnOnce(SessionSetup<'runner, Counterpart, NewSessionResponse>) -> Result<T, Error>,
) -> Result<T, Error>
pub async fn run_until<T>( self, op: impl for<'runner> AsyncFnOnce(SessionSetup<'runner, Counterpart, NewSessionResponse>) -> Result<T, Error>, ) -> Result<T, Error>
Run this session synchronously. The current task will be blocked
and op will be executed with the active session information.
This is useful when you have MCP servers that are borrowed from your local
stack frame.
The SessionSetup passed to op holds an ActiveSession with a
non-'static lifetime, which
prevents calling ActiveSession::proxy_remaining_messages (since the
session’s background runners would terminate when op returns).
Requires calling block_task first.
Sourcepub async fn start_session(
self,
) -> Result<SessionSetup<'static, Counterpart, NewSessionResponse>, Error>where
R: 'static,
pub async fn start_session(
self,
) -> Result<SessionSetup<'static, Counterpart, NewSessionResponse>, Error>where
R: 'static,
Send the request to create the session and return a handle.
This is an alternative to Self::run_until that avoids rightward
drift but at the cost of requiring MCP servers that are Send and
don’t access data from the surrounding scope.
Returns a SessionSetup with the ActiveSession<'static, _> and the
complete NewSessionResponse. The session is 'static because its
runners are spawned into background tasks that live for the connection
lifetime.
An error response to session/new is returned to the caller and leaves
the connection running. The session’s dynamic handlers are removed, so
its runners stop once their cleanup finishes. Session routing is
installed before later inbound messages are dispatched.
Requires calling block_task first.
Sourcepub async fn start_session_proxy(
self,
responder: Responder<NewSessionResponse, V1>,
) -> Result<NewSessionResponse, Error>
pub async fn start_session_proxy( self, responder: Responder<NewSessionResponse, V1>, ) -> Result<NewSessionResponse, Error>
Start a proxy session that forwards all messages between client and agent.
A proxy session starts the session with the agent and then automatically proxies all session updates (prompts, tool calls, etc.) from the agent back to the client. You don’t need to handle any messages yourself - the proxy takes care of forwarding everything. This is useful when you want to inject and/or filter prompts coming from the client but otherwise not be involved in the session.
This is a convenience method that combines start_session,
responding to the client, and ActiveSession::proxy_remaining_messages.
For more control (e.g., to send some messages before proxying), use
start_session instead and call
proxy_remaining_messages manually.
Returns a clone of the complete NewSessionResponse forwarded to the
client.
Requires calling block_task first.