pub struct SessionBuilder<Counterpart, Run: RunWithConnectionTo<Counterpart> = NullRun, BlockState: SessionBlockState = NonBlocking>{ /* private fields */ }Expand description
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.
Sourcepub fn on_session_start<F, Fut>(self, op: F) -> Result<(), Error>
pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), Error>
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 an ActiveSession<'static, _> 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 |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>,
op: F,
) -> Result<(), Error>
pub fn on_proxy_session_start<F, Fut>( self, responder: Responder<NewSessionResponse>, op: F, ) -> Result<(), Error>
Spawn a proxy session and run a closure with the session ID.
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 the SessionId once the session is established. 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.builder()
.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 |session_id| {
// 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(ActiveSession<'runner, Counterpart>) -> Result<T, Error>,
) -> Result<T, Error>
pub async fn run_until<T>( self, op: impl for<'runner> AsyncFnOnce(ActiveSession<'runner, Counterpart>) -> 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 ActiveSession passed to op has 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<ActiveSession<'static, Counterpart>, Error>where
R: 'static,
pub async fn start_session(
self,
) -> Result<ActiveSession<'static, Counterpart>, 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 an ActiveSession<'static, _> because the session’s runners are spawned into
background tasks that live for the connection lifetime.
Requires calling block_task first.
Sourcepub async fn start_session_proxy(
self,
responder: Responder<NewSessionResponse>,
) -> Result<SessionId, Error>
pub async fn start_session_proxy( self, responder: Responder<NewSessionResponse>, ) -> Result<SessionId, 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.
Requires calling block_task first.