Skip to main content

tower_mcp/
lib.rs

1//! # tower-mcp
2//!
3//! Tower-native Model Context Protocol (MCP) implementation for Rust.
4//!
5//! This crate provides a composable, middleware-friendly approach to building
6//! MCP servers and clients using the [Tower](https://docs.rs/tower) service abstraction.
7//!
8//! ## Philosophy
9//!
10//! Unlike framework-style MCP implementations, tower-mcp treats MCP as just another
11//! protocol that can be served through Tower's `Service` trait. This means:
12//!
13//! - Standard tower middleware (tracing, metrics, rate limiting, auth) just works
14//! - Same service can be exposed over multiple transports (stdio, HTTP, WebSocket)
15//! - Easy integration with existing tower-based applications (axum, tonic, etc.)
16//!
17//! ## Familiar to axum Users
18//!
19//! If you've used [axum](https://docs.rs/axum), tower-mcp's API will feel familiar.
20//! We've adopted axum's patterns for a consistent Rust web ecosystem experience:
21//!
22//! - **Extractor pattern**: Tool handlers use extractors like [`extract::State<T>`],
23//!   [`extract::Json<T>`], and [`extract::Context`] - just like axum's request extractors
24//! - **Router composition**: [`McpRouter::merge()`] and [`McpRouter::nest()`] work like
25//!   axum's router methods for combining routers
26//! - **Per-route middleware**: Apply Tower layers to individual tools, resources, or
27//!   prompts via `.layer()` on builders
28//! - **Builder pattern**: Fluent builders for tools, resources, and prompts
29//!
30//! ```rust
31//! use std::sync::Arc;
32//! use tower_mcp::{ToolBuilder, CallToolResult};
33//! use tower_mcp::extract::{State, Json, Context};
34//! use schemars::JsonSchema;
35//! use serde::Deserialize;
36//!
37//! #[derive(Clone)]
38//! struct AppState { db_url: String }
39//!
40//! #[derive(Deserialize, JsonSchema)]
41//! struct SearchInput { query: String }
42//!
43//! // Looks just like an axum handler!
44//! let tool = ToolBuilder::new("search")
45//!     .title("Search Database")
46//!     .description("Search the database")
47//!     .extractor_handler(
48//!         Arc::new(AppState { db_url: "postgres://...".into() }),
49//!         |State(app): State<Arc<AppState>>,
50//!          ctx: Context,
51//!          Json(input): Json<SearchInput>| async move {
52//!             ctx.report_progress(0.5, Some(1.0), Some("Searching...")).await;
53//!             Ok(CallToolResult::text(format!("Found results for: {}", input.query)))
54//!         },
55//!     )
56//!     .build();
57//! ```
58//!
59//! ## Quick Start: Server
60//!
61//! Build an MCP server with tools, resources, and prompts:
62//!
63//! ```rust,no_run
64//! use tower_mcp::{BoxError, McpRouter, ToolBuilder, CallToolResult, StdioTransport};
65//! use schemars::JsonSchema;
66//! use serde::Deserialize;
67//!
68//! #[derive(Debug, Deserialize, JsonSchema)]
69//! struct GreetInput {
70//!     name: String,
71//! }
72//!
73//! #[tokio::main]
74//! async fn main() -> Result<(), BoxError> {
75//!     // Define a tool
76//!     let greet = ToolBuilder::new("greet")
77//!         .title("Greet")
78//!         .description("Greet someone by name")
79//!         .handler(|input: GreetInput| async move {
80//!             Ok(CallToolResult::text(format!("Hello, {}!", input.name)))
81//!         })
82//!         .build();
83//!
84//!     // Create router and run over stdio
85//!     let router = McpRouter::new()
86//!         .server_info("my-server", "1.0.0")
87//!         .tool(greet);
88//!
89//!     StdioTransport::new(router).run().await?;
90//!     Ok(())
91//! }
92//! ```
93//!
94//! ## Quick Start: Client
95//!
96//! Connect to an MCP server and call tools:
97//!
98//! ```rust,no_run
99//! use tower_mcp::BoxError;
100//! use tower_mcp::client::{McpClient, StdioClientTransport};
101//!
102//! #[tokio::main]
103//! async fn main() -> Result<(), BoxError> {
104//!     // Connect to server
105//!     let transport = StdioClientTransport::spawn("my-mcp-server", &[]).await?;
106//!     let client = McpClient::connect(transport).await?;
107//!
108//!     // Initialize and list tools
109//!     client.initialize("my-client", "1.0.0").await?;
110//!     let tools = client.list_tools().await?;
111//!
112//!     // Call a tool
113//!     let result = client.call_tool("greet", serde_json::json!({"name": "World"})).await?;
114//!     println!("{:?}", result);
115//!
116//!     Ok(())
117//! }
118//! ```
119//!
120//! ## Key Types
121//!
122//! ### Server
123//! - [`McpRouter`] - Routes MCP requests to tools, resources, and prompts
124//! - [`ToolBuilder`] - Builder for defining tools with type-safe handlers
125//! - [`ResourceBuilder`] - Builder for defining resources
126//! - [`PromptBuilder`] - Builder for defining prompts
127//! - [`StdioTransport`] - Stdio transport for CLI servers
128//!
129//! ### Client
130//! - [`McpClient`] - Client for connecting to MCP servers
131//! - [`StdioClientTransport`] - Spawn and connect to server subprocesses
132//!
133//! ### Protocol
134//! - [`CallToolResult`] - Tool execution result with content
135//! - [`ReadResourceResult`] - Resource read result
136//! - [`GetPromptResult`] - Prompt expansion result
137//! - [`Content`] - Text, image, audio, or resource content
138//!
139//! ### Stateless (2026-07-28, requires `stateless` feature)
140//! - [`stateless::StatelessRequestMeta`] - Per-request `_meta` carrying protocol version,
141//!   client identity, and client capabilities for sessionless 2026-07-28 requests
142//!
143//! ## Feature Flags
144//!
145//! - `full` - Enable all optional features
146//! - `http` - HTTP/SSE transport for web servers (adds axum, hyper)
147//! - `websocket` - WebSocket transport for bidirectional communication
148//! - `childproc` - Child process transport for subprocess management
149//! - `oauth` - OAuth 2.1 resource server support (JWT validation, metadata endpoint; requires `http`)
150//! - `jwks` - JWKS endpoint fetching for remote key sets (requires `oauth`)
151//! - `testing` - Test utilities (`TestClient`) for ergonomic MCP server testing
152//! - `dynamic-tools` - Runtime registration/deregistration of tools, prompts, and resources via
153//!   [`DynamicToolRegistry`], [`DynamicPromptRegistry`], [`DynamicResourceRegistry`],
154//!   [`DynamicResourceTemplateRegistry`]
155//! - `proxy` - Multi-server aggregation proxy ([`McpProxy`](proxy::McpProxy))
156//! - `http-client` - HTTP client transport for connecting to remote MCP servers
157//! - `oauth-client` - OAuth 2.0 client-side token acquisition via client credentials grant (requires `http-client`)
158//! - `macros` - Optional proc macros (`#[tool_fn]`, `#[prompt_fn]`, `#[resource_fn]`, `#[resource_template_fn]`)
159//! - `stateless` - Experimental 2026-07-28 stateless protocol mode (SEP-2575 + SEP-2567). Enables
160//!   version-gated sessionless dispatch, `server/discover` RPC, per-request `_meta` via
161//!   [`stateless::StatelessRequestMeta`], and `messages/listen` SSE endpoint. Requires `http`.
162//!
163//! ## Middleware Placement Guide
164//!
165//! tower-mcp supports Tower middleware at multiple levels. Choose based on scope:
166//!
167//! | Level | Method | Scope | Use Cases |
168//! |-------|--------|-------|-----------|
169//! | **Transport** | `StdioTransport::layer()`, `HttpTransport::layer()` | All MCP requests | Global timeout, rate limit, metrics |
170//! | **axum** | `.into_router().layer()` | HTTP layer only | CORS, compression, request logging |
171//! | **Per-tool** | `ToolBuilder::...layer()` | Single tool | Tool-specific timeout, concurrency |
172//! | **Per-resource** | `ResourceBuilder::...layer()` | Single resource | Caching, read timeout |
173//! | **Per-prompt** | `PromptBuilder::...layer()` | Single prompt | Generation timeout |
174//!
175//! ### Decision Tree
176//!
177//! ```text
178//! Where should my middleware go?
179//! │
180//! ├─ Affects ALL MCP requests?
181//! │  └─ Yes → Transport: StdioTransport::layer(), HttpTransport::layer(), or WebSocketTransport::layer()
182//! │
183//! ├─ HTTP-specific (CORS, compression, headers)?
184//! │  └─ Yes → axum: transport.into_router().layer(...)
185//! │
186//! ├─ Only one specific tool?
187//! │  └─ Yes → Per-tool: ToolBuilder::...handler(...).layer(...)
188//! │
189//! ├─ Only one specific resource?
190//! │  └─ Yes → Per-resource: ResourceBuilder::...handler(...).layer(...)
191//! │
192//! └─ Only one specific prompt?
193//!    └─ Yes → Per-prompt: PromptBuilder::...handler(...).layer(...)
194//! ```
195//!
196//! ### Example: Layered Timeouts
197//!
198//! ```rust,ignore
199//! use std::time::Duration;
200//! use tower::timeout::TimeoutLayer;
201//! use tower_mcp::{McpRouter, ToolBuilder, CallToolResult, HttpTransport};
202//! use schemars::JsonSchema;
203//! use serde::Deserialize;
204//!
205//! #[derive(Debug, Deserialize, JsonSchema)]
206//! struct SearchInput { query: String }
207//!
208//! // This tool gets a longer timeout than the global default
209//! let slow_search = ToolBuilder::new("slow_search")
210//!     .description("Thorough search (may take a while)")
211//!     .handler(|input: SearchInput| async move {
212//!         // ... slow operation ...
213//!         Ok(CallToolResult::text("results"))
214//!     })
215//!     .layer(TimeoutLayer::new(Duration::from_secs(60)))  // 60s for this tool
216//!     .build();
217//!
218//! let router = McpRouter::new()
219//!     .server_info("example", "1.0.0")
220//!     .tool(slow_search);
221//!
222//! // Global 30s timeout for all OTHER requests
223//! let transport = HttpTransport::new(router)
224//!     .layer(TimeoutLayer::new(Duration::from_secs(30)));
225//! ```
226//!
227//! In this example:
228//! - `slow_search` tool has a 60-second timeout (per-tool layer)
229//! - All other MCP requests have a 30-second timeout (transport layer)
230//! - The per-tool layer is **inner** to the transport layer
231//!
232//! ### Layer Ordering
233//!
234//! Layers wrap from outside in. The first layer added is the outermost:
235//!
236//! ```text
237//! Request → [Transport Layer] → [Per-tool Layer] → Handler → Response
238//! ```
239//!
240//! For per-tool/resource/prompt, chained `.layer()` calls also wrap outside-in:
241//!
242//! ```rust,ignore
243//! ToolBuilder::new("api")
244//!     .handler(...)
245//!     .layer(TimeoutLayer::new(...))      // Outer: timeout checked first
246//!     .layer(ConcurrencyLimitLayer::new(5)) // Inner: concurrency after timeout
247//!     .build()
248//! ```
249//!
250//! ### Full Example
251//!
252//! See [`examples/tool_middleware.rs`](https://github.com/joshrotenberg/tower-mcp/blob/main/examples/tool_middleware.rs)
253//! for a complete example demonstrating:
254//! - Different timeouts per tool
255//! - Concurrency limiting for expensive operations
256//! - Multiple layers combined on a single tool
257//!
258//! ## Advanced Features
259//!
260//! ### Sampling (LLM Requests)
261//!
262//! Tools can request LLM completions from the client via [`RequestContext::sample()`].
263//! This enables AI-assisted tools like "suggest a query" or "analyze results":
264//!
265//! ```rust,ignore
266//! use tower_mcp::{ToolBuilder, CallToolResult, CreateMessageParams, SamplingMessage};
267//! use tower_mcp::extract::Context;
268//!
269//! let tool = ToolBuilder::new("suggest")
270//!     .description("Get AI suggestions")
271//!     .extractor_handler(|ctx: Context| async move {
272//!         if !ctx.can_sample() {
273//!             return Ok(CallToolResult::error("Sampling not available"));
274//!         }
275//!
276//!         let params = CreateMessageParams::new()
277//!             .message(SamplingMessage::user("Suggest 3 search queries for: rust async"))
278//!             .max_tokens(200);
279//!
280//!         let result = ctx.sample(params).await?;
281//!         let text = result.first_text().unwrap_or("No response");
282//!         Ok(CallToolResult::text(text))
283//!     })
284//!     .build();
285//! ```
286//!
287//! ### Elicitation (User Input)
288//!
289//! Tools can request user input via forms using [`RequestContext::elicit_form()`]
290//! or the convenience method [`RequestContext::confirm()`]:
291//!
292//! ```rust,ignore
293//! use tower_mcp::{ToolBuilder, CallToolResult};
294//! use tower_mcp::extract::Context;
295//!
296//! // Simple confirmation dialog
297//! let delete_tool = ToolBuilder::new("delete")
298//!     .description("Delete a file")
299//!     .extractor_handler(|ctx: Context| async move {
300//!         if !ctx.confirm("Are you sure you want to delete this file?").await? {
301//!             return Ok(CallToolResult::text("Cancelled"));
302//!         }
303//!         // ... perform deletion ...
304//!         Ok(CallToolResult::text("Deleted"))
305//!     })
306//!     .build();
307//! ```
308//!
309//! For complex forms, use [`ElicitFormSchema`] to define multiple fields.
310//!
311//! ### Progress Notifications
312//!
313//! Long-running tools can report progress via [`RequestContext::report_progress()`]:
314//!
315//! ```rust,ignore
316//! use tower_mcp::{ToolBuilder, CallToolResult};
317//! use tower_mcp::extract::Context;
318//!
319//! let process_tool = ToolBuilder::new("process")
320//!     .description("Process items")
321//!     .extractor_handler(|ctx: Context| async move {
322//!         let items = vec!["a", "b", "c", "d", "e"];
323//!         let total = items.len() as f64;
324//!
325//!         for (i, item) in items.iter().enumerate() {
326//!             ctx.report_progress(i as f64, Some(total), Some(&format!("Processing {}", item))).await;
327//!             // ... process item ...
328//!         }
329//!
330//!         Ok(CallToolResult::text("Done"))
331//!     })
332//!     .build();
333//! ```
334//!
335//! ### Stateless Mode (2026-07-28, requires `stateless` + `http` features)
336//!
337//! The `stateless` feature enables experimental support for the 2026-07-28 MCP protocol
338//! (SEP-2575 final + SEP-2567 accepted). In this mode the initialize/initialized handshake
339//! is replaced by two new RPCs:
340//!
341//! - **`server/discover`** -- stateless capability discovery. Clients that send requests with
342//!   `MCP-Protocol-Version: 2026-07-28` (SEP-2243 header) can call `server/discover` instead
343//!   of `initialize` to learn what the server supports without establishing a session.
344//! - **`messages/listen`** -- client-initiated SSE subscription. A GET to `/mcp` with
345//!   `MCP-Protocol-Version: 2026-07-28` opens a server-push stream that is not tied to any
346//!   session, allowing stateless clients to receive notifications.
347//!
348//! Per-request client identity and capabilities ride in each request's `_meta` object via
349//! [`stateless::StatelessRequestMeta`] rather than being negotiated once at session open.
350//! The `MCP-Protocol-Version` header value is the version gate: requests carrying `2026-07-28`
351//! or later route through the stateless path; older requests continue through the
352//! session-based path unchanged.
353//!
354//! ```rust,ignore
355//! use tower_mcp::{McpRouter, HttpTransport};
356//! use tower_mcp::stateless::StatelessConfig;
357//!
358//! let router = McpRouter::new().server_info("my-server", "1.0.0");
359//!
360//! // Enable stateless mode alongside the session-based path.
361//! let transport = HttpTransport::new(router)
362//!     .stateless(StatelessConfig::new());
363//! ```
364//!
365//! The 2026-07-28 protocol is experimental. The `UPCOMING_PROTOCOL_VERSION` constant in
366//! `tower_mcp::protocol` tracks the target version string and will not appear in
367//! `SUPPORTED_PROTOCOL_VERSIONS` until the spec is stable.
368//!
369//! ### Router Composition
370//!
371//! Combine multiple routers using [`McpRouter::merge()`] or [`McpRouter::nest()`]:
372//!
373//! ```rust,ignore
374//! use tower_mcp::McpRouter;
375//!
376//! // Create domain-specific routers
377//! let db_router = McpRouter::new()
378//!     .tool(query_tool)
379//!     .tool(insert_tool);
380//!
381//! let api_router = McpRouter::new()
382//!     .tool(fetch_tool);
383//!
384//! // Nest with prefixes: tools become "db.query", "db.insert", "api.fetch"
385//! let combined = McpRouter::new()
386//!     .server_info("combined", "1.0")
387//!     .nest("db", db_router)
388//!     .nest("api", api_router);
389//!
390//! // Or merge without prefixes
391//! let merged = McpRouter::new()
392//!     .merge(db_router)
393//!     .merge(api_router);
394//! ```
395//!
396//! ### Multi-Server Proxy
397//!
398//! Aggregate multiple backend MCP servers behind a single endpoint using
399//! [`McpProxy`](proxy::McpProxy) (requires the `proxy` feature):
400//!
401//! ```rust,ignore
402//! use tower_mcp::proxy::McpProxy;
403//! use tower_mcp::client::StdioClientTransport;
404//!
405//! let proxy = McpProxy::builder("my-proxy", "1.0.0")
406//!     .backend("db", StdioClientTransport::spawn("db-server", &[]).await?)
407//!     .await
408//!     .backend("fs", StdioClientTransport::spawn("fs-server", &[]).await?)
409//!     .await
410//!     .build()
411//!     .await?;
412//!
413//! // Tools become `db_query`, `fs_read`, etc.
414//! // Serve over any transport -- stdio, HTTP, WebSocket.
415//! GenericStdioTransport::new(proxy).run().await?;
416//! ```
417//!
418//! The proxy supports per-backend Tower middleware, notification forwarding,
419//! health checks, and request coalescing. See the [`proxy`] module for details.
420//!
421//! ## Production Deployment
422//!
423//! See the [`deployment`] module for load balancer patterns, session
424//! affinity, horizontal scaling with the [`session_store`] and
425//! [`event_store`] traits, reverse proxy configuration (nginx, Caddy,
426//! Traefik), observability, and sidecar deployments.
427//!
428//! ## MCP Specification
429//!
430//! This crate implements the MCP specification (2025-11-25):
431//! <https://modelcontextprotocol.io/specification/2025-11-25>
432//!
433//! The `stateless` feature additionally tracks the upcoming 2026-07-28 protocol defined by:
434//! - [SEP-2567](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2567) (accepted) --
435//!   `messages/listen` SSE endpoint
436//! - [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2575) (final) --
437//!   stateless session model, `server/discover`, per-request `_meta`
438//! - [SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2243) (final) --
439//!   strict HTTP headers (`Mcp-Method`, `Mcp-Name`, `MCP-Protocol-Version`)
440
441pub mod async_task;
442pub mod auth;
443pub mod client;
444pub mod context;
445#[cfg(any(feature = "http", feature = "websocket"))]
446pub mod deployment;
447pub mod error;
448#[cfg(any(feature = "http", feature = "websocket"))]
449pub mod event_store;
450pub mod extract;
451pub mod filter;
452pub mod jsonrpc;
453pub mod middleware;
454#[cfg(feature = "oauth")]
455pub mod oauth;
456pub mod prompt;
457pub mod protocol;
458#[cfg(feature = "proxy")]
459pub mod proxy;
460#[cfg(feature = "dynamic-tools")]
461pub mod registry;
462pub mod resource;
463pub mod router;
464pub mod session;
465#[cfg(any(feature = "http", feature = "websocket"))]
466pub mod session_store;
467#[cfg(feature = "stateless")]
468pub mod stateless;
469#[cfg(feature = "testing")]
470pub mod testing;
471pub mod tool;
472pub mod tracing_layer;
473pub mod transport;
474
475// Re-export proc macros when the `macros` feature is enabled
476#[cfg(feature = "macros")]
477pub use tower_mcp_macros::prompt_fn;
478#[cfg(feature = "macros")]
479pub use tower_mcp_macros::resource_fn;
480#[cfg(feature = "macros")]
481pub use tower_mcp_macros::resource_template_fn;
482#[cfg(feature = "macros")]
483pub use tower_mcp_macros::tool_fn;
484
485// Re-exports
486pub use async_task::{Task, TaskStore};
487pub use client::{
488    ChannelTransport, ClientHandler, ClientTransport, McpClient, McpClientBuilder,
489    NotificationHandler, StdioClientTransport,
490};
491#[cfg(feature = "http-client")]
492pub use client::{HttpClientConfig, HttpClientTransport};
493#[cfg(feature = "oauth-client")]
494pub use client::{OAuthClientCredentials, OAuthClientError, TokenProvider};
495pub use context::{
496    ChannelClientRequester, ClientRequester, ClientRequesterHandle, Extensions,
497    NotificationReceiver, NotificationSender, OutgoingRequest, OutgoingRequestReceiver,
498    OutgoingRequestSender, RequestContext, RequestContextBuilder, ServerNotification,
499    outgoing_request_channel,
500};
501pub use error::{BoxError, Error, Result, ResultExt, ToolError};
502pub use filter::{
503    CapabilityFilter, DenialBehavior, Filterable, PromptFilter, ResourceFilter, ToolFilter,
504};
505pub use jsonrpc::{JsonRpcLayer, JsonRpcService};
506pub use middleware::{
507    AuditLayer, AuditService, McpTracingLayer, McpTracingService, ToolCallLoggingLayer,
508    ToolCallLoggingService,
509};
510pub use prompt::{BoxPromptService, Prompt, PromptBuilder, PromptHandler, PromptRequest};
511#[allow(deprecated)]
512pub use protocol::{
513    BooleanSchema, CallToolParams, CallToolResult, CancelTaskParams, CancelledParams,
514    ClientCapabilities, ClientTasksCancelCapability, ClientTasksCapability,
515    ClientTasksElicitationCapability, ClientTasksElicitationCreateCapability,
516    ClientTasksListCapability, ClientTasksRequestsCapability, ClientTasksSamplingCapability,
517    ClientTasksSamplingCreateMessageCapability, CompleteParams, CompleteResult, Completion,
518    CompletionArgument, CompletionContext, CompletionReference, CompletionsCapability, Content,
519    ContentAnnotations, ContentRole, CreateMessageParams, CreateMessageResult, CreateTaskResult,
520    ElicitAction, ElicitFieldValue, ElicitFormParams, ElicitFormSchema, ElicitMode,
521    ElicitRequestParams, ElicitResult, ElicitUrlParams, ElicitationCapability,
522    ElicitationCompleteParams, ElicitationFormCapability, ElicitationUrlCapability, EmptyResult,
523    GetPromptParams, GetPromptResult, GetPromptResultBuilder, GetTaskInfoParams,
524    GetTaskResultParams, IconTheme, Implementation, IncludeContext, InitializeParams,
525    InitializeResult, IntegerSchema, JsonRpcErrorResponse, JsonRpcMessage, JsonRpcNotification,
526    JsonRpcRequest, JsonRpcResponse, JsonRpcResponseMessage, JsonRpcResultResponse,
527    ListPromptsParams, ListPromptsResult, ListResourceTemplatesParams, ListResourceTemplatesResult,
528    ListResourcesParams, ListResourcesResult, ListRootsParams, ListRootsResult, ListTasksParams,
529    ListTasksResult, ListToolsParams, ListToolsResult, LogLevel, LoggingCapability,
530    LoggingMessageParams, McpNotification, McpRequest, McpResponse, ModelHint, ModelPreferences,
531    MultiSelectEnumItems, MultiSelectEnumSchema, NumberSchema, PrimitiveSchemaDefinition,
532    ProgressParams, ProgressToken, PromptArgument, PromptDefinition, PromptMessage,
533    PromptReference, PromptRole, PromptsCapability, ReadResourceParams, ReadResourceResult,
534    RequestId, RequestMeta, ResourceContent, ResourceDefinition, ResourceReference,
535    ResourceTemplateDefinition, ResourcesCapability, Root, RootsCapability, SamplingCapability,
536    SamplingContent, SamplingContentOrArray, SamplingContextCapability, SamplingMessage,
537    SamplingTool, SamplingToolsCapability, ServerCapabilities, SetLogLevelParams,
538    SingleSelectEnumSchema, StringSchema, SubscribeResourceParams, TaskInfo, TaskObject,
539    TaskRequestParams, TaskStatus, TaskStatusChangedParams, TaskStatusParams, TaskSupportMode,
540    TasksCancelCapability, TasksCapability, TasksListCapability, TasksRequestsCapability,
541    TasksToolsCallCapability, TasksToolsRequestsCapability, ToolAnnotations, ToolChoice,
542    ToolDefinition, ToolExecution, ToolIcon, ToolsCapability, UnsubscribeResourceParams,
543    UpdateTaskParams,
544};
545pub use protocol::{RESULT_TYPE_TASK, TASKS_EXTENSION_ID};
546#[cfg(feature = "dynamic-tools")]
547pub use registry::{
548    DynamicPromptRegistry, DynamicResourceRegistry, DynamicResourceTemplateRegistry,
549    DynamicToolRegistry,
550};
551pub use resource::{
552    BoxResourceService, Resource, ResourceBuilder, ResourceHandler, ResourceRequest,
553    ResourceTemplate, ResourceTemplateBuilder, ResourceTemplateHandler,
554};
555pub use router::{McpRouter, RouterRequest, RouterResponse, ToolAnnotationsMap};
556pub use session::{SessionPhase, SessionState};
557pub use tool::{BoxToolService, GuardLayer, NoParams, Tool, ToolBuilder, ToolHandler, ToolRequest};
558pub use transport::{
559    BidirectionalStdioTransport, CatchError, GenericStdioTransport, StdioTransport,
560    SyncStdioTransport,
561};
562
563#[cfg(feature = "http")]
564pub use transport::{HttpTransport, SessionHandle, SessionInfo};
565
566#[cfg(feature = "websocket")]
567pub use transport::WebSocketTransport;
568
569#[cfg(any(feature = "http", feature = "websocket", feature = "unix"))]
570pub use transport::McpBoxService;
571
572#[cfg(all(unix, feature = "unix"))]
573pub use transport::UnixSocketTransport;
574
575#[cfg(feature = "childproc")]
576pub use transport::{ChildProcessConnection, ChildProcessTransport};
577
578#[cfg(feature = "oauth")]
579pub use oauth::{ScopeEnforcementLayer, ScopeEnforcementService};
580
581#[cfg(feature = "jwks")]
582pub use oauth::{JwksError, JwksValidator, JwksValidatorBuilder};
583
584#[cfg(feature = "testing")]
585pub use testing::TestClient;