pub struct McpContext {
pub client_name: Option<String>,
pub client_version: Option<String>,
pub session_id: Option<String>,
pub auth_claims: Option<String>,
pub progress_token: Option<String>,
/* private fields */
}Expand description
Per-request context passed to tool handlers registered via
McpServer::tool_with_context — caller identity and session info that a
plain Fn(&str) -> ... tool handler has no way to see.
Constructed in McpServer::execute from the current request’s headers
plus whatever clientInfo was recorded for this session at initialize
time (see McpServer::handle_request_with_context).
Fields§
§client_name: Option<String>clientInfo.name sent in this session’s initialize call, if the
client sent one and this request carries a recognized Mcp-Session-Id.
client_version: Option<String>clientInfo.version sent in this session’s initialize call, under
the same conditions as client_name.
session_id: Option<String>The Mcp-Session-Id header on this request, if present — the value
the server minted and returned in the initialize response header
for this session (see the module docs’ Sessions section).
auth_claims: Option<String>Verified JWT claims as a JSON string. Not populated by anything in
this crate yet — reserved for a future JWT-auth integration
(MCP_TODO.md TODO-11/TODO-13); always None today.
progress_token: Option<String>The raw JSON value of params._meta.progressToken from the
triggering tools/call request, if the client sent one — a spec
string | number, so this is stored pre-rendered (already correctly
quoted if it’s a string) rather than decoded, and spliced back
verbatim by Self::report_progress. None for anything other than
a tools/call whose caller asked for progress updates.
Implementations§
Source§impl McpContext
impl McpContext
Sourcepub fn report_progress(
&self,
progress: f64,
total: Option<f64>,
message: Option<&str>,
)
pub fn report_progress( &self, progress: f64, total: Option<f64>, message: Option<&str>, )
Push a notifications/progress event over the SSE channel for this
request’s progressToken, if the client asked for progress updates
(params._meta.progressToken on the triggering tools/call) and
this context was built through a live McpServer (via execute(),
not a bare McpContext { .. } — see the sse_clients field doc).
Silently does nothing in either case — a handler doesn’t need to branch on whether progress reporting is actually wired up before calling this; it’s always safe to call.
total and message are both optional, matching the spec’s
notifications/progress shape: {"progressToken":...,"progress":..., "total":...,"message":"..."} (with total/message omitted when not
given here).
use rust_web_server::mcp::{McpContent, McpServer};
let server = McpServer::new("my-server", "1.0")
.tool_with_context("long_job", "Do something slow", "{}", |ctx, _args| {
ctx.report_progress(0.0, Some(100.0), Some("starting"));
// ... do work ...
ctx.report_progress(100.0, Some(100.0), Some("done"));
Ok(McpContent::text("done"))
});Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
true if the client sent notifications/cancelled referencing this
tools/call’s request id.
Rust cannot forcibly interrupt a running synchronous closure — same
limitation crate::timeout::with_timeout documents — so this is
cooperative cancellation: nothing happens automatically. A handler
that does its own iterative work (processing a batch of items, say)
can check this between steps and return early; a handler that never
checks it runs to completion regardless, exactly as before this
existed.
Always false if the client never sent a cancellation, if this
wasn’t dispatched as a tools/call (the only method cancellation
applies to), or if ctx was built without a live server behind it.
use rust_web_server::mcp::{McpContent, McpServer};
let server = McpServer::new("my-server", "1.0")
.tool_with_context("process_batch", "Process many items", "{}", |ctx, _args| {
for i in 0..1_000_000 {
if ctx.is_cancelled() {
return Err("cancelled by client".to_string());
}
// ... process item i ...
}
Ok(McpContent::text("done"))
});Sourcepub fn sample(
&self,
request: SamplingRequest,
timeout: Duration,
) -> Result<SamplingResponse, String>
pub fn sample( &self, request: SamplingRequest, timeout: Duration, ) -> Result<SamplingResponse, String>
Ask the connected client to run LLM inference (“sampling”) and block
until it replies or timeout elapses — the server-initiated half of
sampling/createMessage. This reverses the usual request direction:
the server sends a JSON-RPC request to the client over the GET /mcp SSE stream, and the client answers with its own POST /mcp
carrying a JSON-RPC response (no method) that
McpServer::handle_request_with_context recognizes and routes
back here instead of treating it as an invalid request.
Blocking (not async fn) is deliberate: tool handlers in this crate
are plain synchronous closures — there is no async tool handler
support yet (MCP_TODO.md’s TODO-17) for this to .await inside of.
The calling thread parks on the response channel for up to
timeout; on a thread-pool server this ties up one worker for that
long, same tradeoff crate::timeout::with_timeout already accepts
for bounding a slow handler’s caller.
Fails fast, before sending anything, if: the client’s initialize
call never declared capabilities.sampling (it wouldn’t know what
to do with the request); this request has no session id (nothing to
address the request to); or ctx has no live server behind it.
Otherwise fails with a timeout error if the client never responds —
including if it simply has no GET /mcp SSE connection open for
this session, since there’s no separate “not connected” signal.
use rust_web_server::mcp::{McpServer, PromptMessage, SamplingRequest};
use std::time::Duration;
let server = McpServer::new("my-server", "1.0")
.tool_with_context("ask_llm", "Ask the connected client's model a question", "{}", |ctx, _args| {
let response = ctx.sample(
SamplingRequest {
messages: vec![PromptMessage::user("What is 2+2?")],
max_tokens: 100,
system_prompt: None,
},
Duration::from_secs(30),
)?;
Ok(response.content)
});Sourcepub fn list_roots(&self, timeout: Duration) -> Result<Vec<McpRoot>, String>
pub fn list_roots(&self, timeout: Duration) -> Result<Vec<McpRoot>, String>
Ask the connected client which filesystem roots (workspace directories, mounted volumes, …) it has access to — useful for a file-system-aware tool that should only operate within the client’s declared workspace rather than the whole filesystem.
Like Self::sample, this reverses the usual request direction (a
server-initiated roots/list request over SSE, answered by the
client’s own POST /mcp) and blocks the calling thread for up to
timeout — see sample’s docs for why blocking is deliberate here.
The result is cached per session: the first call after initialize
(or after the client sends notifications/roots/list_changed, which
invalidates the cache) does a live round trip; later calls in the
same session return the cached list without sending anything. A tool
handler can call this on every invocation without worrying about
spamming the client with redundant roots/list requests.
Fails fast, before sending anything, under the same conditions as
Self::sample (translated to roots): the client never declared
capabilities.roots, this request has no session id, or ctx has
no live server behind it.
use rust_web_server::mcp::McpServer;
use std::time::Duration;
let server = McpServer::new("my-server", "1.0")
.tool_with_context("list_workspace_files", "List files in the client's workspace", "{}", |ctx, _args| {
let roots = ctx.list_roots(Duration::from_secs(10))?;
let names: Vec<String> = roots.iter().map(|r| r.uri.clone()).collect();
Ok(rust_web_server::mcp::McpContent::text(names.join(", ")))
});Trait Implementations§
Source§impl Clone for McpContext
impl Clone for McpContext
Source§fn clone(&self) -> McpContext
fn clone(&self) -> McpContext
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more