neva/types/
request.rs

1//! Represents a request from an MCP client
2
3use std::fmt;
4use std::fmt::{Debug, Formatter};
5use serde::{Serialize, Deserialize};
6use super::{ProgressToken, Message, JSONRPC_VERSION};
7
8#[cfg(feature = "server")]
9use crate::Context;
10
11#[cfg(feature = "http-server")]
12use {
13    crate::auth::DefaultClaims,
14    volga::headers::HeaderMap
15};
16
17pub use from_request::FromRequest;
18pub use request_id::RequestId;
19
20pub mod from_request;
21pub mod request_id;
22
23/// A request in the JSON-RPC protocol.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Request {
26    /// JSON-RPC protocol version. 
27    ///
28    /// > **Note:** always 2.0.
29    pub jsonrpc: String,
30
31    /// Request identifier. Must be a string or number and unique within the session.
32    pub id: RequestId,
33    
34    /// Name of the method to invoke.
35    pub method: String,
36    
37    /// Optional parameters for the method.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub params: Option<serde_json::Value>,
40    
41    /// Current MCP Session ID
42    #[serde(skip)]
43    pub session_id: Option<uuid::Uuid>,
44
45    /// HTTP headers
46    #[serde(skip)]
47    #[cfg(feature = "http-server")]
48    pub headers: HeaderMap,
49
50    /// Authentication and Authorization claims
51    #[serde(skip)]
52    #[cfg(feature = "http-server")]
53    pub claims: Option<Box<DefaultClaims>>,
54}
55
56/// Provides metadata related to the request that provides additional protocol-level information.
57/// 
58/// > **Note:** This class contains properties that are used by the Model Context Protocol
59/// > for features like progress tracking and other protocol-specific capabilities.
60#[derive(Default, Clone, Deserialize, Serialize)]
61pub struct RequestParamsMeta {
62    /// An opaque token that will be attached to any subsequent progress notifications.
63    /// 
64    /// > **Note:** The receiver is not obligated to provide these notifications.
65    #[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
66    pub progress_token: Option<ProgressToken>,
67    
68    /// MCP request context
69    #[serde(skip)]
70    #[cfg(feature = "server")]
71    pub(crate) context: Option<Context>
72}
73
74impl Debug for RequestParamsMeta {
75    #[inline]
76    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77        f.debug_struct("RequestParamsMeta")
78            .field("progress_token", &self.progress_token)
79            .finish()
80    }
81}
82
83impl From<Request> for Message {
84    #[inline]
85    fn from(request: Request) -> Self {
86        Self::Request(request)
87    }
88}
89
90impl RequestParamsMeta {
91    /// Creates a new [`RequestParamsMeta`] with [`ProgressToken`] for a specific [`RequestId`]
92    pub fn new(id: &RequestId) -> Self {
93        Self {
94            progress_token: Some(ProgressToken::from(id)),
95            #[cfg(feature = "server")]
96            context: None
97        }
98    }
99}
100
101impl Request {
102    /// Creates a new [`Request`]
103    pub fn new<T: Serialize>(id: Option<RequestId>, method: &str, params: Option<T>) -> Self {
104        Self {
105            jsonrpc: JSONRPC_VERSION.into(),
106            session_id: None,
107            id: id.unwrap_or_default(),
108            method: method.into(),
109            params: params.and_then(|p| serde_json::to_value(p).ok()),
110            #[cfg(feature = "http-server")]
111            headers: HeaderMap::with_capacity(8),
112            #[cfg(feature = "http-server")]
113            claims: None,
114        }
115    }
116
117    /// Returns request's id if it's specified, otherwise returns default value
118    ///
119    /// Default: `(no id)`
120    pub fn id(&self) -> RequestId {
121        self.id.clone()
122    }
123
124    /// Returns the full id (session_id?/request_id)
125    pub fn full_id(&self) -> RequestId {
126        let id = self.id.clone();
127        if let Some(session_id) = self.session_id {
128            id.concat(RequestId::Uuid(session_id))
129        } else {
130            id
131        }
132    }
133    
134    /// Returns [`Request`] params metadata
135    pub fn meta(&self) -> Option<RequestParamsMeta> {
136        self.params.as_ref()?
137            .get("_meta")
138            .cloned()
139            .and_then(|meta| serde_json::from_value(meta).ok())
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    
146}