Skip to main content

turbomcp_types/
wire.rs

1//! MCP protocol wire-wrapper types.
2//!
3//! Request/response/notification envelopes exchanged over the wire by MCP
4//! clients and servers. These are the canonical definitions; `turbomcp-protocol`
5//! re-exports them verbatim.
6//!
7//! Kept `no_std + alloc` compatible so WASM and embedded consumers can depend
8//! on the same types as native transports.
9
10#[cfg(not(feature = "std"))]
11use alloc::{collections::BTreeMap as HashMap, string::String, vec, vec::Vec};
12#[cfg(feature = "std")]
13use std::collections::HashMap;
14
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17
18use crate::content::{Content, PromptMessage};
19use crate::definitions::Implementation;
20use crate::protocol::{ClientCapabilities, ServerCapabilities};
21
22// =============================================================================
23// Initialization handshake
24// =============================================================================
25
26/// The `initialize` request sent by the client as the first message after connection.
27///
28/// Used to exchange capabilities and agree on a protocol version for the session.
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct InitializeRequest {
31    /// The protocol version the client wishes to use.
32    #[serde(rename = "protocolVersion")]
33    pub protocol_version: crate::primitives::ProtocolVersion,
34    /// The capabilities supported by the client.
35    pub capabilities: ClientCapabilities,
36    /// Information about the client's implementation (e.g., name, version).
37    #[serde(rename = "clientInfo")]
38    pub client_info: Implementation,
39    /// Optional metadata for the request.
40    ///
41    /// Per MCP 2025-11-25, `_meta` is always `{ [key: string]: unknown }` —
42    /// modelled here as `HashMap<String, Value>` so non-object values are
43    /// rejected at deserialize time.
44    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
45    pub meta: Option<HashMap<String, Value>>,
46}
47
48/// The response to a successful `initialize` request.
49///
50/// Server confirms connection parameters and declares its own capabilities.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct InitializeResult {
53    /// The protocol version that will be used for the session, chosen by the server.
54    #[serde(rename = "protocolVersion")]
55    pub protocol_version: crate::primitives::ProtocolVersion,
56    /// The capabilities supported by the server.
57    pub capabilities: ServerCapabilities,
58    /// Information about the server's implementation (e.g., name, version).
59    #[serde(rename = "serverInfo")]
60    pub server_info: Implementation,
61    /// Optional human-readable instructions for the client.
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub instructions: Option<String>,
64    /// Optional metadata for the result. See note on `InitializeRequest::meta`.
65    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
66    pub meta: Option<HashMap<String, Value>>,
67}
68
69/// Sent by the client after a successful `InitializeResult` to confirm readiness.
70///
71/// This notification has no parameters.
72#[derive(Debug, Clone, Default, Serialize, Deserialize)]
73pub struct InitializedNotification {}
74
75// =============================================================================
76// Tool invocation
77// =============================================================================
78
79/// The result of a `CallToolRequest`.
80#[derive(Debug, Clone, Serialize, Deserialize, Default)]
81pub struct CallToolResult {
82    /// The output of the tool as a series of content blocks. Required.
83    pub content: Vec<Content>,
84    /// Whether the tool execution resulted in an error.
85    ///
86    /// When `true`, all content blocks should be treated as error information;
87    /// the message may span multiple text blocks for structured error reporting.
88    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
89    pub is_error: Option<bool>,
90    /// Optional structured output conforming to the tool's `output_schema`.
91    ///
92    /// Tools that emit structured content SHOULD also include the serialized
93    /// JSON in a `TextContent` block for clients that don't support structured
94    /// output.
95    #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
96    pub structured_content: Option<Value>,
97    /// Optional metadata for the result.
98    ///
99    /// For client applications and tools to pass context that should NOT be
100    /// exposed to LLMs (tracking IDs, metrics, cache status, etc.).
101    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
102    pub meta: Option<HashMap<String, Value>>,
103}
104
105impl CallToolResult {
106    /// Create a successful result with a single text content block.
107    #[must_use]
108    pub fn text(text: impl Into<String>) -> Self {
109        Self {
110            content: vec![Content::text(text)],
111            ..Default::default()
112        }
113    }
114
115    /// Create an error result with a single text content block and `is_error = true`.
116    #[must_use]
117    pub fn error(message: impl Into<String>) -> Self {
118        Self {
119            content: vec![Content::text(message)],
120            is_error: Some(true),
121            ..Default::default()
122        }
123    }
124
125    /// Create a successful JSON result (pretty-printed text content).
126    pub fn json<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
127        let text = serde_json::to_string_pretty(value)?;
128        Ok(Self::text(text))
129    }
130
131    /// Create a result with multiple content items.
132    #[must_use]
133    pub fn contents(contents: Vec<Content>) -> Self {
134        Self {
135            content: contents,
136            ..Default::default()
137        }
138    }
139
140    /// Create an image result (base64-encoded).
141    #[must_use]
142    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
143        Self {
144            content: vec![Content::image(data, mime_type)],
145            ..Default::default()
146        }
147    }
148
149    /// Extracts and concatenates all text content (newline-joined).
150    ///
151    /// Returns an empty string if no text blocks are present.
152    pub fn all_text(&self) -> String {
153        let texts: Vec<&str> = self.content.iter().filter_map(Content::as_text).collect();
154        texts.join("\n")
155    }
156
157    /// Returns the text of the first text block, if any.
158    pub fn first_text(&self) -> Option<&str> {
159        self.content.first().and_then(Content::as_text)
160    }
161
162    /// Whether `is_error` is explicitly `true`.
163    pub fn has_error(&self) -> bool {
164        self.is_error.unwrap_or(false)
165    }
166}
167
168// =============================================================================
169// Prompt retrieval
170// =============================================================================
171
172/// The result of a `prompts/get` request.
173#[derive(Debug, Clone, Default, Serialize, Deserialize)]
174pub struct GetPromptResult {
175    /// Optional description of this prompt.
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub description: Option<String>,
178    /// The sequence of messages that compose the prompt.
179    pub messages: Vec<PromptMessage>,
180    /// Optional metadata for the result.
181    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
182    pub meta: Option<HashMap<String, Value>>,
183}