1use serde::{Deserialize, Deserializer, Serialize};
16use serde_json::{Map, Value};
17use std::borrow::Cow;
18
19pub const LATEST_PROTOCOL_VERSION: &str = "2026-07-28";
21
22pub const LATEST_HANDSHAKE_PROTOCOL_VERSION: &str = "2025-11-25";
26
27pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &[
29 "2026-07-28",
30 "2025-11-25",
31 "2025-06-18",
32 "2025-03-26",
33 "2024-11-05",
34];
35
36#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40#[serde(untagged)]
41pub enum RequestId {
42 String(String),
43 Integer(i64),
44}
45
46impl From<i64> for RequestId {
47 fn from(value: i64) -> Self {
48 Self::Integer(value)
49 }
50}
51
52impl From<String> for RequestId {
53 fn from(value: String) -> Self {
54 Self::String(value)
55 }
56}
57
58impl From<&str> for RequestId {
59 fn from(value: &str) -> Self {
60 Self::String(value.into())
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct JsonRpcRequest {
67 pub jsonrpc: Cow<'static, str>,
68 pub id: RequestId,
69 pub method: String,
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub params: Option<Value>,
72}
73
74impl JsonRpcRequest {
75 pub fn new(id: impl Into<RequestId>, method: impl Into<String>, params: Option<Value>) -> Self {
76 Self {
77 jsonrpc: Cow::Borrowed("2.0"),
78 id: id.into(),
79 method: method.into(),
80 params,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct JsonRpcNotification {
88 pub jsonrpc: Cow<'static, str>,
89 pub method: String,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub params: Option<Value>,
92}
93
94impl JsonRpcNotification {
95 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
96 Self {
97 jsonrpc: Cow::Borrowed("2.0"),
98 method: method.into(),
99 params,
100 }
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct JsonRpcResponse {
108 pub jsonrpc: Cow<'static, str>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub id: Option<RequestId>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub result: Option<Value>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub error: Option<JsonRpcError>,
115}
116
117impl JsonRpcResponse {
118 pub fn success(id: RequestId, result: impl Serialize) -> Self {
119 Self {
120 jsonrpc: Cow::Borrowed("2.0"),
121 id: Some(id),
122 result: Some(serde_json::to_value(result).unwrap_or(Value::Null)),
123 error: None,
124 }
125 }
126
127 pub fn error(id: impl Into<Option<RequestId>>, error: JsonRpcError) -> Self {
128 Self {
129 jsonrpc: Cow::Borrowed("2.0"),
130 id: id.into(),
131 result: None,
132 error: Some(error),
133 }
134 }
135
136 pub fn into_result(self) -> Result<Value, JsonRpcError> {
139 match (self.result, self.error) {
140 (_, Some(error)) => Err(error),
141 (Some(result), None) => Ok(result),
142 (None, None) => Err(JsonRpcError {
143 code: error_codes::INTERNAL_ERROR,
144 message: "response carried neither result nor error".into(),
145 data: None,
146 }),
147 }
148 }
149}
150
151pub mod error_codes {
154 pub const PARSE_ERROR: i64 = -32700;
155 pub const INVALID_REQUEST: i64 = -32600;
156 pub const METHOD_NOT_FOUND: i64 = -32601;
157 pub const INVALID_PARAMS: i64 = -32602;
158 pub const INTERNAL_ERROR: i64 = -32603;
159 pub const HEADER_MISMATCH: i64 = -32020;
160 pub const MISSING_REQUIRED_CLIENT_CAPABILITY: i64 = -32021;
161 pub const UNSUPPORTED_PROTOCOL_VERSION: i64 = -32022;
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct JsonRpcError {
167 pub code: i64,
168 pub message: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub data: Option<Value>,
171}
172
173impl JsonRpcError {
174 pub fn method_not_found(method: &str) -> Self {
175 Self {
176 code: error_codes::METHOD_NOT_FOUND,
177 message: format!("unknown method: {method}"),
178 data: None,
179 }
180 }
181
182 pub fn invalid_params(message: impl Into<String>) -> Self {
183 Self {
184 code: error_codes::INVALID_PARAMS,
185 message: message.into(),
186 data: None,
187 }
188 }
189
190 pub fn internal(message: impl Into<String>) -> Self {
191 Self {
192 code: error_codes::INTERNAL_ERROR,
193 message: message.into(),
194 data: None,
195 }
196 }
197}
198
199impl std::fmt::Display for JsonRpcError {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 write!(f, "{} (code {})", self.message, self.code)
202 }
203}
204
205impl std::error::Error for JsonRpcError {}
206
207#[derive(Debug, Clone, Serialize)]
211#[serde(untagged)]
212pub enum JsonRpcMessage {
213 Request(JsonRpcRequest),
214 Notification(JsonRpcNotification),
215 Response(JsonRpcResponse),
216}
217
218impl<'de> Deserialize<'de> for JsonRpcMessage {
219 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
220 where
221 D: Deserializer<'de>,
222 {
223 use serde::de::Error;
224 let value = Value::deserialize(deserializer)?;
225 let object = value
226 .as_object()
227 .ok_or_else(|| D::Error::custom("expected a JSON-RPC object"))?;
228 if object.contains_key("method") {
229 if object.contains_key("id") {
230 serde_json::from_value(value).map(Self::Request)
231 } else {
232 serde_json::from_value(value).map(Self::Notification)
233 }
234 .map_err(D::Error::custom)
235 } else if object.contains_key("result") || object.contains_key("error") {
236 serde_json::from_value(value)
237 .map(Self::Response)
238 .map_err(D::Error::custom)
239 } else {
240 Err(D::Error::custom(
241 "object is neither a request, a notification, nor a response",
242 ))
243 }
244 }
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
251#[serde(rename_all = "camelCase")]
252pub struct Implementation {
253 pub name: Cow<'static, str>,
254 pub version: Cow<'static, str>,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub title: Option<String>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub description: Option<String>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub website_url: Option<String>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub icons: Option<Vec<Icon>>,
263}
264
265impl Implementation {
266 pub fn new(name: impl Into<Cow<'static, str>>, version: impl Into<Cow<'static, str>>) -> Self {
267 Self {
268 name: name.into(),
269 version: version.into(),
270 title: None,
271 description: None,
272 website_url: None,
273 icons: None,
274 }
275 }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
280#[serde(rename_all = "camelCase")]
281pub struct Icon {
282 pub src: String,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub mime_type: Option<String>,
285 #[serde(skip_serializing_if = "Option::is_none")]
286 pub sizes: Option<Vec<String>>,
287 #[serde(skip_serializing_if = "Option::is_none")]
288 pub theme: Option<String>,
289}
290
291#[derive(Debug, Clone, Default, Serialize, Deserialize)]
294#[serde(rename_all = "camelCase")]
295pub struct ClientCapabilities {
296 #[serde(skip_serializing_if = "Option::is_none")]
297 pub elicitation: Option<Value>,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub roots: Option<Value>,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub sampling: Option<Value>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub experimental: Option<Map<String, Value>>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub extensions: Option<Map<String, Value>>,
306}
307
308#[derive(Debug, Clone, Default, Serialize, Deserialize)]
310#[serde(rename_all = "camelCase")]
311pub struct ServerCapabilities {
312 #[serde(skip_serializing_if = "Option::is_none")]
313 pub tools: Option<ToolsCapability>,
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub prompts: Option<PromptsCapability>,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub resources: Option<ResourcesCapability>,
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub completions: Option<Value>,
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub logging: Option<Value>,
322 #[serde(skip_serializing_if = "Option::is_none")]
323 pub experimental: Option<Map<String, Value>>,
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub extensions: Option<Map<String, Value>>,
326}
327
328impl ServerCapabilities {
329 pub fn tools_only() -> Self {
331 Self {
332 tools: Some(ToolsCapability::default()),
333 ..Self::default()
334 }
335 }
336}
337
338#[derive(Debug, Clone, Default, Serialize, Deserialize)]
339#[serde(rename_all = "camelCase")]
340pub struct ToolsCapability {
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub list_changed: Option<bool>,
343}
344
345#[derive(Debug, Clone, Default, Serialize, Deserialize)]
346#[serde(rename_all = "camelCase")]
347pub struct PromptsCapability {
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub list_changed: Option<bool>,
350}
351
352#[derive(Debug, Clone, Default, Serialize, Deserialize)]
353#[serde(rename_all = "camelCase")]
354pub struct ResourcesCapability {
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub list_changed: Option<bool>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub subscribe: Option<bool>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
366#[serde(rename_all = "camelCase")]
367pub struct InitializeRequestParams {
368 pub protocol_version: String,
369 #[serde(default)]
370 pub capabilities: ClientCapabilities,
371 pub client_info: Implementation,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
378#[serde(rename_all = "camelCase")]
379pub struct InitializeResult {
380 pub protocol_version: String,
381 #[serde(default)]
382 pub capabilities: ServerCapabilities,
383 pub server_info: Implementation,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub instructions: Option<String>,
386 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
387 pub meta: Option<Map<String, Value>>,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
395#[serde(rename_all = "camelCase")]
396pub struct DiscoverResult {
397 pub supported_versions: Vec<String>,
398 #[serde(default)]
399 pub capabilities: ServerCapabilities,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub instructions: Option<String>,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub ttl_ms: Option<u64>,
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub cache_scope: Option<CacheScope>,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub result_type: Option<ResultType>,
408 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
409 pub meta: Option<Map<String, Value>>,
410}
411
412#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
415#[serde(rename_all = "lowercase")]
416pub enum CacheScope {
417 Public,
418 Private,
419}
420
421#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
429#[serde(rename_all = "snake_case")]
430pub enum ResultType {
431 #[default]
433 Complete,
434 InputRequired,
437 #[serde(untagged)]
438 Other(String),
439}
440
441pub mod meta_keys {
446 pub const PROTOCOL_VERSION: &str = "io.modelcontextprotocol/protocolVersion";
448 pub const CLIENT_INFO: &str = "io.modelcontextprotocol/clientInfo";
450 pub const CLIENT_CAPABILITIES: &str = "io.modelcontextprotocol/clientCapabilities";
453 pub const SERVER_INFO: &str = "io.modelcontextprotocol/serverInfo";
455 pub const LOG_LEVEL: &str = "io.modelcontextprotocol/logLevel";
457 pub const SUBSCRIPTION_ID: &str = "io.modelcontextprotocol/subscriptionId";
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct Tool {
468 pub name: String,
469 pub input_schema: Value,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub title: Option<String>,
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub description: Option<String>,
477 #[serde(skip_serializing_if = "Option::is_none")]
478 pub output_schema: Option<Value>,
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub annotations: Option<ToolAnnotations>,
481 #[serde(skip_serializing_if = "Option::is_none")]
482 pub icons: Option<Vec<Icon>>,
483 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
484 pub meta: Option<Map<String, Value>>,
485}
486
487impl Tool {
488 pub fn new(name: impl Into<String>, input_schema: Value) -> Self {
489 Self {
490 name: name.into(),
491 input_schema,
492 title: None,
493 description: None,
494 output_schema: None,
495 annotations: None,
496 icons: None,
497 meta: None,
498 }
499 }
500}
501
502#[derive(Debug, Clone, Default, Serialize, Deserialize)]
505#[serde(rename_all = "camelCase")]
506pub struct ToolAnnotations {
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub title: Option<String>,
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub read_only_hint: Option<bool>,
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub destructive_hint: Option<bool>,
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub idempotent_hint: Option<bool>,
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub open_world_hint: Option<bool>,
517}
518
519#[derive(Debug, Clone, Default, Serialize, Deserialize)]
521#[serde(rename_all = "camelCase")]
522pub struct ListToolsResult {
523 pub tools: Vec<Tool>,
524 #[serde(skip_serializing_if = "Option::is_none")]
525 pub next_cursor: Option<String>,
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub ttl_ms: Option<u64>,
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub cache_scope: Option<CacheScope>,
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub result_type: Option<ResultType>,
532 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
533 pub meta: Option<Map<String, Value>>,
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize)]
538#[serde(rename_all = "camelCase")]
539pub struct CallToolRequestParams {
540 pub name: String,
541 #[serde(skip_serializing_if = "Option::is_none")]
542 pub arguments: Option<Map<String, Value>>,
543 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
544 pub meta: Option<Map<String, Value>>,
545}
546
547#[derive(Debug, Clone, Default, Serialize, Deserialize)]
552#[serde(rename_all = "camelCase")]
553pub struct CallToolResult {
554 pub content: Vec<ContentBlock>,
555 #[serde(skip_serializing_if = "Option::is_none")]
556 pub structured_content: Option<Value>,
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub is_error: Option<bool>,
559 #[serde(skip_serializing_if = "Option::is_none")]
560 pub result_type: Option<ResultType>,
561 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
562 pub meta: Option<Map<String, Value>>,
563}
564
565impl CallToolResult {
566 pub fn text(text: impl Into<String>) -> Self {
567 Self {
568 content: vec![ContentBlock::text(text)],
569 result_type: Some(ResultType::Complete),
570 ..Self::default()
571 }
572 }
573
574 pub fn from_content(content: Vec<ContentBlock>, structured_content: Option<Value>) -> Self {
577 Self {
578 content,
579 structured_content,
580 result_type: Some(ResultType::Complete),
581 ..Self::default()
582 }
583 }
584
585 pub fn error(text: impl Into<String>) -> Self {
586 Self {
587 is_error: Some(true),
588 ..Self::text(text)
589 }
590 }
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize)]
602#[serde(rename_all = "camelCase")]
603pub struct InputRequiredResult {
604 #[serde(skip_serializing_if = "Option::is_none")]
609 pub input_requests: Option<Map<String, Value>>,
610 #[serde(skip_serializing_if = "Option::is_none")]
614 pub request_state: Option<String>,
615 #[serde(skip_serializing_if = "Option::is_none")]
616 pub result_type: Option<ResultType>,
617 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
618 pub meta: Option<Map<String, Value>>,
619}
620
621#[derive(Debug, Clone, Serialize)]
629#[serde(untagged)]
630pub enum ToolCallOutcome {
631 Complete(CallToolResult),
632 InputRequired(InputRequiredResult),
633}
634
635impl<'de> Deserialize<'de> for ToolCallOutcome {
636 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
637 where
638 D: Deserializer<'de>,
639 {
640 use serde::de::Error;
641 let value = Value::deserialize(deserializer)?;
642 let result_type = value.get("resultType").and_then(Value::as_str);
643 if result_type == Some("input_required") {
644 serde_json::from_value(value).map(Self::InputRequired)
645 } else {
646 serde_json::from_value(value).map(Self::Complete)
647 }
648 .map_err(D::Error::custom)
649 }
650}
651
652#[derive(Debug, Clone, Default)]
665pub struct RequestContext {
666 pub protocol_version: Option<String>,
668 pub client_info: Option<Implementation>,
672 pub client_capabilities: ClientCapabilities,
675}
676
677impl RequestContext {
678 pub fn from_meta(meta: Option<&Map<String, Value>>) -> Self {
680 let Some(meta) = meta else {
681 return Self::default();
682 };
683
684 Self {
685 protocol_version: meta
686 .get(meta_keys::PROTOCOL_VERSION)
687 .and_then(Value::as_str)
688 .map(String::from),
689 client_info: meta
690 .get(meta_keys::CLIENT_INFO)
691 .cloned()
692 .and_then(|value| serde_json::from_value(value).ok()),
693 client_capabilities: meta
694 .get(meta_keys::CLIENT_CAPABILITIES)
695 .cloned()
696 .and_then(|value| serde_json::from_value(value).ok())
697 .unwrap_or_default(),
698 }
699 }
700
701 pub fn from_params(params: Option<&Value>) -> Self {
703 Self::from_meta(
704 params
705 .and_then(|params| params.get("_meta"))
706 .and_then(Value::as_object),
707 )
708 }
709
710 pub fn supports_elicitation(&self) -> bool {
713 self.client_capabilities.elicitation.is_some()
714 }
715}
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
721#[serde(tag = "type")]
722pub enum ContentBlock {
723 #[serde(rename = "text", rename_all = "camelCase")]
724 Text {
725 text: String,
726 #[serde(skip_serializing_if = "Option::is_none")]
727 annotations: Option<Annotations>,
728 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
729 meta: Option<Map<String, Value>>,
730 },
731 #[serde(rename = "image", rename_all = "camelCase")]
732 Image {
733 data: String,
735 mime_type: String,
736 #[serde(skip_serializing_if = "Option::is_none")]
737 annotations: Option<Annotations>,
738 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
739 meta: Option<Map<String, Value>>,
740 },
741 #[serde(rename = "audio", rename_all = "camelCase")]
742 Audio {
743 data: String,
745 mime_type: String,
746 #[serde(skip_serializing_if = "Option::is_none")]
747 annotations: Option<Annotations>,
748 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
749 meta: Option<Map<String, Value>>,
750 },
751 #[serde(rename = "resource_link", rename_all = "camelCase")]
752 ResourceLink {
753 uri: String,
754 name: String,
755 #[serde(skip_serializing_if = "Option::is_none")]
756 title: Option<String>,
757 #[serde(skip_serializing_if = "Option::is_none")]
758 description: Option<String>,
759 #[serde(skip_serializing_if = "Option::is_none")]
760 mime_type: Option<String>,
761 #[serde(skip_serializing_if = "Option::is_none")]
762 size: Option<u64>,
763 #[serde(skip_serializing_if = "Option::is_none")]
764 icons: Option<Vec<Icon>>,
765 #[serde(skip_serializing_if = "Option::is_none")]
766 annotations: Option<Annotations>,
767 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
768 meta: Option<Map<String, Value>>,
769 },
770 #[serde(rename = "resource", rename_all = "camelCase")]
771 EmbeddedResource {
772 resource: ResourceContents,
773 #[serde(skip_serializing_if = "Option::is_none")]
774 annotations: Option<Annotations>,
775 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
776 meta: Option<Map<String, Value>>,
777 },
778}
779
780impl ContentBlock {
781 pub fn text(text: impl Into<String>) -> Self {
782 Self::Text {
783 text: text.into(),
784 annotations: None,
785 meta: None,
786 }
787 }
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize)]
792#[serde(untagged)]
793pub enum ResourceContents {
794 #[serde(rename_all = "camelCase")]
795 Text {
796 uri: String,
797 #[serde(skip_serializing_if = "Option::is_none")]
798 mime_type: Option<String>,
799 text: String,
800 },
801 #[serde(rename_all = "camelCase")]
802 Blob {
803 uri: String,
804 #[serde(skip_serializing_if = "Option::is_none")]
805 mime_type: Option<String>,
806 blob: String,
808 },
809}
810
811#[derive(Debug, Clone, Default, Serialize, Deserialize)]
813#[serde(rename_all = "camelCase")]
814pub struct Annotations {
815 #[serde(skip_serializing_if = "Option::is_none")]
816 pub audience: Option<Vec<String>>,
817 #[serde(skip_serializing_if = "Option::is_none")]
818 pub priority: Option<f64>,
819 #[serde(skip_serializing_if = "Option::is_none")]
820 pub last_modified: Option<String>,
821}
822
823#[derive(Serialize, Deserialize, Debug)]
827pub struct Example<T> {
828 pub description: &'static str,
829 #[serde(flatten)]
830 pub item: T,
831}