pmcp/lib.rs
1//! # MCP SDK for Rust
2//!
3//! A high-quality Rust implementation of the Model Context Protocol (MCP) SDK.
4//!
5//! This crate provides both client and server implementations of MCP with:
6//! - Full protocol compatibility with the TypeScript SDK
7//! - Zero-copy parsing where possible
8//! - Comprehensive type safety
9//! - Multiple transport options (stdio, HTTP/SSE, WebSocket)
10//! - Built-in authentication support
11//!
12//! ## Quick Start
13//!
14//! ### Client Example
15//!
16//! ```rust
17//! use pmcp::{Client, StdioTransport, ClientCapabilities};
18//!
19//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
20//! // Create a client with stdio transport
21//! let transport = StdioTransport::new();
22//! let mut client = Client::new(transport);
23//!
24//! // Initialize the connection
25//! let server_info = client.initialize(ClientCapabilities::default()).await?;
26//!
27//! // List available tools
28//! let tools = client.list_tools(None).await?;
29//! # Ok(())
30//! # }
31//! ```
32//!
33//! ### Server Example
34//!
35//! ```rust
36//! use pmcp::{Server, ServerCapabilities, ToolHandler};
37//! use async_trait::async_trait;
38//! use serde_json::Value;
39//!
40//! struct MyTool;
41//!
42//! #[async_trait]
43//! impl ToolHandler for MyTool {
44//! async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> Result<Value, pmcp::Error> {
45//! Ok(serde_json::json!({"result": "success"}))
46//! }
47//! }
48//!
49//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
50//! let server = Server::builder()
51//! .name("my-server")
52//! .version("1.0.0")
53//! .capabilities(ServerCapabilities::default())
54//! .tool("my-tool", MyTool)
55//! .build()?;
56//!
57//! // Run with stdio transport
58//! server.run_stdio().await?;
59//! # Ok(())
60//! # }
61//! ```
62
63#![warn(
64 missing_docs,
65 missing_debug_implementations,
66 rust_2018_idioms,
67 unreachable_pub
68)]
69#![deny(unsafe_code)]
70#![cfg_attr(docsrs, feature(doc_cfg))]
71// Allow certain clippy lints that are too pedantic for this codebase
72#![allow(clippy::missing_errors_doc)]
73#![allow(clippy::return_self_not_must_use)]
74#![allow(clippy::multiple_crate_versions)]
75#![allow(clippy::result_large_err)]
76
77pub mod assets;
78pub mod client;
79#[cfg(feature = "composition")]
80#[cfg_attr(docsrs, doc(cfg(feature = "composition")))]
81pub mod composition;
82pub mod error;
83pub mod runtime;
84pub mod server;
85pub mod shared;
86pub mod types;
87pub mod utils;
88
89#[cfg(feature = "simd")]
90pub mod simd;
91
92// Re-export commonly used types
93pub use client::{Client, ClientBuilder};
94pub use error::{Error, ErrorCode, Result};
95#[cfg(not(target_arch = "wasm32"))]
96pub use server::cancellation::RequestHandlerExtra;
97#[cfg(not(target_arch = "wasm32"))]
98pub use server::{
99 auth,
100 simple_prompt::{SimplePrompt, SyncPrompt},
101 simple_resources::{DynamicResourceHandler, ResourceCollection, StaticResource},
102 simple_tool::{SimpleTool, SyncTool},
103 typed_tool::{SimpleToolExt, SyncToolExt, TypedSyncTool, TypedTool, TypedToolWithOutput},
104 ui::UIResourceBuilder,
105 PromptHandler, ResourceHandler, SamplingHandler, Server, ServerBuilder, ToolHandler,
106};
107#[cfg(target_arch = "wasm32")]
108pub use server::{
109 wasm_server::{
110 SimpleTool, WasmMcpServer, WasmMcpServerBuilder, WasmPrompt, WasmResource, WasmTool,
111 },
112 wasm_typed_tool::WasmTypedTool,
113};
114// Re-export WASM server types under their native names for compatibility
115#[cfg(target_arch = "wasm32")]
116pub use server::wasm_server::{WasmMcpServer as Server, WasmMcpServerBuilder as ServerBuilder};
117#[cfg(target_arch = "wasm32")]
118pub use server::wasm_typed_tool::WasmTypedTool as TypedTool;
119#[cfg(not(target_arch = "wasm32"))]
120pub use shared::StdioTransport;
121pub use shared::{
122 batch::{BatchRequest, BatchResponse},
123 uri_template::UriTemplate,
124 AuthMiddleware, LoggingMiddleware, Middleware, MiddlewareChain, RetryMiddleware, Transport,
125};
126
127#[cfg(all(feature = "websocket", not(target_arch = "wasm32")))]
128pub use shared::{WebSocketConfig, WebSocketTransport};
129
130#[cfg(all(feature = "websocket-wasm", target_arch = "wasm32"))]
131pub use shared::{WasmWebSocketConfig, WasmWebSocketTransport};
132
133#[cfg(target_arch = "wasm32")]
134pub use shared::{WasmHttpClient, WasmHttpConfig, WasmHttpTransport};
135
136#[cfg(all(feature = "http", not(target_arch = "wasm32")))]
137pub use shared::{HttpConfig, HttpTransport};
138pub use types::{
139 AuthInfo, AuthScheme, CallToolRequest, CallToolResult, ClientCapabilities, ClientNotification,
140 ClientRequest, CompleteRequest, CompleteResult, CompletionArgument, CompletionReference,
141 Content, CreateMessageParams, CreateMessageRequest, CreateMessageResult, GetPromptResult,
142 Implementation, IncludeContext, ListResourcesResult, ListToolsResult, LoggingLevel,
143 MessageContent, ModelPreferences, ProgressNotification, ProgressToken, PromptMessage,
144 ProtocolVersion, ReadResourceResult, RequestId, ResourceInfo, Role, RootsCapabilities,
145 SamplingCapabilities, SamplingMessage, ServerCapabilities, ServerNotification, ServerRequest,
146 TokenUsage, ToolCapabilities, ToolInfo, UIMimeType, UIResource, UIResourceContents,
147};
148
149/// Type alias for [`CallToolResult`] - provides convenient access to tool execution results
150///
151/// This alias was added to resolve the common expectation that `ToolResult` should be
152/// importable directly from the crate root. It provides the same functionality as
153/// [`CallToolResult`] but with a more intuitive name for users implementing MCP tools.
154///
155/// # Examples
156///
157/// Basic usage:
158///
159/// ```rust
160/// use pmcp::{ToolResult, Content};
161///
162/// // Create a successful tool result
163/// let result = ToolResult {
164/// content: vec![Content::Text {
165/// text: "Operation completed successfully".to_string(),
166/// }],
167/// is_error: false,
168/// };
169///
170/// assert_eq!(result.content.len(), 1);
171/// assert!(!result.is_error);
172/// ```
173///
174/// Error handling:
175///
176/// ```rust
177/// use pmcp::{ToolResult, Content};
178///
179/// // Create an error result
180/// let error_result = ToolResult {
181/// content: vec![Content::Text {
182/// text: "Tool execution failed: Invalid input parameter".to_string(),
183/// }],
184/// is_error: true,
185/// };
186///
187/// assert!(error_result.is_error);
188/// ```
189///
190/// Using with different content types:
191///
192/// ```rust
193/// use pmcp::{ToolResult, Content};
194///
195/// // Tool result with resource content
196/// let resource_result = ToolResult {
197/// content: vec![Content::Resource {
198/// uri: "file:///tmp/output.txt".to_string(),
199/// text: Some("File contents here...".to_string()),
200/// mime_type: Some("text/plain".to_string()),
201/// }],
202/// is_error: false,
203/// };
204///
205/// match &resource_result.content[0] {
206/// Content::Resource { uri, mime_type, .. } => {
207/// assert_eq!(uri, "file:///tmp/output.txt");
208/// assert_eq!(mime_type, &Some("text/plain".to_string()));
209/// }
210/// _ => panic!("Expected resource content"),
211/// }
212/// ```
213///
214/// Serialization and JSON compatibility:
215///
216/// ```rust
217/// use pmcp::{ToolResult, Content};
218/// use serde_json;
219///
220/// let result = ToolResult {
221/// content: vec![Content::Text {
222/// text: "Hello, MCP!".to_string(),
223/// }],
224/// is_error: false,
225/// };
226///
227/// // Serialize to JSON
228/// let json_str = serde_json::to_string(&result).unwrap();
229/// println!("Serialized: {}", json_str);
230///
231/// // Deserialize back
232/// let deserialized: ToolResult = serde_json::from_str(&json_str).unwrap();
233/// assert_eq!(result.content.len(), deserialized.content.len());
234/// ```
235pub use types::CallToolResult as ToolResult;
236#[cfg(not(target_arch = "wasm32"))]
237pub use utils::{BatchingConfig, DebouncingConfig, MessageBatcher, MessageDebouncer};
238
239// Re-export async_trait for convenience
240pub use async_trait::async_trait;
241
242/// Protocol version constants
243///
244/// # Examples
245///
246/// ```rust
247/// use pmcp::LATEST_PROTOCOL_VERSION;
248///
249/// // Use in client initialization
250/// let protocol_version = LATEST_PROTOCOL_VERSION;
251/// println!("Using MCP protocol version: {}", protocol_version);
252///
253/// // Check if a version is the latest
254/// assert_eq!(LATEST_PROTOCOL_VERSION, "2025-06-18");
255/// ```
256pub const LATEST_PROTOCOL_VERSION: &str = "2025-06-18";
257
258/// Default protocol version to use for negotiation
259///
260/// # Examples
261///
262/// ```rust
263/// use pmcp::DEFAULT_PROTOCOL_VERSION;
264///
265/// // Use as fallback when negotiating protocol version
266/// let negotiated_version = DEFAULT_PROTOCOL_VERSION;
267/// println!("Negotiating with protocol version: {}", negotiated_version);
268///
269/// // This is typically used internally by the SDK
270/// assert_eq!(DEFAULT_PROTOCOL_VERSION, "2025-03-26");
271/// ```
272pub const DEFAULT_PROTOCOL_VERSION: &str = "2025-03-26";
273
274/// List of all protocol versions supported by this SDK
275///
276/// # Examples
277///
278/// ```rust
279/// use pmcp::SUPPORTED_PROTOCOL_VERSIONS;
280///
281/// // Check if a version is supported
282/// let version_to_check = "2025-03-26";
283/// let is_supported = SUPPORTED_PROTOCOL_VERSIONS.contains(&version_to_check);
284/// assert!(is_supported);
285///
286/// // List all supported versions
287/// println!("Supported MCP protocol versions:");
288/// for version in SUPPORTED_PROTOCOL_VERSIONS {
289/// println!(" - {}", version);
290/// }
291///
292/// // Use in version negotiation
293/// fn negotiate_version(client_version: &str) -> Option<&'static str> {
294/// SUPPORTED_PROTOCOL_VERSIONS.iter()
295/// .find(|&&v| v == client_version)
296/// .copied()
297/// }
298/// ```
299pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &[
300 LATEST_PROTOCOL_VERSION,
301 "2025-03-26",
302 "2024-11-05",
303 "2024-10-07",
304];
305
306/// Default request timeout in milliseconds
307///
308/// # Examples
309///
310/// ```rust
311/// use pmcp::DEFAULT_REQUEST_TIMEOUT_MS;
312/// use std::time::Duration;
313///
314/// // Convert to Duration for use with timeouts
315/// let timeout = Duration::from_millis(DEFAULT_REQUEST_TIMEOUT_MS);
316/// println!("Default timeout: {:?}", timeout);
317///
318/// // Use in custom transport configuration
319/// struct TransportConfig {
320/// timeout_ms: u64,
321/// }
322///
323/// impl Default for TransportConfig {
324/// fn default() -> Self {
325/// Self {
326/// timeout_ms: DEFAULT_REQUEST_TIMEOUT_MS,
327/// }
328/// }
329/// }
330///
331/// // Verify default value
332/// assert_eq!(DEFAULT_REQUEST_TIMEOUT_MS, 60_000); // 60 seconds
333/// ```
334pub const DEFAULT_REQUEST_TIMEOUT_MS: u64 = 60_000;
335
336/// Server-side logging function (placeholder for examples).
337///
338/// In a real server context, this would send a `LogMessage` notification.
339/// For examples, this is a no-op.
340#[allow(clippy::unused_async)]
341pub async fn log(
342 _level: types::protocol::LogLevel,
343 _message: &str,
344 _data: Option<serde_json::Value>,
345) {
346 // In a real implementation, this would:
347 // 1. Get the current server context
348 // 2. Send a LogMessage notification through the transport
349 // For now, this is a placeholder for the examples
350}