Skip to main content

mcp_kit/server/
extract.rs

1/// Extractors for tool/resource/prompt handlers — inspired by axum's extractor pattern.
2use crate::error::{McpError, McpResult};
3use serde::de::DeserializeOwned;
4
5// ─── Json<T> extractor ────────────────────────────────────────────────────────
6
7/// Deserialize the full arguments object as type `T`.
8pub struct Json<T>(pub T);
9
10impl<T: DeserializeOwned> Json<T> {
11    pub fn from_value(v: serde_json::Value) -> McpResult<Self> {
12        serde_json::from_value(v)
13            .map(Json)
14            .map_err(|e| McpError::InvalidParams(e.to_string()))
15    }
16}
17
18impl<T> std::ops::Deref for Json<T> {
19    type Target = T;
20    fn deref(&self) -> &Self::Target {
21        &self.0
22    }
23}
24
25// ─── State<S> extractor ───────────────────────────────────────────────────────
26
27/// Share arbitrary state across handlers.
28#[derive(Clone)]
29pub struct State<S>(pub S);
30
31impl<S> std::ops::Deref for State<S> {
32    type Target = S;
33    fn deref(&self) -> &Self::Target {
34        &self.0
35    }
36}
37
38// ─── Extension<T> extractor ───────────────────────────────────────────────────
39
40/// Type-map extension data attached to a session.
41#[derive(Clone)]
42pub struct Extension<T>(pub T);
43
44impl<T> std::ops::Deref for Extension<T> {
45    type Target = T;
46    fn deref(&self) -> &Self::Target {
47        &self.0
48    }
49}
50
51// ─── Auth extractor ───────────────────────────────────────────────────────────
52
53/// Extracts the [`AuthenticatedIdentity`] for the current request.
54///
55/// Available inside `#[tool]`, `#[resource]`, and `#[prompt]` handlers when
56/// the `auth` feature is enabled and the server is configured with an auth
57/// provider.
58///
59/// Returns [`McpError::Unauthorized`] if the request is unauthenticated.
60///
61/// # Example
62///
63/// ```rust,no_run
64/// use mcp_kit::prelude::*;
65/// use mcp_kit::Auth;
66///
67/// #[mcp_kit::tool(description = "A protected tool")]
68/// async fn my_tool(input: String, auth: Auth) -> McpResult<CallToolResult> {
69///     if !auth.has_scope("tools:execute") {
70///         return Err(McpError::Unauthorized("missing scope".into()));
71///     }
72///     Ok(CallToolResult::text(format!("Hello, {}!", auth.subject)))
73/// }
74/// ```
75///
76/// [`AuthenticatedIdentity`]: crate::auth::AuthenticatedIdentity
77/// [`McpError::Unauthorized`]: crate::error::McpError::Unauthorized
78#[cfg(feature = "auth")]
79pub struct Auth(pub crate::auth::AuthenticatedIdentity);
80
81#[cfg(feature = "auth")]
82impl Auth {
83    /// Pull the current identity from the task-local auth context.
84    /// Returns `Err(McpError::Unauthorized)` if no identity is present.
85    pub fn from_context() -> McpResult<Self> {
86        crate::server::auth_context::current()
87            .map(Auth)
88            .ok_or_else(|| McpError::Unauthorized("unauthenticated request".into()))
89    }
90}
91
92#[cfg(feature = "auth")]
93impl std::ops::Deref for Auth {
94    type Target = crate::auth::AuthenticatedIdentity;
95    fn deref(&self) -> &Self::Target {
96        &self.0
97    }
98}