Skip to main content

flymcprust_core/
context.rs

1//! Per-request context ([`Ctx`]), shared application state ([`State`]) and
2//! transport-provided values ([`TransportContext`]).
3
4use std::any::{Any, TypeId, type_name};
5use std::collections::HashMap;
6use std::fmt;
7use std::ops::Deref;
8use std::sync::Arc;
9
10use tokio_util::sync::CancellationToken;
11
12use crate::mrtr::Round;
13use crate::notify::Notifier;
14use crate::spec::{self, RequestId};
15
16/// A small type map: at most one value per type.
17#[derive(Clone, Default)]
18pub(crate) struct TypeMap {
19    values: HashMap<TypeId, (Arc<dyn Any + Send + Sync>, &'static str)>,
20}
21
22impl TypeMap {
23    pub(crate) fn insert<T: Send + Sync + 'static>(&mut self, value: Arc<T>) {
24        self.values
25            .insert(TypeId::of::<T>(), (value, type_name::<T>()));
26    }
27
28    pub(crate) fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
29        let (value, _) = self.values.get(&TypeId::of::<T>())?;
30        Arc::clone(value).downcast::<T>().ok()
31    }
32
33    pub(crate) fn contains(&self, id: TypeId) -> bool {
34        self.values.contains_key(&id)
35    }
36}
37
38impl fmt::Debug for TypeMap {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_set()
41            .entries(self.values.values().map(|(_, name)| name))
42            .finish()
43    }
44}
45
46/// Values attached by the transport to a request (for example, authenticated
47/// claims or HTTP headers). Tower middleware can populate it before the
48/// request reaches the server.
49#[derive(Clone, Default, Debug)]
50pub struct TransportContext {
51    values: TypeMap,
52}
53
54impl TransportContext {
55    /// Creates an empty context.
56    #[must_use]
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Stores a value, replacing any previous value of the same type.
62    pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
63        self.values.insert(Arc::new(value));
64    }
65
66    /// Returns the value of type `T`, if present.
67    #[must_use]
68    pub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
69        self.values.get::<T>()
70    }
71}
72
73/// Protocol metadata carried by a request's `_meta`.
74#[derive(Debug, Clone)]
75pub(crate) struct RequestMeta {
76    pub(crate) protocol_version: String,
77    pub(crate) client_capabilities: spec::ClientCapabilities,
78    pub(crate) client_info: Option<spec::Implementation>,
79    pub(crate) progress_token: Option<spec::ProgressToken>,
80    /// Minimum level of log messages the client asked for.
81    #[allow(deprecated)]
82    pub(crate) log_level: Option<spec::LoggingLevel>,
83}
84
85/// Context of the request being handled. Declare a `Ctx` parameter in a tool
86/// to receive it; it is excluded from the tool's input schema.
87///
88/// Cloning is cheap.
89#[derive(Clone)]
90pub struct Ctx {
91    inner: Arc<CtxInner>,
92}
93
94struct CtxInner {
95    request_id: RequestId,
96    meta: RequestMeta,
97    cancellation: CancellationToken,
98    transport: TransportContext,
99    state: Arc<TypeMap>,
100    round: Option<Arc<Round>>,
101    notifier: Option<Arc<Notifier>>,
102}
103
104impl Ctx {
105    pub(crate) fn new(
106        request_id: RequestId,
107        meta: RequestMeta,
108        cancellation: CancellationToken,
109        transport: TransportContext,
110        state: Arc<TypeMap>,
111        round: Option<Arc<Round>>,
112        notifier: Option<Arc<Notifier>>,
113    ) -> Self {
114        Self {
115            inner: Arc::new(CtxInner {
116                request_id,
117                meta,
118                cancellation,
119                transport,
120                state,
121                round,
122                notifier,
123            }),
124        }
125    }
126
127    /// Notifications of this request, when the client asked for them.
128    pub(crate) fn notifier(&self) -> Option<&Notifier> {
129        self.inner.notifier.as_deref()
130    }
131
132    pub(crate) fn notifier_handle(&self) -> Option<Arc<Notifier>> {
133        self.inner.notifier.clone()
134    }
135
136    /// MRTR data, for `tools/call`, `resources/read` and `prompts/get`.
137    pub(crate) fn round(&self) -> Option<&Round> {
138        self.inner.round.as_deref()
139    }
140
141    /// The JSON-RPC id of the request.
142    #[must_use]
143    pub fn request_id(&self) -> &RequestId {
144        &self.inner.request_id
145    }
146
147    /// The protocol version the client used for this request.
148    #[must_use]
149    pub fn protocol_version(&self) -> &str {
150        &self.inner.meta.protocol_version
151    }
152
153    /// The capabilities the client declared for this request.
154    #[must_use]
155    pub fn client_capabilities(&self) -> &spec::ClientCapabilities {
156        &self.inner.meta.client_capabilities
157    }
158
159    /// The client's self-reported identity. Informational only: never use it
160    /// for security decisions.
161    #[must_use]
162    pub fn client_info(&self) -> Option<&spec::Implementation> {
163        self.inner.meta.client_info.as_ref()
164    }
165
166    /// The progress token, when the client asked for progress notifications.
167    #[must_use]
168    pub fn progress_token(&self) -> Option<&spec::ProgressToken> {
169        self.inner.meta.progress_token.as_ref()
170    }
171
172    /// Whether the client declared support for an extension.
173    #[must_use]
174    pub fn client_supports_extension(&self, id: &str) -> bool {
175        self.inner
176            .meta
177            .client_capabilities
178            .extensions
179            .as_ref()
180            .is_some_and(|e| e.contains_key(id))
181    }
182
183    /// A token cancelled when the client cancels the request (stdio
184    /// `notifications/cancelled`, or the HTTP response stream closing).
185    /// Long-running tools should stop work when it fires.
186    #[must_use]
187    pub fn cancellation(&self) -> &CancellationToken {
188        &self.inner.cancellation
189    }
190
191    /// Whether the request has been cancelled.
192    #[must_use]
193    pub fn is_cancelled(&self) -> bool {
194        self.inner.cancellation.is_cancelled()
195    }
196
197    /// Values attached by the transport.
198    #[must_use]
199    pub fn transport(&self) -> &TransportContext {
200        &self.inner.transport
201    }
202
203    /// Shared state registered with
204    /// [`ServerBuilder::state`](crate::ServerBuilder::state).
205    #[must_use]
206    pub fn state<T: Send + Sync + 'static>(&self) -> Option<State<T>> {
207        self.inner.state.get::<T>().map(State)
208    }
209}
210
211impl fmt::Debug for Ctx {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        f.debug_struct("Ctx")
214            .field("request_id", &self.inner.request_id)
215            .field("protocol_version", &self.inner.meta.protocol_version)
216            .field("cancelled", &self.is_cancelled())
217            .finish_non_exhaustive()
218    }
219}
220
221/// Shared application state registered with
222/// [`ServerBuilder::state`](crate::ServerBuilder::state). Declare a
223/// `State<T>` parameter in a tool to receive it; it is excluded from the
224/// tool's input schema. A tool that needs a state type that was never
225/// registered makes [`ServerBuilder::build`](crate::ServerBuilder::build) fail.
226pub struct State<T: ?Sized>(Arc<T>);
227
228impl<T: ?Sized> State<T> {
229    /// The shared pointer to the state.
230    #[must_use]
231    pub fn arc(&self) -> &Arc<T> {
232        &self.0
233    }
234
235    /// Unwraps into the shared pointer.
236    #[must_use]
237    pub fn into_arc(self) -> Arc<T> {
238        self.0
239    }
240}
241
242impl<T: ?Sized> Clone for State<T> {
243    fn clone(&self) -> Self {
244        Self(Arc::clone(&self.0))
245    }
246}
247
248impl<T: ?Sized> Deref for State<T> {
249    type Target = T;
250    fn deref(&self) -> &T {
251        &self.0
252    }
253}
254
255impl<T: ?Sized + fmt::Debug> fmt::Debug for State<T> {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        f.debug_tuple("State").field(&self.0).finish()
258    }
259}