Skip to main content

pmcp/server/
mod.rs

1//! MCP server implementation.
2
3#[cfg(not(target_arch = "wasm32"))]
4use crate::error::{Error, Result};
5#[cfg(not(target_arch = "wasm32"))]
6use crate::shared::{Protocol, ProtocolOptions, TransportMessage};
7#[cfg(not(target_arch = "wasm32"))]
8use crate::types::{
9    CallToolRequest, CallToolResult, ClientCapabilities, ClientRequest, GetPromptRequest,
10    Implementation, InitializeResult, JSONRPCResponse, ListPromptsRequest, ListPromptsResult,
11    ListResourceTemplatesRequest, ListResourceTemplatesResult, ListResourcesRequest,
12    ListResourcesResult, ListToolsRequest, ListToolsResult, Notification, ProtocolVersion,
13    ReadResourceRequest, Request, RequestId, ServerCapabilities, ServerNotification, ToolInfo,
14};
15#[cfg(not(target_arch = "wasm32"))]
16use async_trait::async_trait;
17#[cfg(not(target_arch = "wasm32"))]
18use serde_json::Value;
19#[cfg(not(target_arch = "wasm32"))]
20use std::collections::HashMap;
21#[cfg(not(target_arch = "wasm32"))]
22use std::sync::Arc;
23
24#[cfg(not(target_arch = "wasm32"))]
25use crate::runtime::RwLock;
26#[cfg(not(target_arch = "wasm32"))]
27use tokio::sync::mpsc;
28
29// Core modules (currently native-only due to dependencies)
30#[cfg(not(target_arch = "wasm32"))]
31pub mod adapters;
32#[cfg(not(target_arch = "wasm32"))]
33pub mod builder;
34#[cfg(not(target_arch = "wasm32"))]
35pub mod core;
36pub mod limits;
37
38// Native-only modules (require tokio, threading, etc.)
39#[cfg(not(target_arch = "wasm32"))]
40pub mod auth;
41#[cfg(not(target_arch = "wasm32"))]
42pub mod batch;
43/// Builder-scoped middleware executor for workflow registration.
44#[cfg(not(target_arch = "wasm32"))]
45pub mod builder_middleware_executor;
46#[cfg(not(target_arch = "wasm32"))]
47pub mod cancellation;
48/// Dynamic resource provider system for pattern-based resource routing.
49#[cfg(not(target_arch = "wasm32"))]
50pub mod dynamic_resources;
51#[cfg(not(target_arch = "wasm32"))]
52pub mod http_middleware;
53/// Middleware executor abstraction for consistent tool execution.
54#[cfg(not(target_arch = "wasm32"))]
55pub mod middleware_executor;
56/// Concrete `PeerHandle` implementation delegating to the
57/// `ServerRequestDispatcher`.
58#[cfg(not(target_arch = "wasm32"))]
59pub(crate) mod peer_impl;
60#[cfg(not(target_arch = "wasm32"))]
61pub mod preset;
62/// Progress reporting support for long-running operations.
63#[cfg(not(target_arch = "wasm32"))]
64pub mod progress;
65/// Outbound server-to-client request dispatcher with response correlation.
66#[cfg(not(target_arch = "wasm32"))]
67pub(crate) mod server_request_dispatcher;
68/// Simple prompt implementations with metadata support.
69#[cfg(not(target_arch = "wasm32"))]
70pub mod simple_prompt;
71/// Simple resource implementations with builder pattern support.
72#[cfg(not(target_arch = "wasm32"))]
73pub mod simple_resources;
74/// Simple tool implementations with schema support.
75#[cfg(not(target_arch = "wasm32"))]
76pub mod simple_tool;
77/// SDK-level task store trait and in-memory implementation.
78#[cfg(not(target_arch = "wasm32"))]
79pub mod task_store;
80/// Task routing trait for MCP Tasks integration.
81#[cfg(not(target_arch = "wasm32"))]
82pub mod tasks;
83/// Tool middleware for cross-cutting concerns in tool execution.
84#[cfg(not(target_arch = "wasm32"))]
85pub mod tool_middleware;
86
87/// Observability infrastructure for tracing, metrics, and logging.
88#[cfg(not(target_arch = "wasm32"))]
89pub mod observability;
90/// Workflow-based prompt system with type-safe handles and ergonomic builders.
91#[cfg(not(target_arch = "wasm32"))]
92pub mod workflow;
93
94/// State extractor for `#[mcp_tool]` shared state injection.
95#[cfg(not(target_arch = "wasm32"))]
96pub mod state;
97
98/// Typed tool implementations with automatic schema generation.
99#[cfg(not(target_arch = "wasm32"))]
100pub mod typed_tool;
101
102/// Typed prompt implementations with automatic argument schema generation.
103#[cfg(not(target_arch = "wasm32"))]
104pub mod typed_prompt;
105
106/// UI resource implementations for MCP Apps Extension (SEP-1865).
107#[cfg(not(target_arch = "wasm32"))]
108pub mod ui;
109
110/// MCP Apps Extension - Interactive UI support for multiple MCP hosts.
111///
112/// Provides adapters for `ChatGPT` Apps, MCP Apps (SEP-1865), and MCP-UI.
113#[cfg(all(not(target_arch = "wasm32"), feature = "mcp-apps"))]
114pub mod mcp_apps;
115
116/// Agent Skills (SEP-2640) — [`skills::Skill`] / [`skills::SkillReference`] /
117/// [`skills::Skills`] plus a dual-surface `PromptHandler` fallback.
118///
119/// Gated on `feature = "skills"` AND `not(target_arch = "wasm32")`: the
120/// module's contents consume [`ResourceHandler`] and [`PromptHandler`],
121/// which are themselves non-wasm-only.
122#[cfg(all(feature = "skills", not(target_arch = "wasm32")))]
123pub mod skills;
124
125/// Re-export the public Skills DX types so callers can `use
126/// pmcp::server::{Skill, SkillReference, Skills}` without descending
127/// into the `skills::` submodule path. The canonical path remains
128/// `pmcp::server::skills::*`.
129#[cfg(all(feature = "skills", not(target_arch = "wasm32")))]
130pub use skills::{Skill, SkillReference, Skills};
131
132/// Validation helpers for typed tools.
133#[cfg(not(target_arch = "wasm32"))]
134pub mod validation;
135
136/// Schema utilities for normalizing and inlining JSON schemas.
137#[cfg(feature = "schema-generation")]
138pub mod schema_utils;
139
140/// Standard error codes for validation with client elicitation support.
141#[cfg(not(target_arch = "wasm32"))]
142pub mod error_codes;
143
144/// Cross-platform path validation with security constraints.
145#[cfg(not(target_arch = "wasm32"))]
146pub mod path_validation;
147
148/// WASM-compatible typed tools with automatic schema generation.
149#[cfg(target_arch = "wasm32")]
150pub mod wasm_typed_tool;
151
152// For WASM, provide a simple stub for RequestHandlerExtra
153#[cfg(target_arch = "wasm32")]
154pub mod cancellation {
155    /// Stub for WASM - no cancellation support
156    #[derive(Debug, Clone, Default)]
157    pub struct RequestHandlerExtra;
158}
159/// Axum Router convenience function for secure MCP server hosting.
160#[cfg(feature = "streamable-http")]
161pub mod axum_router;
162#[cfg(not(target_arch = "wasm32"))]
163pub mod dynamic;
164#[cfg(not(target_arch = "wasm32"))]
165pub mod elicitation;
166#[cfg(not(target_arch = "wasm32"))]
167pub mod notification_debouncer;
168#[cfg(all(not(target_arch = "wasm32"), feature = "resource-watcher"))]
169pub mod resource_watcher;
170#[cfg(not(target_arch = "wasm32"))]
171pub mod roots;
172#[cfg(all(not(target_arch = "wasm32"), feature = "streamable-http"))]
173pub mod streamable_http_server;
174#[cfg(not(target_arch = "wasm32"))]
175pub mod subscriptions;
176/// Tower middleware layers for MCP HTTP security (DNS rebinding, security headers).
177#[cfg(feature = "streamable-http")]
178pub mod tower_layers;
179#[cfg(not(target_arch = "wasm32"))]
180pub mod transport;
181
182// WASM-specific modules and types
183#[cfg(target_arch = "wasm32")]
184pub mod wasi_adapter;
185#[cfg(target_arch = "wasm32")]
186pub mod wasm_core;
187#[cfg(target_arch = "wasm32")]
188pub mod wasm_server;
189#[cfg(all(test, target_arch = "wasm32"))]
190mod wasm_server_tests;
191
192// WASM-compatible protocol handler trait
193#[cfg(target_arch = "wasm32")]
194pub use wasi_protocol::ProtocolHandler;
195
196#[cfg(target_arch = "wasm32")]
197mod wasi_protocol {
198    use crate::error::Result;
199    use crate::types::{JSONRPCResponse, Notification, Request, RequestId};
200    use async_trait::async_trait;
201
202    /// Protocol-agnostic request handler trait for WASM.
203    ///
204    /// This is a simplified version of the ProtocolHandler trait that
205    /// doesn't depend on native-only types like handlers and managers.
206    #[async_trait(?Send)]
207    pub trait ProtocolHandler {
208        /// Handle a single request and return a response.
209        async fn handle_request(&self, id: RequestId, request: Request) -> JSONRPCResponse;
210
211        /// Handle a notification (no response expected).
212        async fn handle_notification(&self, notification: Notification) -> Result<()>;
213    }
214}
215
216#[cfg(test)]
217mod adapter_tests;
218#[cfg(test)]
219mod core_tests;
220
221/// Handler for tool execution.
222#[cfg(not(target_arch = "wasm32"))]
223#[async_trait]
224pub trait ToolHandler: Send + Sync {
225    /// Handle a tool call with the given arguments.
226    async fn handle(&self, args: Value, extra: cancellation::RequestHandlerExtra) -> Result<Value>;
227
228    /// Get tool metadata including description and schema.
229    /// Returns None to use default empty metadata.
230    fn metadata(&self) -> Option<crate::types::ToolInfo> {
231        None
232    }
233}
234
235/// Handler for prompt generation.
236#[cfg(not(target_arch = "wasm32"))]
237#[async_trait]
238pub trait PromptHandler: Send + Sync {
239    /// Generate a prompt with the given arguments.
240    async fn handle(
241        &self,
242        args: HashMap<String, String>,
243        extra: cancellation::RequestHandlerExtra,
244    ) -> Result<crate::types::GetPromptResult>;
245
246    /// Get prompt metadata including description and arguments schema.
247    /// Returns None to use default empty metadata.
248    fn metadata(&self) -> Option<crate::types::PromptInfo> {
249        None
250    }
251}
252
253/// Handler for resource access.
254#[cfg(not(target_arch = "wasm32"))]
255#[async_trait]
256pub trait ResourceHandler: Send + Sync {
257    /// Read a resource at the given URI.
258    async fn read(
259        &self,
260        uri: &str,
261        extra: cancellation::RequestHandlerExtra,
262    ) -> Result<crate::types::ReadResourceResult>;
263
264    /// List available resources.
265    async fn list(
266        &self,
267        _cursor: Option<String>,
268        extra: cancellation::RequestHandlerExtra,
269    ) -> Result<crate::types::ListResourcesResult>;
270}
271
272/// Handler for message sampling (LLM operations).
273#[cfg(not(target_arch = "wasm32"))]
274#[async_trait]
275pub trait SamplingHandler: Send + Sync {
276    /// Create a message using the language model.
277    async fn create_message(
278        &self,
279        params: crate::types::CreateMessageParams,
280        extra: cancellation::RequestHandlerExtra,
281    ) -> Result<crate::types::CreateMessageResult>;
282}
283
284/// MCP server implementation.
285///
286/// # Examples
287///
288/// ```rust,no_run
289/// use pmcp::{Server, ServerCapabilities, ToolHandler};
290/// use async_trait::async_trait;
291/// use serde_json::Value;
292///
293/// struct MyTool;
294///
295/// #[async_trait]
296/// impl ToolHandler for MyTool {
297///     async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
298///         Ok(serde_json::json!({"result": "success"}))
299///     }
300/// }
301///
302/// # async fn example() -> pmcp::Result<()> {
303/// let server = Server::builder()
304///     .name("my-server")
305///     .version("1.0.0")
306///     .tool("my-tool", MyTool)
307///     .build()?;
308///
309/// server.run_stdio().await?;
310/// # Ok(())
311/// # }
312/// ```
313#[cfg(not(target_arch = "wasm32"))]
314#[allow(dead_code)]
315pub struct Server {
316    info: Implementation,
317    capabilities: ServerCapabilities,
318    tools: HashMap<String, Arc<dyn ToolHandler>>,
319    tool_infos: HashMap<String, ToolInfo>,
320    /// Cached URI-to-tool-meta index for widget resource `_meta` propagation.
321    uri_to_tool_meta: HashMap<String, serde_json::Map<String, serde_json::Value>>,
322    prompts: HashMap<String, Arc<dyn PromptHandler>>,
323    resources: Option<Arc<dyn ResourceHandler>>,
324    sampling: Option<Arc<dyn SamplingHandler>>,
325    client_capabilities: Arc<RwLock<Option<ClientCapabilities>>>,
326    initialized: Arc<RwLock<bool>>,
327    /// Channel for sending notifications
328    notification_tx: Option<mpsc::Sender<Notification>>,
329    /// Cancellation manager for request cancellation
330    cancellation_manager: cancellation::CancellationManager,
331    /// Roots manager for directory/URI registration
332    roots_manager: Arc<RwLock<roots::RootsManager>>,
333    /// Subscription manager for resource subscriptions
334    subscription_manager: Arc<RwLock<subscriptions::SubscriptionManager>>,
335    /// Elicitation manager for user input requests
336    elicitation_manager: Option<Arc<elicitation::ElicitationManager>>,
337    /// Outbound server-to-client request dispatcher with response correlation.
338    /// Wired in `Server::run`; `None` outside the run lifecycle.
339    #[allow(clippy::struct_field_names)]
340    server_request_dispatcher: Option<Arc<server_request_dispatcher::ServerRequestDispatcher>>,
341    /// Cached peer handle built alongside the dispatcher so dispatch sites
342    /// clone the Arc rather than allocating a new `DispatchPeerHandle` per
343    /// request. `None` outside the run lifecycle.
344    peer_handle: Option<Arc<dyn crate::shared::peer::PeerHandle>>,
345    /// Authentication provider for validating requests
346    auth_provider: Option<Arc<dyn auth::AuthProvider>>,
347    /// Tool authorizer for fine-grained access control
348    tool_authorizer: Option<Arc<dyn auth::ToolAuthorizer>>,
349    /// Tool middleware chain for cross-cutting concerns in tool execution
350    #[cfg(not(target_arch = "wasm32"))]
351    tool_middleware_chain: Arc<RwLock<tool_middleware::ToolMiddlewareChain>>,
352    /// HTTP middleware chain for `StreamableHttpServer` (configured via `ServerBuilder`)
353    #[cfg(feature = "streamable-http")]
354    http_middleware: Option<Arc<http_middleware::ServerHttpMiddlewareChain>>,
355}
356
357#[cfg(not(target_arch = "wasm32"))]
358impl std::fmt::Debug for Server {
359    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360        f.debug_struct("Server")
361            .field("info", &self.info)
362            .field("capabilities", &self.capabilities)
363            .field("tools", &self.tools.keys().collect::<Vec<_>>())
364            .field("prompts", &self.prompts.keys().collect::<Vec<_>>())
365            .field("resources", &self.resources.is_some())
366            .field("sampling", &self.sampling.is_some())
367            .field("initialized", &self.initialized)
368            .finish()
369    }
370}
371
372#[cfg(not(target_arch = "wasm32"))]
373impl Server {
374    /// Check if a tool exists
375    pub fn has_tool(&self, name: &str) -> bool {
376        self.tools.contains_key(name)
377    }
378
379    /// Check if a prompt exists
380    pub fn has_prompt(&self, name: &str) -> bool {
381        self.prompts.contains_key(name)
382    }
383
384    /// Get a prompt handler by name.
385    ///
386    /// Returns a borrowed reference to the registered prompt handler `Arc`,
387    /// or `None` if no prompt with that name has been registered. Callers
388    /// who need ownership can `Arc::clone(...)` the returned reference.
389    ///
390    /// # Handler-level testing pattern
391    ///
392    /// This accessor is the public API surface for the handler-level
393    /// integration testing pattern documented in the testing chapter of
394    /// the PMCP book: build a `Server`, retrieve the handler by name,
395    /// invoke `.handle(...).await` directly with a synthetic
396    /// `RequestHandlerExtra`. It exercises handler logic in isolation
397    /// without spinning up a transport.
398    ///
399    /// # What this pattern skips
400    ///
401    /// This pattern exercises handler logic only. The JSONRPC dispatch
402    /// path (`Server::handle_request`) is bypassed, so `auth_provider`,
403    /// `tool_authorizer`, and `tool_middleware` are **not** invoked. For
404    /// full-pipeline tests that exercise the security pipeline, drive a
405    /// real transport (stdio or streamable-http) with a `pmcp::Client`.
406    ///
407    /// # Examples
408    ///
409    /// ```rust
410    /// use std::collections::HashMap;
411    /// use std::sync::Arc;
412    /// use async_trait::async_trait;
413    /// use pmcp::{PromptHandler, Server};
414    /// use pmcp::types::{GetPromptResult, PromptMessage, Content};
415    /// use pmcp::types::content::Role;
416    ///
417    /// struct GreetingPrompt;
418    ///
419    /// #[async_trait]
420    /// impl PromptHandler for GreetingPrompt {
421    ///     async fn handle(
422    ///         &self,
423    ///         args: HashMap<String, String>,
424    ///         _extra: pmcp::RequestHandlerExtra,
425    ///     ) -> pmcp::Result<GetPromptResult> {
426    ///         let who = args.get("name").cloned().unwrap_or_else(|| "world".to_string());
427    ///         Ok(GetPromptResult::new(
428    ///             vec![PromptMessage::new(Role::User, Content::text(format!("Hello, {}!", who)))],
429    ///             Some("Greeting prompt".to_string()),
430    ///         ))
431    ///     }
432    /// }
433    ///
434    /// # async fn example() -> pmcp::Result<()> {
435    /// let server = Server::builder()
436    ///     .name("demo")
437    ///     .version("0.1")
438    ///     .prompt_arc("greet", Arc::new(GreetingPrompt))
439    ///     .build()?;
440    ///
441    /// let handler = server.get_prompt("greet").expect("registered above");
442    /// let mut args = HashMap::new();
443    /// args.insert("name".to_string(), "claude".to_string());
444    /// let result = handler
445    ///     .handle(args, pmcp::RequestHandlerExtra::default())
446    ///     .await?;
447    /// assert_eq!(result.messages.len(), 1);
448    /// # Ok(())
449    /// # }
450    /// ```
451    pub fn get_prompt(&self, name: &str) -> Option<&Arc<dyn PromptHandler>> {
452        self.prompts.get(name)
453    }
454
455    /// Get a tool handler by name.
456    ///
457    /// Returns a borrowed reference to the registered tool handler `Arc`,
458    /// or `None` if no tool with that name has been registered. Callers
459    /// who need ownership can `Arc::clone(...)` the returned reference.
460    ///
461    /// # Handler-level testing pattern
462    ///
463    /// This accessor is the public API surface for the handler-level
464    /// integration testing pattern: build a `Server`, retrieve the
465    /// handler by name, invoke `.handle(...).await` directly with a
466    /// synthetic `RequestHandlerExtra`. It exercises handler logic in
467    /// isolation without spinning up a transport, which is the primary
468    /// shape downstream toolkit authors use to assert on a built
469    /// `pmcp::Server`'s registered handlers.
470    ///
471    /// # What this pattern skips
472    ///
473    /// This pattern exercises handler logic only. The JSONRPC dispatch
474    /// path (`Server::handle_request`) is bypassed, so `auth_provider`,
475    /// `tool_authorizer`, and `tool_middleware` are **not** invoked. For
476    /// full-pipeline tests that exercise the security pipeline, drive a
477    /// real transport (stdio or streamable-http) with a `pmcp::Client`.
478    ///
479    /// # Examples
480    ///
481    /// ```rust
482    /// use std::sync::Arc;
483    /// use async_trait::async_trait;
484    /// use pmcp::{Server, ToolHandler};
485    /// use serde_json::Value;
486    ///
487    /// struct EchoTool;
488    ///
489    /// #[async_trait]
490    /// impl ToolHandler for EchoTool {
491    ///     async fn handle(
492    ///         &self,
493    ///         args: Value,
494    ///         _extra: pmcp::RequestHandlerExtra,
495    ///     ) -> pmcp::Result<Value> {
496    ///         Ok(serde_json::json!({ "echoed": args }))
497    ///     }
498    /// }
499    ///
500    /// # async fn example() -> pmcp::Result<()> {
501    /// let server = Server::builder()
502    ///     .name("demo")
503    ///     .version("0.1")
504    ///     .tool_arc("echo", Arc::new(EchoTool))
505    ///     .build()?;
506    ///
507    /// let handler = server.get_tool("echo").expect("registered above");
508    /// let result = handler
509    ///     .handle(serde_json::json!({"msg": "hi"}), pmcp::RequestHandlerExtra::default())
510    ///     .await?;
511    /// assert_eq!(result, serde_json::json!({"echoed": {"msg": "hi"}}));
512    /// # Ok(())
513    /// # }
514    /// ```
515    pub fn get_tool(&self, name: &str) -> Option<&Arc<dyn ToolHandler>> {
516        self.tools.get(name)
517    }
518
519    /// Get the HTTP middleware chain configured via `ServerBuilder`.
520    ///
521    /// Returns the HTTP middleware chain that was set using
522    /// `ServerBuilder::with_http_middleware()`. This can be used when
523    /// creating a `StreamableHttpServer`.
524    ///
525    /// # Examples
526    ///
527    /// ```rust,no_run
528    /// # #[cfg(feature = "streamable-http")]
529    /// # {
530    /// use pmcp::Server;
531    /// use pmcp::server::streamable_http_server::StreamableHttpServerConfig;
532    ///
533    /// # async fn example() -> pmcp::Result<()> {
534    /// let server = Server::builder()
535    ///     .name("my-server")
536    ///     .version("1.0.0")
537    ///     // ... with_http_middleware() called here
538    ///     .build()?;
539    ///
540    /// let config = StreamableHttpServerConfig {
541    ///     http_middleware: server.http_middleware(),
542    ///     ..Default::default()
543    /// };
544    /// # Ok(())
545    /// # }
546    /// # }
547    /// ```
548    #[cfg(feature = "streamable-http")]
549    pub fn http_middleware(&self) -> Option<Arc<http_middleware::ServerHttpMiddlewareChain>> {
550        self.http_middleware.clone()
551    }
552
553    /// Get the authentication provider configured via `ServerBuilder`.
554    ///
555    /// Returns the authentication provider that was set using
556    /// `ServerBuilder::auth_provider()`. This can be used by transport
557    /// layers to validate incoming requests and extract auth context.
558    pub fn get_auth_provider(&self) -> Option<Arc<dyn auth::AuthProvider>> {
559        self.auth_provider.clone()
560    }
561
562    /// Build tool and resource registries for workflow expansion.
563    ///
564    /// Creates `HashMap` registries that can be used to build an `ExpansionContext`
565    /// for converting workflow prompts to protocol types. The registries are
566    /// automatically populated from all registered tools and resources.
567    ///
568    /// Returns a tuple of (`tools_map`, `resources_map`) that can be used with
569    /// `ExpansionContext`.
570    ///
571    /// # Examples
572    ///
573    /// ```rust,ignore
574    /// use pmcp::Server;
575    /// use pmcp::server::workflow::{InternalPromptMessage, ToolHandle, PromptContent, conversion::ExpansionContext};
576    /// use pmcp::types::Role;
577    ///
578    /// # async fn example() -> pmcp::Result<()> {
579    /// let server = Server::builder()
580    ///     .name("example-server")
581    ///     .version("1.0.0")
582    ///     .build()?;
583    ///
584    /// // Build registries from registered tools/resources
585    /// let (tools, resources) = server.build_expansion_registries();
586    ///
587    /// // Create expansion context
588    /// let ctx = ExpansionContext {
589    ///     tools: &tools,
590    ///     resources: &resources,
591    /// };
592    ///
593    /// // Use it to convert workflow prompts to protocol types
594    /// let msg = InternalPromptMessage::new(
595    ///     Role::System,
596    ///     PromptContent::ToolHandle(ToolHandle::new("my_tool"))
597    /// );
598    /// let protocol_msg = msg.to_protocol(&ctx)?;
599    /// # Ok(())
600    /// # }
601    /// ```
602    pub fn build_expansion_registries(
603        &self,
604    ) -> (
605        HashMap<Arc<str>, workflow::conversion::ToolInfo>,
606        HashMap<Arc<str>, workflow::conversion::ResourceInfo>,
607    ) {
608        use std::collections::HashMap;
609
610        // Build tools map from registered tool handlers
611        let mut tools_map = HashMap::new();
612        for (name, handler) in &self.tools {
613            if let Some(metadata) = handler.metadata() {
614                tools_map.insert(
615                    Arc::from(name.as_str()),
616                    workflow::conversion::ToolInfo {
617                        name: metadata.name,
618                        description: metadata.description.unwrap_or_default(),
619                        input_schema: metadata.input_schema,
620                    },
621                );
622            }
623        }
624
625        // Build resources map (currently empty - resources don't have metadata())
626        // This could be enhanced in the future when resources have better metadata
627        let resources_map = HashMap::new();
628
629        (tools_map, resources_map)
630    }
631
632    /// Send a notification.
633    ///
634    /// Sends a notification to the connected client. Notifications are one-way
635    /// messages that don't expect a response.
636    ///
637    /// # Arguments
638    ///
639    /// * `notification` - The server notification to send
640    ///
641    /// # Examples
642    ///
643    /// ```rust,no_run
644    /// use pmcp::{Server, ServerNotification, ProgressNotification, ProgressToken};
645    ///
646    /// # async fn example() -> pmcp::Result<()> {
647    /// let server = Server::builder()
648    ///     .name("example-server")
649    ///     .version("1.0.0")
650    ///     .build()?;
651    ///
652    /// // Send a progress notification
653    /// let progress = ProgressNotification::new(
654    ///     ProgressToken::String("task-123".to_string()),
655    ///     50.0,
656    ///     Some("Processing...".to_string()),
657    /// );
658    ///
659    /// server.send_notification(ServerNotification::Progress(progress)).await;
660    /// # Ok(())
661    /// # }
662    /// ```
663    pub async fn send_notification(&self, notification: ServerNotification) {
664        if let Some(tx) = &self.notification_tx {
665            let _ = tx.send(Notification::Server(notification)).await;
666        }
667    }
668
669    /// Get client capabilities.
670    ///
671    /// Returns the capabilities that the client declared during initialization.
672    /// This can be used to check if the client supports specific features.
673    ///
674    /// # Examples
675    ///
676    /// ```rust,no_run
677    /// use pmcp::Server;
678    ///
679    /// # async fn example() -> pmcp::Result<()> {
680    /// let server = Server::builder()
681    ///     .name("example-server")
682    ///     .version("1.0.0")
683    ///     .build()?;
684    ///
685    /// // Check client capabilities after initialization
686    /// if let Some(capabilities) = server.get_client_capabilities().await {
687    ///     if capabilities.sampling.is_some() {
688    ///         println!("Client supports LLM sampling requests");
689    ///     }
690    ///     if capabilities.elicitation.is_some() {
691    ///         println!("Client supports user input requests");
692    ///     }
693    /// }
694    /// # Ok(())
695    /// # }
696    /// ```
697    ///
698    /// # Returns
699    ///
700    /// - `Some(ClientCapabilities)` if the client has been initialized
701    /// - `None` if the client hasn't initialized yet
702    pub async fn get_client_capabilities(&self) -> Option<ClientCapabilities> {
703        self.client_capabilities.read().await.clone()
704    }
705
706    /// Check if the server is initialized.
707    ///
708    /// Returns true if the initialization handshake with a client has completed.
709    /// The server must be initialized before it can process most requests.
710    ///
711    /// # Examples
712    ///
713    /// ```rust,no_run
714    /// use pmcp::Server;
715    ///
716    /// # async fn example() -> pmcp::Result<()> {
717    /// let server = Server::builder()
718    ///     .name("example-server")
719    ///     .version("1.0.0")
720    ///     .build()?;
721    ///
722    /// if server.is_initialized().await {
723    ///     println!("Server is ready to handle requests");
724    /// } else {
725    ///     println!("Waiting for client initialization");
726    /// }
727    /// # Ok(())
728    /// # }
729    /// ```
730    pub async fn is_initialized(&self) -> bool {
731        *self.initialized.read().await
732    }
733    /// Create a new server builder.
734    ///
735    /// Returns a `ServerBuilder` for configuring and constructing a new MCP server.
736    /// The builder pattern allows you to set server information, capabilities,
737    /// and register handlers before building the final server instance.
738    ///
739    /// # Examples
740    ///
741    /// ```rust,no_run
742    /// use pmcp::{Server, ToolHandler};
743    /// use async_trait::async_trait;
744    /// use serde_json::Value;
745    ///
746    /// struct HelloTool;
747    ///
748    /// #[async_trait]
749    /// impl ToolHandler for HelloTool {
750    ///     async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
751    ///         Ok(serde_json::json!({"message": "Hello, World!"}))
752    ///     }
753    /// }
754    ///
755    /// # async fn example() -> pmcp::Result<()> {
756    /// let server = Server::builder()
757    ///     .name("greeting-server")
758    ///     .version("1.0.0")
759    ///     .tool("hello", HelloTool{})
760    ///     .build()?;
761    /// # Ok(())
762    /// # }
763    /// ```
764    pub fn builder() -> ServerBuilder {
765        ServerBuilder::new()
766    }
767
768    /// Run the server with stdio transport.
769    ///
770    /// Starts the server using stdin/stdout for communication.
771    /// This is the standard way to run MCP servers as they communicate
772    /// via JSON-RPC over stdio.
773    ///
774    /// # Examples
775    ///
776    /// ```rust,no_run
777    /// use pmcp::{Server, ToolHandler};
778    /// use async_trait::async_trait;
779    /// use serde_json::Value;
780    ///
781    /// struct EchoTool;
782    ///
783    /// #[async_trait]
784    /// impl ToolHandler for EchoTool {
785    ///     async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
786    ///         Ok(args) // Echo the input
787    ///     }
788    /// }
789    ///
790    /// # async fn example() -> pmcp::Result<()> {
791    /// let server = Server::builder()
792    ///     .name("echo-server")
793    ///     .version("1.0.0")
794    ///     .tool("echo", EchoTool{})
795    ///     .build()?;
796    ///
797    /// // This will run indefinitely, handling client requests
798    /// server.run_stdio().await?;
799    /// # Ok(())
800    /// # }
801    /// ```
802    ///
803    /// # Errors
804    ///
805    /// Returns an error if:
806    /// - The stdio transport fails to initialize
807    /// - Communication with the client fails
808    /// - The server encounters an unrecoverable error
809    pub async fn run_stdio(self) -> Result<()> {
810        let transport = crate::shared::StdioTransport::new();
811        self.run(transport).await
812    }
813
814    /// Run the server with a custom transport.
815    ///
816    /// Starts the server using a custom transport implementation.
817    /// This allows for different communication mechanisms beyond stdio,
818    /// such as TCP sockets, `WebSockets`, or other protocols.
819    ///
820    /// # Arguments
821    ///
822    /// * `transport` - The transport implementation to use for communication
823    ///
824    /// # Examples
825    ///
826    /// ```rust,no_run
827    /// use pmcp::{Server, StdioTransport, ToolHandler};
828    /// use async_trait::async_trait;
829    /// use serde_json::Value;
830    ///
831    /// struct CalculatorTool;
832    ///
833    /// #[async_trait]
834    /// impl ToolHandler for CalculatorTool {
835    ///     async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
836    ///         let a = args["a"].as_f64().unwrap_or(0.0);
837    ///         let b = args["b"].as_f64().unwrap_or(0.0);
838    ///         Ok(serde_json::json!({"result": a + b}))
839    ///     }
840    /// }
841    ///
842    /// # async fn example() -> pmcp::Result<()> {
843    /// let server = Server::builder()
844    ///     .name("calculator-server")
845    ///     .version("1.0.0")
846    ///     .tool("add", CalculatorTool{})
847    ///     .build()?;
848    ///
849    /// let transport = StdioTransport::new();
850    /// server.run(transport).await?;
851    /// # Ok(())
852    /// # }
853    /// ```
854    ///
855    /// # Errors
856    ///
857    /// Returns an error if:
858    /// - The transport fails to initialize or operate
859    /// - Communication with the client fails
860    /// - The server encounters an unrecoverable error
861    pub async fn run<T: crate::shared::Transport + 'static>(mut self, transport: T) -> Result<()> {
862        let (notification_tx, notification_rx) = mpsc::channel(100);
863        self.notification_tx = Some(notification_tx);
864
865        // Hook cancellation manager to send notifications via the same channel
866        if let Some(tx) = &self.notification_tx {
867            let tx = tx.clone();
868            self.cancellation_manager
869                .set_notification_sender(Arc::new(move |notification| {
870                    let _ = tx.try_send(notification);
871                }));
872        }
873
874        // Outbound server-to-client request channel + dispatcher. Drain task
875        // wraps each `(correlation_id, ServerRequest)` as
876        // `TransportMessage::Request` and forwards to the transport;
877        // `handle_transport_message` routes responses back through
878        // `dispatcher.handle_response`.
879        let (outbound_tx, outbound_rx) =
880            mpsc::channel::<(String, crate::types::ServerRequest)>(100);
881        let dispatcher = Arc::new(
882            server_request_dispatcher::ServerRequestDispatcher::new_with_channel(outbound_tx),
883        );
884        let peer: Arc<dyn crate::shared::peer::PeerHandle> = Arc::new(
885            crate::server::peer_impl::DispatchPeerHandle::new(dispatcher.clone()),
886        );
887        self.peer_handle = Some(peer);
888        self.server_request_dispatcher = Some(dispatcher);
889
890        let server = Arc::new(self);
891        let transport = Arc::new(RwLock::new(transport));
892        let protocol = Arc::new(RwLock::new(Protocol::new(ProtocolOptions::default())));
893
894        Self::spawn_notification_handler(transport.clone(), notification_rx);
895        server_request_dispatcher::spawn_server_request_drain(transport.clone(), outbound_rx);
896        Self::spawn_message_handler(server.clone(), transport.clone(), protocol);
897
898        // Keep the main task alive
899        Self::run_main_loop().await
900    }
901
902    /// Attach the cached peer handle to `extra` when a dispatcher is configured.
903    /// No-op on wasm32 and when running outside the `run()` lifecycle.
904    #[inline]
905    fn attach_peer(
906        &self,
907        extra: crate::server::cancellation::RequestHandlerExtra,
908    ) -> crate::server::cancellation::RequestHandlerExtra {
909        #[cfg(not(target_arch = "wasm32"))]
910        if let Some(peer) = self.peer_handle.as_ref() {
911            return extra.with_peer(peer.clone());
912        }
913        extra
914    }
915
916    /// Spawn task to handle outgoing notifications.
917    fn spawn_notification_handler(
918        transport: Arc<RwLock<impl crate::shared::Transport + 'static>>,
919        mut notification_rx: mpsc::Receiver<Notification>,
920    ) {
921        tokio::spawn(async move {
922            while let Some(notification) = notification_rx.recv().await {
923                if let Err(e) =
924                    Self::send_notification_through_transport(&transport, notification).await
925                {
926                    Self::log_error(&format!("Failed to send notification: {}", e)).await;
927                }
928            }
929        });
930    }
931
932    /// Spawn task to handle incoming messages.
933    fn spawn_message_handler(
934        server: Arc<Self>,
935        transport: Arc<RwLock<impl crate::shared::Transport + 'static>>,
936        _protocol: Arc<RwLock<Protocol>>,
937    ) {
938        tokio::spawn(async move {
939            loop {
940                let message = match Self::receive_message_from_transport(&transport).await {
941                    Ok(msg) => msg,
942                    Err(e) => {
943                        Self::log_error(&format!("Transport receive error: {}", e)).await;
944                        break;
945                    },
946                };
947
948                if let Err(e) = Self::handle_transport_message(&server, &transport, message).await {
949                    Self::log_error(&format!("Message handling error: {}", e)).await;
950                    break;
951                }
952            }
953        });
954    }
955
956    /// Send a notification through the transport.
957    async fn send_notification_through_transport(
958        transport: &Arc<RwLock<impl crate::shared::Transport>>,
959        notification: Notification,
960    ) -> Result<()> {
961        let mut t = transport.write().await;
962        t.send(TransportMessage::Notification(notification)).await
963    }
964
965    /// Receive a message from the transport.
966    async fn receive_message_from_transport(
967        transport: &Arc<RwLock<impl crate::shared::Transport>>,
968    ) -> Result<TransportMessage> {
969        let mut t = transport.write().await;
970        t.receive().await
971    }
972
973    /// Handle a transport message.
974    async fn handle_transport_message(
975        server: &Arc<Self>,
976        transport: &Arc<RwLock<impl crate::shared::Transport>>,
977        message: TransportMessage,
978    ) -> Result<()> {
979        match message {
980            TransportMessage::Request { id, request } => {
981                Self::handle_request_message(server, transport, id, request).await
982            },
983            TransportMessage::Response(response) => {
984                // Route correlated client responses through the dispatcher so
985                // pending dispatches resolve.
986                if let Some(dispatcher) = &server.server_request_dispatcher {
987                    let correlation_id = response.id.to_string();
988                    let payload = match &response.payload {
989                        crate::types::jsonrpc::ResponsePayload::Result(value) => value.clone(),
990                        crate::types::jsonrpc::ResponsePayload::Error(err) => {
991                            // Represent errors as a JSON object so callers can
992                            // distinguish — dispatch() returns the Value as-is.
993                            serde_json::to_value(err).unwrap_or(Value::Null)
994                        },
995                    };
996                    if let Err(e) = dispatcher.handle_response(&correlation_id, payload).await {
997                        Self::log_warning(&format!(
998                            "Failed to route response {}: {}",
999                            correlation_id, e
1000                        ))
1001                        .await;
1002                    }
1003                } else {
1004                    Self::log_warning("Server received response but no dispatcher configured")
1005                        .await;
1006                }
1007                Ok(())
1008            },
1009            TransportMessage::Notification(notification) => {
1010                // Handle client cancellation notifications
1011                if let Notification::Client(crate::types::ClientNotification::Cancelled(params)) =
1012                    &notification
1013                {
1014                    let request_id = params.request_id.to_string();
1015                    server
1016                        .cancellation_manager
1017                        .cancel_request_silent(request_id)
1018                        .await?;
1019                }
1020
1021                Self::log_debug("Server received notification").await;
1022                Ok(())
1023            },
1024        }
1025    }
1026
1027    /// Handle a request message.
1028    async fn handle_request_message(
1029        server: &Arc<Self>,
1030        transport: &Arc<RwLock<impl crate::shared::Transport>>,
1031        id: RequestId,
1032        request: Request,
1033    ) -> Result<()> {
1034        let response = server.handle_request(id, request, None).await;
1035        let mut t = transport.write().await;
1036        t.send(TransportMessage::Response(response)).await
1037    }
1038
1039    /// Log an error message.
1040    async fn log_error(message: &str) {
1041        crate::log(crate::types::LogLevel::Error, message, None).await;
1042    }
1043
1044    /// Log a warning message.
1045    async fn log_warning(message: &str) {
1046        crate::log(crate::types::LogLevel::Warning, message, None).await;
1047    }
1048
1049    /// Log a debug message.
1050    async fn log_debug(message: &str) {
1051        crate::log(crate::types::LogLevel::Debug, message, None).await;
1052    }
1053
1054    /// Run the main event loop.
1055    async fn run_main_loop() -> Result<()> {
1056        loop {
1057            tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
1058        }
1059    }
1060
1061    async fn handle_request(
1062        &self,
1063        id: RequestId,
1064        request: Request,
1065        auth_context: Option<auth::AuthContext>,
1066    ) -> JSONRPCResponse {
1067        match request {
1068            Request::Client(ref boxed_req)
1069                if matches!(**boxed_req, ClientRequest::Initialize(_)) =>
1070            {
1071                let ClientRequest::Initialize(init_req) = boxed_req.as_ref() else {
1072                    unreachable!("Pattern matched for Initialize");
1073                };
1074                // Store client capabilities
1075                *self.client_capabilities.write().await = Some(init_req.capabilities.clone());
1076                *self.initialized.write().await = true;
1077
1078                let negotiated_version =
1079                    crate::negotiate_protocol_version(&init_req.protocol_version);
1080
1081                let result = InitializeResult {
1082                    protocol_version: ProtocolVersion(negotiated_version.to_string()),
1083                    capabilities: self.capabilities.clone(),
1084                    server_info: self.info.clone(),
1085                    instructions: None,
1086                };
1087                JSONRPCResponse {
1088                    jsonrpc: "2.0".to_string(),
1089                    id: id.clone(),
1090                    payload: crate::types::jsonrpc::ResponsePayload::Result(
1091                        serde_json::to_value(result).unwrap(),
1092                    ),
1093                }
1094            },
1095            Request::Client(boxed_req) => {
1096                self.handle_client_request(id, *boxed_req, auth_context)
1097                    .await
1098            },
1099            Request::Server(_) => JSONRPCResponse {
1100                jsonrpc: "2.0".to_string(),
1101                id,
1102                payload: crate::types::jsonrpc::ResponsePayload::Error(
1103                    crate::types::jsonrpc::JSONRPCError {
1104                        code: -32601,
1105                        message: "Server requests not supported by server".to_string(),
1106                        data: None,
1107                    },
1108                ),
1109            },
1110        }
1111    }
1112
1113    async fn handle_client_request(
1114        &self,
1115        id: RequestId,
1116        request: ClientRequest,
1117        auth_context: Option<auth::AuthContext>,
1118    ) -> JSONRPCResponse {
1119        let result = self
1120            .process_client_request(id.clone(), request, auth_context)
1121            .await;
1122        Self::create_response(id, result)
1123    }
1124
1125    /// Process a client request and return the result.
1126    async fn process_client_request(
1127        &self,
1128        request_id: RequestId,
1129        request: ClientRequest,
1130        auth_context: Option<auth::AuthContext>,
1131    ) -> Result<serde_json::Value> {
1132        match request {
1133            ClientRequest::Initialize(_) => {
1134                // Already handled above
1135                unreachable!("Initialize should be handled separately")
1136            },
1137            ClientRequest::ListTools(req) => self.handle_list_tools(req),
1138            ClientRequest::CallTool(req) => {
1139                self.handle_call_tool(request_id, req, auth_context).await
1140            },
1141            ClientRequest::ListPrompts(req) => self.handle_list_prompts(req),
1142            ClientRequest::GetPrompt(req) => {
1143                self.handle_get_prompt(request_id, req, auth_context).await
1144            },
1145            ClientRequest::ListResources(req) => {
1146                self.handle_list_resources(request_id, req, auth_context)
1147                    .await
1148            },
1149            ClientRequest::ReadResource(req) => {
1150                self.handle_read_resource(request_id, req, auth_context)
1151                    .await
1152            },
1153            ClientRequest::ListResourceTemplates(req) => {
1154                Self::handle_list_resource_templates(self, req)
1155            },
1156            ClientRequest::Subscribe(_)
1157            | ClientRequest::Unsubscribe(_)
1158            | ClientRequest::Complete(_)
1159            | ClientRequest::SetLoggingLevel { level: _ }
1160            | ClientRequest::Ping => Ok(serde_json::json!({})),
1161            ClientRequest::CreateMessage(req) => self.handle_create_message(request_id, *req).await,
1162            // Note: Elicitation responses are now handled as the response to
1163            // ServerRequest::ElicitationCreate in the JSON-RPC response flow,
1164            // not as a separate client request variant.
1165            // Task requests (experimental MCP Tasks) — routed directly in each arm below
1166            ClientRequest::TasksGet(_)
1167            | ClientRequest::TasksResult(_)
1168            | ClientRequest::TasksList(_)
1169            | ClientRequest::TasksCancel(_) => Err(crate::Error::protocol(
1170                crate::ErrorCode::METHOD_NOT_FOUND,
1171                "Tasks not supported: no task router configured",
1172            )),
1173        }
1174    }
1175
1176    /// Create a JSON-RPC response from a result.
1177    fn create_response(id: RequestId, result: Result<serde_json::Value>) -> JSONRPCResponse {
1178        match result {
1179            Ok(value) => JSONRPCResponse {
1180                jsonrpc: "2.0".to_string(),
1181                id,
1182                payload: crate::types::jsonrpc::ResponsePayload::Result(value),
1183            },
1184            Err(e) => JSONRPCResponse {
1185                jsonrpc: "2.0".to_string(),
1186                id,
1187                payload: crate::types::jsonrpc::ResponsePayload::Error(
1188                    crate::types::jsonrpc::JSONRPCError {
1189                        code: -32603,
1190                        message: e.to_string(),
1191                        data: None,
1192                    },
1193                ),
1194            },
1195        }
1196    }
1197
1198    fn handle_list_tools(&self, _req: ListToolsRequest) -> Result<Value> {
1199        let tools: Vec<ToolInfo> = self.tool_infos.values().cloned().collect();
1200
1201        Ok(serde_json::to_value(ListToolsResult {
1202            tools,
1203            next_cursor: None,
1204        })?)
1205    }
1206
1207    async fn handle_call_tool(
1208        &self,
1209        request_id: RequestId,
1210        req: CallToolRequest,
1211        auth_context: Option<auth::AuthContext>,
1212    ) -> Result<Value> {
1213        let handler = self
1214            .tools
1215            .get(&req.name)
1216            .ok_or_else(|| Error::not_found(format!("Tool '{}' not found", req.name)))?;
1217
1218        let request_id_str = request_id.to_string();
1219        let cancellation_token = self
1220            .cancellation_manager
1221            .create_token(request_id_str.clone())
1222            .await;
1223
1224        // Auth context now comes from the transport layer
1225        // Validate authentication if auth provider is configured
1226        let validated_auth_context = if let Some(auth_provider) = &self.auth_provider {
1227            // If auth_context was provided by transport, use it; otherwise validate
1228            if auth_context.is_some() {
1229                auth_context
1230            } else {
1231                // Fallback: try to validate without headers (for backward compatibility)
1232                auth_provider.validate_request(None).await?
1233            }
1234        } else {
1235            auth_context // No auth provider, just use what was provided
1236        };
1237
1238        // Check tool authorization if tool authorizer is configured
1239        if let (Some(auth_ctx), Some(authorizer)) = (&validated_auth_context, &self.tool_authorizer)
1240        {
1241            if !authorizer.can_access_tool(auth_ctx, &req.name).await? {
1242                return Err(Error::protocol(
1243                    crate::error::ErrorCode::AUTHENTICATION_REQUIRED,
1244                    format!("Access denied for tool '{}'", req.name),
1245                ));
1246            }
1247        }
1248
1249        // Create progress reporter if progress token is provided
1250        #[allow(clippy::used_underscore_binding)] // _meta is part of MCP protocol spec
1251        let progress_reporter = req
1252            ._meta
1253            .as_ref()
1254            .and_then(|meta| meta.progress_token.as_ref())
1255            .and_then(|token| {
1256                self.notification_tx.as_ref().map(|tx| {
1257                    let tx = tx.clone();
1258                    let reporter = crate::server::progress::ServerProgressReporter::new(
1259                        token.clone(),
1260                        Arc::new(move |notification| {
1261                            let _ = tx.try_send(notification);
1262                        }),
1263                    );
1264                    Arc::new(reporter) as Arc<dyn crate::server::progress::ProgressReporter>
1265                })
1266            });
1267
1268        let mut extra = self.attach_peer(
1269            crate::server::cancellation::RequestHandlerExtra::new(
1270                request_id.to_string(),
1271                cancellation_token,
1272            )
1273            .with_auth_context(validated_auth_context)
1274            .with_progress_reporter(progress_reporter),
1275        );
1276
1277        // Execute tool with middleware (native-only)
1278        #[cfg(not(target_arch = "wasm32"))]
1279        let result = {
1280            // Create tool context for middleware
1281            let context = tool_middleware::ToolContext::new(&req.name, &request_id_str);
1282
1283            // Clone arguments for middleware processing
1284            let mut args = req.arguments;
1285
1286            // Process request through tool middleware chain
1287            // Middleware rejection short-circuits tool execution
1288            self.tool_middleware_chain
1289                .read()
1290                .await
1291                .process_request(&req.name, &mut args, &mut extra, &context)
1292                .await?;
1293
1294            // Execute the tool with potentially modified args and extra
1295            let mut result = handler.handle(args, extra).await;
1296
1297            // Process response through tool middleware chain
1298            if let Err(e) = self
1299                .tool_middleware_chain
1300                .read()
1301                .await
1302                .process_response(&req.name, &mut result, &context)
1303                .await
1304            {
1305                // Log error but continue with original result
1306                tracing::warn!("Tool response middleware processing failed: {}", e);
1307            }
1308
1309            // If tool execution failed, call handle_tool_error
1310            if let Err(ref e) = result {
1311                self.tool_middleware_chain
1312                    .read()
1313                    .await
1314                    .handle_tool_error(&req.name, e, &context)
1315                    .await;
1316            }
1317
1318            result
1319        };
1320
1321        // On WASM, execute tool directly without middleware
1322        #[cfg(target_arch = "wasm32")]
1323        let result = handler.handle(req.arguments, extra).await;
1324
1325        // Token cleanup is unconditional (success or failure) and does not
1326        // touch `result`, so it runs once before the value/error split.
1327        self.cancellation_manager
1328            .remove_token(&request_id_str)
1329            .await;
1330        let result = match result {
1331            Ok(v) => v,
1332            // `Error::ToolRejected` is an APPLICATION-level rejection (e.g.
1333            // Code Mode policy: a SELECT missing its LIMIT), not a protocol
1334            // fault. Map it to a successful `CallToolResult { isError: true }`
1335            // (message → content, details → structuredContent) so the model
1336            // reads the reason and retries with corrected input, instead of
1337            // `?`-propagating a JSON-RPC error that reads as a server crash.
1338            // All other errors keep propagating as protocol errors.
1339            Err(Error::ToolRejected { message, details }) => {
1340                return Ok(serde_json::to_value(CallToolResult::rejected(
1341                    message, details,
1342                ))?);
1343            },
1344            Err(e) => return Err(e),
1345        };
1346        // Build CallToolResult, adding structured_content for widget tools
1347        let text = result.to_string();
1348        let mut call_result = CallToolResult::new(vec![crate::types::Content::text(text)]);
1349
1350        if let Some(info) = self.tool_infos.get(&req.name) {
1351            call_result = call_result.with_widget_enrichment(info, result);
1352        }
1353
1354        Ok(serde_json::to_value(call_result)?)
1355    }
1356
1357    fn handle_list_prompts(&self, _req: ListPromptsRequest) -> Result<Value> {
1358        let prompts = self
1359            .prompts
1360            .iter()
1361            .map(|(name, handler)| {
1362                // Use prompt metadata if provided, otherwise use defaults
1363                if let Some(mut info) = handler.metadata() {
1364                    // Ensure the name matches the registered name
1365                    info.name.clone_from(name);
1366                    info
1367                } else {
1368                    crate::types::PromptInfo::new(name)
1369                }
1370            })
1371            .collect::<Vec<_>>();
1372
1373        Ok(serde_json::to_value(ListPromptsResult {
1374            prompts,
1375            next_cursor: None,
1376        })?)
1377    }
1378
1379    async fn handle_get_prompt(
1380        &self,
1381        request_id: RequestId,
1382        req: GetPromptRequest,
1383        auth_context: Option<auth::AuthContext>,
1384    ) -> Result<Value> {
1385        let handler = self
1386            .prompts
1387            .get(&req.name)
1388            .ok_or_else(|| Error::not_found(format!("Prompt '{}' not found", req.name)))?;
1389
1390        let request_id_str = request_id.to_string();
1391        let cancellation_token = self
1392            .cancellation_manager
1393            .create_token(request_id_str.clone())
1394            .await;
1395
1396        // Create progress reporter if progress token is provided
1397        #[allow(clippy::used_underscore_binding)] // _meta is part of MCP protocol spec
1398        let progress_reporter = req
1399            ._meta
1400            .as_ref()
1401            .and_then(|meta| meta.progress_token.as_ref())
1402            .and_then(|token| {
1403                self.notification_tx.as_ref().map(|tx| {
1404                    let tx = tx.clone();
1405                    let reporter = crate::server::progress::ServerProgressReporter::new(
1406                        token.clone(),
1407                        Arc::new(move |notification| {
1408                            let _ = tx.try_send(notification);
1409                        }),
1410                    );
1411                    Arc::new(reporter) as Arc<dyn crate::server::progress::ProgressReporter>
1412                })
1413            });
1414
1415        let extra = self.attach_peer(
1416            crate::server::cancellation::RequestHandlerExtra::new(
1417                request_id_str.clone(),
1418                cancellation_token,
1419            )
1420            .with_auth_context(auth_context)
1421            .with_progress_reporter(progress_reporter),
1422        );
1423        let result = match handler.handle(req.arguments, extra).await {
1424            Ok(v) => {
1425                self.cancellation_manager
1426                    .remove_token(&request_id_str)
1427                    .await;
1428                Ok(v)
1429            },
1430            Err(e) => {
1431                self.cancellation_manager
1432                    .remove_token(&request_id_str)
1433                    .await;
1434                Err(e)
1435            },
1436        }?;
1437        Ok(serde_json::to_value(result)?)
1438    }
1439
1440    async fn handle_list_resources(
1441        &self,
1442        request_id: RequestId,
1443        req: ListResourcesRequest,
1444        auth_context: Option<auth::AuthContext>,
1445    ) -> Result<Value> {
1446        if let Some(handler) = &self.resources {
1447            let request_id_str = request_id.to_string();
1448            let cancellation_token = self
1449                .cancellation_manager
1450                .create_token(request_id_str.clone())
1451                .await;
1452            let extra = self.attach_peer(
1453                crate::server::cancellation::RequestHandlerExtra::new(
1454                    request_id_str.clone(),
1455                    cancellation_token,
1456                )
1457                .with_auth_context(auth_context),
1458            );
1459            let mut result = match handler.list(req.cursor, extra).await {
1460                Ok(v) => {
1461                    self.cancellation_manager
1462                        .remove_token(&request_id_str)
1463                        .await;
1464                    Ok(v)
1465                },
1466                Err(e) => {
1467                    self.cancellation_manager
1468                        .remove_token(&request_id_str)
1469                        .await;
1470                    Err(e)
1471                },
1472            }?;
1473            // Enrich ResourceInfo with tool _meta for widget resources
1474            if !self.uri_to_tool_meta.is_empty() {
1475                for resource in &mut result.resources {
1476                    if let Some(tool_meta) = self.uri_to_tool_meta.get(&resource.uri) {
1477                        let meta = resource.meta.get_or_insert_with(serde_json::Map::new);
1478                        crate::types::ui::deep_merge(meta, tool_meta.clone());
1479                    }
1480                }
1481            }
1482            Ok(serde_json::to_value(result)?)
1483        } else {
1484            Ok(serde_json::to_value(ListResourcesResult {
1485                resources: vec![],
1486                next_cursor: None,
1487            })?)
1488        }
1489    }
1490
1491    async fn handle_read_resource(
1492        &self,
1493        request_id: RequestId,
1494        req: ReadResourceRequest,
1495        auth_context: Option<auth::AuthContext>,
1496    ) -> Result<Value> {
1497        let handler = self
1498            .resources
1499            .as_ref()
1500            .ok_or_else(|| Error::not_found("No resource handler configured".to_string()))?;
1501
1502        let request_id_str = request_id.to_string();
1503        let cancellation_token = self
1504            .cancellation_manager
1505            .create_token(request_id_str.clone())
1506            .await;
1507
1508        // Create progress reporter if progress token is provided
1509        #[allow(clippy::used_underscore_binding)] // _meta is part of MCP protocol spec
1510        let progress_reporter = req
1511            ._meta
1512            .as_ref()
1513            .and_then(|meta| meta.progress_token.as_ref())
1514            .and_then(|token| {
1515                self.notification_tx.as_ref().map(|tx| {
1516                    let tx = tx.clone();
1517                    let reporter = crate::server::progress::ServerProgressReporter::new(
1518                        token.clone(),
1519                        Arc::new(move |notification| {
1520                            let _ = tx.try_send(notification);
1521                        }),
1522                    );
1523                    Arc::new(reporter) as Arc<dyn crate::server::progress::ProgressReporter>
1524                })
1525            });
1526
1527        let extra = self.attach_peer(
1528            crate::server::cancellation::RequestHandlerExtra::new(
1529                request_id_str.clone(),
1530                cancellation_token,
1531            )
1532            .with_auth_context(auth_context)
1533            .with_progress_reporter(progress_reporter),
1534        );
1535        let mut result = match handler.read(&req.uri, extra).await {
1536            Ok(v) => {
1537                self.cancellation_manager
1538                    .remove_token(&request_id_str)
1539                    .await;
1540                Ok(v)
1541            },
1542            Err(e) => {
1543                self.cancellation_manager
1544                    .remove_token(&request_id_str)
1545                    .await;
1546                Err(e)
1547            },
1548        }?;
1549        // Merge tool descriptor keys into content _meta for widget resources
1550        if !self.uri_to_tool_meta.is_empty() {
1551            for content in &mut result.contents {
1552                if let crate::types::Content::Resource { uri, meta, .. } = content {
1553                    if let Some(tool_meta) = self.uri_to_tool_meta.get(uri.as_str()) {
1554                        let content_meta = meta.get_or_insert_with(serde_json::Map::new);
1555                        crate::types::ui::deep_merge(content_meta, tool_meta.clone());
1556                    }
1557                }
1558            }
1559        }
1560        Ok(serde_json::to_value(result)?)
1561    }
1562
1563    #[allow(clippy::unused_self)]
1564    fn handle_list_resource_templates(&self, _req: ListResourceTemplatesRequest) -> Result<Value> {
1565        Ok(serde_json::to_value(ListResourceTemplatesResult {
1566            resource_templates: vec![],
1567            next_cursor: None,
1568        })?)
1569    }
1570
1571    async fn handle_create_message(
1572        &self,
1573        request_id: RequestId,
1574        req: crate::types::CreateMessageParams,
1575    ) -> Result<Value> {
1576        let handler = self
1577            .sampling
1578            .as_ref()
1579            .ok_or_else(|| Error::not_found("No sampling handler configured".to_string()))?;
1580
1581        let request_id_str = request_id.to_string();
1582        let cancellation_token = self
1583            .cancellation_manager
1584            .create_token(request_id_str.clone())
1585            .await;
1586        let extra = self.attach_peer(crate::server::cancellation::RequestHandlerExtra::new(
1587            request_id_str.clone(),
1588            cancellation_token,
1589        ));
1590        let result = match handler.create_message(req, extra).await {
1591            Ok(v) => {
1592                self.cancellation_manager
1593                    .remove_token(&request_id_str)
1594                    .await;
1595                Ok(v)
1596            },
1597            Err(e) => {
1598                self.cancellation_manager
1599                    .remove_token(&request_id_str)
1600                    .await;
1601                Err(e)
1602            },
1603        }?;
1604        Ok(serde_json::to_value(result)?)
1605    }
1606
1607    /// Register a root directory or URI that the server has access to.
1608    ///
1609    /// This method allows the server to announce to clients that it has
1610    /// access to specific file system roots or URIs. This is useful for
1611    /// resource handlers that need to expose filesystem access or other
1612    /// URI-based resources.
1613    ///
1614    /// # Arguments
1615    ///
1616    /// * `uri` - The root URI to register (e.g., `file:///home/user/project`)
1617    /// * `name` - Optional human-readable name for the root
1618    ///
1619    /// # Returns
1620    ///
1621    /// An unregister function that can be called to remove the root registration.
1622    ///
1623    /// # Examples
1624    ///
1625    /// ```rust,no_run
1626    /// use pmcp::Server;
1627    ///
1628    /// # async fn example() -> pmcp::Result<()> {
1629    /// let server = Server::builder()
1630    ///     .name("file-server")
1631    ///     .version("1.0.0")
1632    ///     .build()?;
1633    ///
1634    /// // Register a project root
1635    /// let unregister = server.register_root(
1636    ///     "file:///home/user/project",
1637    ///     Some("My Project".to_string())
1638    /// ).await?;
1639    ///
1640    /// // Later, unregister the root
1641    /// unregister();
1642    /// # Ok(())
1643    /// # }
1644    /// ```
1645    pub async fn register_root(
1646        &self,
1647        uri: impl Into<String>,
1648        name: Option<String>,
1649    ) -> Result<impl FnOnce() + Send + 'static> {
1650        let mut roots_manager = self.roots_manager.write().await;
1651        if let Some(tx) = &self.notification_tx {
1652            roots_manager.set_notification_sender({
1653                let tx = tx.clone();
1654                move |server_notification| {
1655                    let _ = tx.try_send(Notification::Server(server_notification));
1656                }
1657            });
1658        }
1659        roots_manager.register_root(uri.into(), name).await
1660    }
1661
1662    /// Get the list of registered roots.
1663    ///
1664    /// Returns a list of all currently registered root URIs and their
1665    /// associated names. Roots are directories or URIs that the server
1666    /// has announced access to.
1667    ///
1668    /// # Returns
1669    ///
1670    /// A vector of `Root` objects containing URI and optional name.
1671    ///
1672    /// # Examples
1673    ///
1674    /// ```rust,no_run
1675    /// use pmcp::Server;
1676    ///
1677    /// # async fn example() -> pmcp::Result<()> {
1678    /// let server = Server::builder()
1679    ///     .name("file-server")
1680    ///     .version("1.0.0")
1681    ///     .build()?;
1682    ///
1683    /// // Register some roots
1684    /// server.register_root("file:///home/user/project1", Some("Project 1".to_string())).await?;
1685    /// server.register_root("file:///home/user/project2", None).await?;
1686    ///
1687    /// // Get the list of roots
1688    /// let roots = server.get_roots().await;
1689    /// println!("Registered {} roots", roots.len());
1690    /// # Ok(())
1691    /// # }
1692    /// ```
1693    pub async fn get_roots(&self) -> Vec<roots::Root> {
1694        let roots_manager = self.roots_manager.read().await;
1695        roots_manager.get_roots().await
1696    }
1697
1698    /// Subscribe a client to resource updates.
1699    ///
1700    /// This method allows the server to track which clients are interested
1701    /// in updates to specific resources. When a resource changes, the server
1702    /// can notify all subscribed clients.
1703    ///
1704    /// # Arguments
1705    ///
1706    /// * `uri` - The resource URI to subscribe to
1707    /// * `client_id` - Identifier for the subscribing client
1708    ///
1709    /// # Examples
1710    ///
1711    /// ```rust,no_run
1712    /// use pmcp::Server;
1713    ///
1714    /// # async fn example() -> pmcp::Result<()> {
1715    /// let server = Server::builder()
1716    ///     .name("file-server")
1717    ///     .version("1.0.0")
1718    ///     .build()?;
1719    ///
1720    /// // Subscribe client to resource updates
1721    /// server.subscribe_resource(
1722    ///     "file:///project/file.txt".to_string(),
1723    ///     "client-123".to_string()
1724    /// ).await?;
1725    /// # Ok(())
1726    /// # }
1727    /// ```
1728    pub async fn subscribe_resource(&self, uri: String, client_id: String) -> Result<()> {
1729        if uri.is_empty() || client_id.is_empty() {
1730            return Err(Error::invalid_params("URI and client_id must not be empty"));
1731        }
1732
1733        let mut subscription_manager = self.subscription_manager.write().await;
1734        if let Some(tx) = &self.notification_tx {
1735            subscription_manager.set_notification_sender({
1736                let tx = tx.clone();
1737                move |notification| {
1738                    let _ = tx.try_send(Notification::Server(notification));
1739                }
1740            });
1741        }
1742
1743        subscription_manager.subscribe(uri, client_id).await
1744    }
1745
1746    /// Cancel a request that is currently being processed.
1747    ///
1748    /// This method allows the server to cancel ongoing requests, which is
1749    /// useful for implementing request timeouts or client-requested cancellations.
1750    ///
1751    /// # Arguments
1752    ///
1753    /// * `request_id` - The ID of the request to cancel
1754    /// * `reason` - Optional reason for cancellation
1755    ///
1756    /// # Examples
1757    ///
1758    /// ```rust,no_run
1759    /// use pmcp::Server;
1760    ///
1761    /// # async fn example() -> pmcp::Result<()> {
1762    /// let server = Server::builder()
1763    ///     .name("cancel-server")
1764    ///     .version("1.0.0")
1765    ///     .build()?;
1766    ///
1767    /// // Cancel a request
1768    /// server.cancel_request(
1769    ///     "request-123".to_string(),
1770    ///     Some("User requested cancellation".to_string())
1771    /// ).await?;
1772    /// # Ok(())
1773    /// # }
1774    /// ```
1775    pub async fn cancel_request(&self, request_id: String, reason: Option<String>) -> Result<()> {
1776        if request_id.is_empty() {
1777            return Err(Error::invalid_params("Request ID must not be empty"));
1778        }
1779
1780        self.cancellation_manager
1781            .cancel_request(request_id, reason)
1782            .await
1783    }
1784
1785    /// Unsubscribe a client from resource updates.
1786    ///
1787    /// This method removes a client's subscription to a specific resource,
1788    /// so they will no longer receive notifications when that resource changes.
1789    ///
1790    /// # Arguments
1791    ///
1792    /// * `uri` - The resource URI to unsubscribe from
1793    /// * `client_id` - Identifier for the client to unsubscribe
1794    ///
1795    /// # Examples
1796    ///
1797    /// ```rust,no_run
1798    /// use pmcp::Server;
1799    ///
1800    /// # async fn example() -> pmcp::Result<()> {
1801    /// let server = Server::builder()
1802    ///     .name("file-server")
1803    ///     .version("1.0.0")
1804    ///     .build()?;
1805    ///
1806    /// // Unsubscribe client from resource updates
1807    /// server.unsubscribe_resource(
1808    ///     "file:///project/file.txt".to_string(),
1809    ///     "client-123".to_string()
1810    /// ).await?;
1811    /// # Ok(())
1812    /// # }
1813    /// ```
1814    pub async fn unsubscribe_resource(&self, uri: String, client_id: String) -> Result<()> {
1815        if uri.is_empty() || client_id.is_empty() {
1816            return Err(Error::invalid_params("URI and client_id must not be empty"));
1817        }
1818
1819        let subscription_manager = self.subscription_manager.read().await;
1820        subscription_manager.unsubscribe(uri, client_id).await
1821    }
1822
1823    /// Notify subscribers that a resource has been updated.
1824    ///
1825    /// # Arguments
1826    ///
1827    /// * `uri` - The URI of the resource that was updated
1828    ///
1829    /// # Returns
1830    ///
1831    /// The number of subscribers that were notified.
1832    pub async fn notify_resource_updated(&self, uri: String) -> Result<usize> {
1833        let mut subscription_manager = self.subscription_manager.write().await;
1834        if let Some(tx) = &self.notification_tx {
1835            subscription_manager.set_notification_sender({
1836                let tx = tx.clone();
1837                move |notification| {
1838                    let _ = tx.try_send(Notification::Server(notification));
1839                }
1840            });
1841        }
1842        subscription_manager.notify_resource_updated(uri).await
1843    }
1844}
1845
1846/// Trait for types annotated with `#[mcp_server]`.
1847///
1848/// Generated by the `#[mcp_server]` proc macro. Provides bulk registration of
1849/// tools and prompts via `register()`. Users should call `.mcp_server(instance)`
1850/// on the builder instead of implementing this trait manually.
1851///
1852/// # Examples
1853///
1854/// ```rust,ignore
1855/// use pmcp::ServerBuilder;
1856///
1857/// #[mcp_server]
1858/// impl MyServer {
1859///     #[mcp_tool(description = "Query data")]
1860///     async fn query(&self, args: QueryArgs) -> Result<Value> { /* ... */ }
1861///
1862///     #[mcp_prompt(description = "Generate query")]
1863///     async fn query_prompt(&self, args: PromptArgs) -> Result<GetPromptResult> { /* ... */ }
1864/// }
1865///
1866/// let server = MyServer { db };
1867/// let builder = ServerBuilder::new()
1868///     .mcp_server(server);
1869/// ```
1870#[cfg(not(target_arch = "wasm32"))]
1871pub trait McpServer {
1872    /// Register all tools and prompts from this server on the builder.
1873    fn register(self, builder: ServerBuilder) -> ServerBuilder;
1874}
1875
1876/// Builder for creating servers.
1877#[cfg(not(target_arch = "wasm32"))]
1878pub struct ServerBuilder {
1879    name: Option<String>,
1880    version: Option<String>,
1881    capabilities: ServerCapabilities,
1882    tools: HashMap<String, Arc<dyn ToolHandler>>,
1883    prompts: HashMap<String, Arc<dyn PromptHandler>>,
1884    resources: Option<Arc<dyn ResourceHandler>>,
1885    sampling: Option<Arc<dyn SamplingHandler>>,
1886    /// Cancellation manager for request cancellation
1887    cancellation_manager: cancellation::CancellationManager,
1888    /// Roots manager for directory/URI registration
1889    roots_manager: roots::RootsManager,
1890    /// Authentication provider for validating requests
1891    auth_provider: Option<Arc<dyn auth::AuthProvider>>,
1892    /// Tool authorizer for fine-grained access control
1893    tool_authorizer: Option<Arc<dyn auth::ToolAuthorizer>>,
1894    /// Tool protection requirements to be applied at build time
1895    tool_protections: HashMap<String, Vec<String>>,
1896    /// Tool middleware chain for cross-cutting concerns
1897    #[cfg(not(target_arch = "wasm32"))]
1898    tool_middlewares: Vec<Arc<dyn tool_middleware::ToolMiddleware>>,
1899    /// HTTP middleware chain for `StreamableHttpServer`
1900    #[cfg(feature = "streamable-http")]
1901    http_middleware: Option<Arc<http_middleware::ServerHttpMiddlewareChain>>,
1902    /// Host layers for MCP Apps metadata enrichment (e.g., `ChatGPT`)
1903    #[cfg(feature = "mcp-apps")]
1904    host_layers: Vec<crate::types::mcp_apps::HostType>,
1905    /// Optional website URL for the server implementation (MCP 2025-11-25)
1906    website_url: Option<String>,
1907    /// Optional icons for the server implementation (MCP 2025-11-25)
1908    icons: Option<Vec<crate::types::protocol::IconInfo>>,
1909    /// Accumulated SEP-2640 Agent Skills. The registry is finalized into a
1910    /// single `SkillsHandler` exactly once at `.build()` time so chained
1911    /// `.skill(...)` / `.skills(...)` calls never produce nested wrappers.
1912    #[cfg(feature = "skills")]
1913    pending_skills: Option<skills::Skills>,
1914}
1915
1916#[cfg(not(target_arch = "wasm32"))]
1917impl std::fmt::Debug for ServerBuilder {
1918    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1919        f.debug_struct("ServerBuilder")
1920            .field("name", &self.name)
1921            .field("version", &self.version)
1922            .field("capabilities", &self.capabilities)
1923            .field("tools", &self.tools.keys().collect::<Vec<_>>())
1924            .field("prompts", &self.prompts.keys().collect::<Vec<_>>())
1925            .field("resources", &self.resources.is_some())
1926            .field("sampling", &self.sampling.is_some())
1927            .finish()
1928    }
1929}
1930
1931#[cfg(not(target_arch = "wasm32"))]
1932impl ServerBuilder {
1933    /// Create a new server builder.
1934    ///
1935    /// Creates a new `ServerBuilder` with default capabilities and no handlers.
1936    /// Use the builder methods to configure the server before calling `build()`.
1937    ///
1938    /// # Examples
1939    ///
1940    /// ```rust,no_run
1941    /// use pmcp::ServerBuilder;
1942    ///
1943    /// let builder = ServerBuilder::new();
1944    /// ```
1945    ///
1946    /// This is equivalent to using the default implementation:
1947    ///
1948    /// ```rust,no_run
1949    /// use pmcp::ServerBuilder;
1950    ///
1951    /// let builder = ServerBuilder::default();
1952    /// ```
1953    pub fn new() -> Self {
1954        Self {
1955            name: None,
1956            version: None,
1957            capabilities: ServerCapabilities::default(),
1958            tools: HashMap::new(),
1959            prompts: HashMap::new(),
1960            resources: None,
1961            sampling: None,
1962            cancellation_manager: cancellation::CancellationManager::new(),
1963            roots_manager: roots::RootsManager::new(),
1964            auth_provider: None,
1965            tool_authorizer: None,
1966            tool_protections: HashMap::new(),
1967            #[cfg(not(target_arch = "wasm32"))]
1968            tool_middlewares: Vec::new(),
1969            #[cfg(feature = "streamable-http")]
1970            http_middleware: None,
1971            #[cfg(feature = "mcp-apps")]
1972            host_layers: Vec::new(),
1973            website_url: None,
1974            icons: None,
1975            #[cfg(feature = "skills")]
1976            pending_skills: None,
1977        }
1978    }
1979
1980    /// Set the server name.
1981    ///
1982    /// The server name identifies this MCP server implementation.
1983    /// This is required and will be sent to clients during initialization.
1984    ///
1985    /// # Arguments
1986    ///
1987    /// * `name` - The name of the server
1988    ///
1989    /// # Examples
1990    ///
1991    /// ```rust,no_run
1992    /// use pmcp::Server;
1993    ///
1994    /// let server = Server::builder()
1995    ///     .name("file-manager")
1996    ///     .version("1.0.0")
1997    ///     .build()?;
1998    /// # Ok::<(), pmcp::Error>(())
1999    /// ```
2000    pub fn name(mut self, name: impl Into<String>) -> Self {
2001        self.name = Some(name.into());
2002        self
2003    }
2004
2005    /// Set the server version.
2006    ///
2007    /// The server version identifies this specific version of the MCP server.
2008    /// This is required and will be sent to clients during initialization.
2009    ///
2010    /// # Arguments
2011    ///
2012    /// * `version` - The version string (e.g., "1.0.0", "2.1.3-beta")
2013    ///
2014    /// # Examples
2015    ///
2016    /// ```rust,no_run
2017    /// use pmcp::Server;
2018    ///
2019    /// let server = Server::builder()
2020    ///     .name("data-processor")
2021    ///     .version("2.1.0")
2022    ///     .build()?;
2023    /// # Ok::<(), pmcp::Error>(())
2024    /// ```
2025    pub fn version(mut self, version: impl Into<String>) -> Self {
2026        self.version = Some(version.into());
2027        self
2028    }
2029
2030    /// Set the website URL for the server implementation (MCP 2025-11-25).
2031    pub fn website_url(mut self, url: impl Into<String>) -> Self {
2032        self.website_url = Some(url.into());
2033        self
2034    }
2035
2036    /// Set icons for the server implementation (MCP 2025-11-25).
2037    pub fn with_icons(mut self, icons: Vec<crate::types::protocol::IconInfo>) -> Self {
2038        self.icons = Some(icons);
2039        self
2040    }
2041
2042    /// Set server capabilities.
2043    ///
2044    /// Configures the capabilities that this server supports.
2045    /// Capabilities inform clients about which MCP features are available.
2046    ///
2047    /// # Arguments
2048    ///
2049    /// * `capabilities` - The server capabilities to advertise
2050    ///
2051    /// # Examples
2052    ///
2053    /// ```rust,no_run
2054    /// use pmcp::{Server, ServerCapabilities, ToolCapabilities};
2055    ///
2056    /// let mut capabilities = ServerCapabilities::default();
2057    /// capabilities.tools = Some(ToolCapabilities {
2058    ///     list_changed: Some(true),
2059    /// });
2060    ///
2061    /// let server = Server::builder()
2062    ///     .name("advanced-server")
2063    ///     .version("1.0.0")
2064    ///     .capabilities(capabilities)
2065    ///     .build()?;
2066    /// # Ok::<(), pmcp::Error>(())
2067    /// ```
2068    pub fn capabilities(mut self, capabilities: ServerCapabilities) -> Self {
2069        self.capabilities = capabilities;
2070        self
2071    }
2072
2073    /// Add a tool handler.
2074    ///
2075    /// Registers a tool that clients can call via the tools/call method.
2076    /// Tools are the primary way servers provide functionality to clients.
2077    ///
2078    /// # Arguments
2079    ///
2080    /// * `name` - The name of the tool (used by clients to call it)
2081    /// * `handler` - The handler implementation for this tool
2082    ///
2083    /// # Examples
2084    ///
2085    /// ```rust,no_run
2086    /// use pmcp::{Server, ToolHandler};
2087    /// use async_trait::async_trait;
2088    /// use serde_json::Value;
2089    ///
2090    /// struct FileListTool;
2091    ///
2092    /// #[async_trait]
2093    /// impl ToolHandler for FileListTool {
2094    ///     async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
2095    ///         let path = args["path"].as_str().unwrap_or(".");
2096    ///         // List files in path...
2097    ///         Ok(serde_json::json!({"files": ["file1.txt", "file2.txt"]}))
2098    ///     }
2099    /// }
2100    ///
2101    /// let server = Server::builder()
2102    ///     .name("file-server")
2103    ///     .version("1.0.0")
2104    ///     .tool("list_files", FileListTool{})
2105    ///     .build()?;
2106    /// # Ok::<(), pmcp::Error>(())
2107    /// ```
2108    pub fn tool(mut self, name: impl Into<String>, handler: impl ToolHandler + 'static) -> Self {
2109        self.tools.insert(name.into(), Arc::new(handler));
2110
2111        // Update capabilities to include tools
2112        // Use Some(false) instead of None to ensure the field serializes properly
2113        if self.capabilities.tools.is_none() {
2114            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2115                list_changed: Some(false),
2116            });
2117        }
2118
2119        self
2120    }
2121
2122    /// Add a tool handler with an Arc.
2123    ///
2124    /// This variant lets the caller share the handler `Arc` between the
2125    /// builder and an external in-process handler map (e.g., a downstream
2126    /// toolkit's handler registry) without writing a delegating wrapper
2127    /// shim. Behavior is otherwise identical to [`Self::tool`]: the first
2128    /// registration auto-enables `capabilities.tools`.
2129    pub fn tool_arc(mut self, name: impl Into<String>, handler: Arc<dyn ToolHandler>) -> Self {
2130        let name = name.into();
2131        self.tools.insert(name, handler);
2132
2133        // Update capabilities to include tools
2134        // Use Some(false) instead of None to ensure the field serializes properly
2135        if self.capabilities.tools.is_none() {
2136            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2137                list_changed: Some(false),
2138            });
2139        }
2140
2141        self
2142    }
2143
2144    /// Register all tools and prompts from an `#[mcp_server]` annotated type.
2145    ///
2146    /// This is the ergonomic counterpart to individually registering tools and
2147    /// prompts. The server instance provides shared state via `&self` to all
2148    /// tool and prompt methods.
2149    ///
2150    /// # Examples
2151    ///
2152    /// ```rust,ignore
2153    /// use pmcp::ServerBuilder;
2154    ///
2155    /// #[mcp_server]
2156    /// impl MyServer {
2157    ///     #[mcp_tool(description = "Query data")]
2158    ///     async fn query(&self, args: QueryArgs) -> Result<Value> { /* ... */ }
2159    ///
2160    ///     #[mcp_prompt(description = "Generate query")]
2161    ///     async fn query_prompt(&self, args: PromptArgs) -> Result<GetPromptResult> { /* ... */ }
2162    /// }
2163    ///
2164    /// let server = MyServer { db };
2165    /// let builder = ServerBuilder::new()
2166    ///     .name("my-server")
2167    ///     .mcp_server(server);
2168    /// ```
2169    pub fn mcp_server<T: McpServer>(self, server: T) -> Self {
2170        server.register(self)
2171    }
2172
2173    /// Add a type-safe tool handler with automatic schema generation.
2174    ///
2175    /// This method provides first-class support for creating tools with:
2176    /// - Automatic JSON schema generation from Rust types
2177    /// - Compile-time type safety
2178    /// - Runtime validation
2179    /// - Field descriptions from doc comments
2180    ///
2181    /// # Example
2182    /// ```no_run
2183    /// # #[cfg(feature = "schema-generation")]
2184    /// # {
2185    /// use pmcp::ServerBuilder;
2186    /// use schemars::JsonSchema;
2187    /// use serde::{Deserialize, Serialize};
2188    ///
2189    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
2190    /// struct EchoArgs {
2191    ///     /// The message to echo
2192    ///     message: String,
2193    ///     /// Optional prefix
2194    ///     prefix: Option<String>,
2195    /// }
2196    ///
2197    /// # #[tokio::main]
2198    /// # async fn main() -> Result<(), pmcp::Error> {
2199    /// let server = ServerBuilder::new()
2200    ///     .name("example")
2201    ///     .tool_typed("echo", |args: EchoArgs, _| {
2202    ///         Box::pin(async move {
2203    ///             let message = match args.prefix {
2204    ///                 Some(p) => format!("{}: {}", p, args.message),
2205    ///                 None => args.message,
2206    ///             };
2207    ///             Ok(serde_json::json!({ "message": message }))
2208    ///         })
2209    ///     })
2210    ///     .build();
2211    /// # Ok::<(), pmcp::Error>(())
2212    /// # }
2213    /// # }
2214    /// ```
2215    #[cfg(feature = "schema-generation")]
2216    pub fn tool_typed<T, F, Fut>(mut self, name: impl Into<String>, handler: F) -> Self
2217    where
2218        T: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2219        F: Fn(T, crate::RequestHandlerExtra) -> Fut + Send + Sync + 'static,
2220        Fut: std::future::Future<Output = crate::Result<serde_json::Value>> + Send + 'static,
2221    {
2222        use crate::server::typed_tool::TypedTool;
2223        use std::pin::Pin;
2224
2225        let name_str = name.into();
2226
2227        // Wrap the handler to return Pin<Box<dyn Future>>
2228        let wrapped_handler = move |args: T,
2229                                    extra: crate::RequestHandlerExtra|
2230              -> Pin<
2231            Box<dyn std::future::Future<Output = crate::Result<serde_json::Value>> + Send>,
2232        > { Box::pin(handler(args, extra)) };
2233
2234        let tool = TypedTool::new(name_str.clone(), wrapped_handler);
2235        self.tools.insert(name_str, Arc::new(tool));
2236
2237        // Update capabilities to include tools
2238        if self.capabilities.tools.is_none() {
2239            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2240                list_changed: Some(false),
2241            });
2242        }
2243
2244        self
2245    }
2246
2247    /// Add a type-safe tool handler with automatic schema generation and description.
2248    ///
2249    /// This is a convenience overload that allows setting a description directly
2250    /// without needing to chain `.with_description()`.
2251    ///
2252    /// # Example
2253    /// ```no_run
2254    /// # #[cfg(feature = "schema-generation")]
2255    /// # {
2256    /// use pmcp::ServerBuilder;
2257    /// use schemars::JsonSchema;
2258    /// use serde::{Deserialize, Serialize};
2259    ///
2260    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
2261    /// struct EchoArgs {
2262    ///     /// The message to echo
2263    ///     message: String,
2264    ///     /// Optional prefix
2265    ///     prefix: Option<String>,
2266    /// }
2267    ///
2268    /// let server = ServerBuilder::new()
2269    ///     .name("example")
2270    ///     .tool_typed_with_description(
2271    ///         "echo",
2272    ///         "Echoes back a message with an optional prefix",
2273    ///         |args: EchoArgs, _| {
2274    ///             Box::pin(async move {
2275    ///                 let message = match args.prefix {
2276    ///                     Some(p) => format!("{}: {}", p, args.message),
2277    ///                     None => args.message,
2278    ///                 };
2279    ///                 Ok(serde_json::json!({ "message": message }))
2280    ///             })
2281    ///         }
2282    ///     );
2283    /// # }
2284    /// ```
2285    #[cfg(feature = "schema-generation")]
2286    pub fn tool_typed_with_description<T, F, Fut>(
2287        mut self,
2288        name: impl Into<String>,
2289        description: impl Into<String>,
2290        handler: F,
2291    ) -> Self
2292    where
2293        T: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2294        F: Fn(T, crate::RequestHandlerExtra) -> Fut + Send + Sync + 'static,
2295        Fut: std::future::Future<Output = crate::Result<serde_json::Value>> + Send + 'static,
2296    {
2297        use crate::server::typed_tool::TypedTool;
2298        use std::pin::Pin;
2299
2300        let name_str = name.into();
2301
2302        // Wrap the handler to return Pin<Box<dyn Future>>
2303        let wrapped_handler = move |args: T,
2304                                    extra: crate::RequestHandlerExtra|
2305              -> Pin<
2306            Box<dyn std::future::Future<Output = crate::Result<serde_json::Value>> + Send>,
2307        > { Box::pin(handler(args, extra)) };
2308
2309        let tool = TypedTool::new(name_str.clone(), wrapped_handler).with_description(description);
2310        self.tools.insert(name_str, Arc::new(tool));
2311
2312        // Update capabilities to include tools
2313        if self.capabilities.tools.is_none() {
2314            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2315                list_changed: Some(false),
2316            });
2317        }
2318
2319        self
2320    }
2321
2322    /// Add a synchronous type-safe tool handler with automatic schema generation.
2323    ///
2324    /// Similar to `tool_typed` but for synchronous handlers.
2325    ///
2326    /// # Example
2327    /// ```no_run
2328    /// # #[cfg(feature = "schema-generation")]
2329    /// # {
2330    /// use pmcp::ServerBuilder;
2331    /// use schemars::JsonSchema;
2332    /// use serde::{Deserialize, Serialize};
2333    ///
2334    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
2335    /// struct MathArgs {
2336    ///     /// First number
2337    ///     a: f64,
2338    ///     /// Second number
2339    ///     b: f64,
2340    ///     /// Operation to perform
2341    ///     op: String,
2342    /// }
2343    ///
2344    /// # fn main() -> Result<(), pmcp::Error> {
2345    /// let server = ServerBuilder::new()
2346    ///     .name("example")
2347    ///     .tool_typed_sync("calculator", |args: MathArgs, _| {
2348    ///         let result = match args.op.as_str() {
2349    ///             "add" => args.a + args.b,
2350    ///             "subtract" => args.a - args.b,
2351    ///             "multiply" => args.a * args.b,
2352    ///             "divide" => args.a / args.b,
2353    ///             _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
2354    ///         };
2355    ///         Ok(serde_json::json!({ "result": result }))
2356    ///     })
2357    ///     .build();
2358    /// # Ok::<(), pmcp::Error>(())
2359    /// # }
2360    /// # }
2361    /// ```
2362    #[cfg(feature = "schema-generation")]
2363    pub fn tool_typed_sync<T, F>(mut self, name: impl Into<String>, handler: F) -> Self
2364    where
2365        T: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2366        F: Fn(T, crate::RequestHandlerExtra) -> crate::Result<serde_json::Value>
2367            + Send
2368            + Sync
2369            + 'static,
2370    {
2371        use crate::server::typed_tool::TypedSyncTool;
2372        let name_str = name.into();
2373        let tool = TypedSyncTool::new(name_str.clone(), handler);
2374        self.tools.insert(name_str, Arc::new(tool));
2375
2376        // Update capabilities to include tools
2377        if self.capabilities.tools.is_none() {
2378            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2379                list_changed: Some(false),
2380            });
2381        }
2382
2383        self
2384    }
2385
2386    /// Add a synchronous type-safe tool handler with automatic schema generation and description.
2387    ///
2388    /// This is a convenience overload that allows setting a description directly
2389    /// without needing to chain `.with_description()`.
2390    ///
2391    /// # Example
2392    /// ```no_run
2393    /// # #[cfg(feature = "schema-generation")]
2394    /// # {
2395    /// use pmcp::ServerBuilder;
2396    /// use schemars::JsonSchema;
2397    /// use serde::{Deserialize, Serialize};
2398    ///
2399    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
2400    /// struct MathArgs {
2401    ///     /// First number
2402    ///     a: f64,
2403    ///     /// Second number
2404    ///     b: f64,
2405    ///     /// Operation to perform
2406    ///     op: String,
2407    /// }
2408    ///
2409    /// let server = ServerBuilder::new()
2410    ///     .name("example")
2411    ///     .tool_typed_sync_with_description(
2412    ///         "calculator",
2413    ///         "Performs synchronous mathematical operations",
2414    ///         |args: MathArgs, _| {
2415    ///             let result = match args.op.as_str() {
2416    ///                 "add" => args.a + args.b,
2417    ///                 "subtract" => args.a - args.b,
2418    ///                 "multiply" => args.a * args.b,
2419    ///                 "divide" => args.a / args.b,
2420    ///                 _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
2421    ///             };
2422    ///             Ok(serde_json::json!({ "result": result }))
2423    ///         }
2424    ///     );
2425    /// # }
2426    /// ```
2427    #[cfg(feature = "schema-generation")]
2428    pub fn tool_typed_sync_with_description<T, F>(
2429        mut self,
2430        name: impl Into<String>,
2431        description: impl Into<String>,
2432        handler: F,
2433    ) -> Self
2434    where
2435        T: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2436        F: Fn(T, crate::RequestHandlerExtra) -> crate::Result<serde_json::Value>
2437            + Send
2438            + Sync
2439            + 'static,
2440    {
2441        use crate::server::typed_tool::TypedSyncTool;
2442        let name_str = name.into();
2443        let tool = TypedSyncTool::new(name_str.clone(), handler).with_description(description);
2444        self.tools.insert(name_str, Arc::new(tool));
2445
2446        // Update capabilities to include tools
2447        if self.capabilities.tools.is_none() {
2448            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2449                list_changed: Some(false),
2450            });
2451        }
2452
2453        self
2454    }
2455
2456    /// Add a type-safe tool handler with both input and output typing.
2457    ///
2458    /// This method provides full type safety for both input and output types,
2459    /// which is useful for testing, documentation, and API contracts.
2460    /// Note that output schemas are not part of the MCP protocol but can be
2461    /// valuable for development and integration testing.
2462    ///
2463    /// # Type Parameters
2464    ///
2465    /// * `TIn` - Input type that implements `JsonSchema`, `Deserialize`, `Send`, `Sync`
2466    /// * `TOut` - Output type that implements `JsonSchema`, `Serialize`, `Send`, `Sync`
2467    ///
2468    /// # Example
2469    /// ```no_run
2470    /// # #[cfg(feature = "schema-generation")]
2471    /// # {
2472    /// use pmcp::{ServerBuilder, TypedToolWithOutput};
2473    /// use schemars::JsonSchema;
2474    /// use serde::{Deserialize, Serialize};
2475    ///
2476    /// #[derive(JsonSchema, Deserialize)]
2477    /// struct MathInput { a: f64, b: f64, op: String }
2478    ///
2479    /// #[derive(JsonSchema, Serialize)]
2480    /// struct MathOutput { result: f64, operation: String }
2481    ///
2482    /// let server = ServerBuilder::new()
2483    ///     .name("example")
2484    ///     .tool_typed_with_output::<MathInput, MathOutput>("math", |args, _| {
2485    ///         Box::pin(async move {
2486    ///             let result = match args.op.as_str() {
2487    ///                 "add" => args.a + args.b,
2488    ///                 "subtract" => args.a - args.b,
2489    ///                 _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
2490    ///             };
2491    ///             Ok(MathOutput {
2492    ///                 result,
2493    ///                 operation: args.op,
2494    ///             })
2495    ///         })
2496    ///     });
2497    /// # }
2498    /// ```
2499    #[cfg(feature = "schema-generation")]
2500    pub fn tool_typed_with_output<TIn, TOut>(
2501        mut self,
2502        name: impl Into<String>,
2503        handler: impl Fn(
2504                TIn,
2505                crate::RequestHandlerExtra,
2506            )
2507                -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::Result<TOut>> + Send>>
2508            + Send
2509            + Sync
2510            + 'static,
2511    ) -> Self
2512    where
2513        TIn: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2514        TOut: serde::Serialize + schemars::JsonSchema + Send + Sync + 'static,
2515    {
2516        use crate::server::typed_tool::TypedToolWithOutput;
2517
2518        let name_str = name.into();
2519        let tool = TypedToolWithOutput::new(name_str.clone(), handler);
2520        self.tools.insert(name_str, Arc::new(tool));
2521
2522        // Update capabilities to include tools
2523        if self.capabilities.tools.is_none() {
2524            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2525                list_changed: Some(false),
2526            });
2527        }
2528
2529        self
2530    }
2531
2532    /// Add a type-safe tool handler with both input and output typing and description.
2533    ///
2534    /// This is a convenience overload that allows setting a description directly
2535    /// without needing to chain `.with_description()`.
2536    ///
2537    /// # Example
2538    /// ```no_run
2539    /// # #[cfg(feature = "schema-generation")]
2540    /// # {
2541    /// use pmcp::ServerBuilder;
2542    /// use schemars::JsonSchema;
2543    /// use serde::{Deserialize, Serialize};
2544    ///
2545    /// #[derive(JsonSchema, Deserialize)]
2546    /// struct MathInput { a: f64, b: f64, op: String }
2547    ///
2548    /// #[derive(JsonSchema, Serialize)]
2549    /// struct MathOutput { result: f64, operation: String }
2550    ///
2551    /// let server = ServerBuilder::new()
2552    ///     .name("example")
2553    ///     .tool_typed_with_output_and_description::<MathInput, MathOutput>(
2554    ///         "math",
2555    ///         "Performs basic mathematical operations on two numbers",
2556    ///         |args, _| {
2557    ///             Box::pin(async move {
2558    ///                 let result = match args.op.as_str() {
2559    ///                     "add" => args.a + args.b,
2560    ///                     "subtract" => args.a - args.b,
2561    ///                     _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
2562    ///                 };
2563    ///                 Ok(MathOutput { result, operation: args.op })
2564    ///             })
2565    ///         }
2566    ///     );
2567    /// # }
2568    /// ```
2569    #[cfg(feature = "schema-generation")]
2570    pub fn tool_typed_with_output_and_description<TIn, TOut>(
2571        mut self,
2572        name: impl Into<String>,
2573        description: impl Into<String>,
2574        handler: impl Fn(
2575                TIn,
2576                crate::RequestHandlerExtra,
2577            )
2578                -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::Result<TOut>> + Send>>
2579            + Send
2580            + Sync
2581            + 'static,
2582    ) -> Self
2583    where
2584        TIn: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync + 'static,
2585        TOut: serde::Serialize + schemars::JsonSchema + Send + Sync + 'static,
2586    {
2587        use crate::server::typed_tool::TypedToolWithOutput;
2588
2589        let name_str = name.into();
2590        let tool =
2591            TypedToolWithOutput::new(name_str.clone(), handler).with_description(description);
2592        self.tools.insert(name_str, Arc::new(tool));
2593
2594        // Update capabilities to include tools
2595        if self.capabilities.tools.is_none() {
2596            self.capabilities.tools = Some(crate::types::ToolCapabilities {
2597                list_changed: Some(false),
2598            });
2599        }
2600
2601        self
2602    }
2603
2604    /// Add a prompt handler.
2605    ///
2606    /// Registers a prompt that clients can retrieve via the prompts/get method.
2607    /// Prompts provide templates that clients can use for various tasks.
2608    ///
2609    /// # Arguments
2610    ///
2611    /// * `name` - The name of the prompt (used by clients to retrieve it)
2612    /// * `handler` - The handler implementation for this prompt
2613    ///
2614    /// # Examples
2615    ///
2616    /// ```rust,no_run
2617    /// use pmcp::{Server, PromptHandler, GetPromptResult, PromptMessage, Content};
2618    /// use async_trait::async_trait;
2619    /// use std::collections::HashMap;
2620    ///
2621    /// struct CodeReviewPrompt;
2622    ///
2623    /// #[async_trait]
2624    /// impl PromptHandler for CodeReviewPrompt {
2625    ///     async fn handle(&self, args: HashMap<String, String>, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<GetPromptResult> {
2626    ///         let language = args.get("language").map(|s| s.as_str()).unwrap_or("unknown");
2627    ///         Ok(GetPromptResult::new(
2628    ///             vec![PromptMessage::user(pmcp::Content::text(format!(
2629    ///                 "Please review this {} code:",
2630    ///                 language
2631    ///             )))],
2632    ///             Some(format!("Code review prompt for {}", language)),
2633    ///         ))
2634    ///     }
2635    /// }
2636    ///
2637    /// let server = Server::builder()
2638    ///     .name("code-server")
2639    ///     .version("1.0.0")
2640    ///     .prompt("code_review", CodeReviewPrompt{})
2641    ///     .build()?;
2642    /// # Ok::<(), pmcp::Error>(())
2643    /// ```
2644    pub fn prompt(
2645        mut self,
2646        name: impl Into<String>,
2647        handler: impl PromptHandler + 'static,
2648    ) -> Self {
2649        self.prompts.insert(name.into(), Arc::new(handler));
2650
2651        // Update capabilities to include prompts
2652        // Use Some(false) instead of None to ensure the field serializes properly
2653        if self.capabilities.prompts.is_none() {
2654            self.capabilities.prompts = Some(crate::types::PromptCapabilities {
2655                list_changed: Some(false),
2656            });
2657        }
2658
2659        self
2660    }
2661
2662    /// Add a prompt handler with an Arc.
2663    ///
2664    /// This variant lets the caller share the handler `Arc` between the
2665    /// builder and an external in-process handler map (e.g., a downstream
2666    /// toolkit's handler registry) without writing a delegating wrapper
2667    /// shim. Behavior is otherwise identical to [`Self::prompt`]: the first
2668    /// registration auto-enables `capabilities.prompts`.
2669    pub fn prompt_arc(mut self, name: impl Into<String>, handler: Arc<dyn PromptHandler>) -> Self {
2670        let name = name.into();
2671        self.prompts.insert(name, handler);
2672
2673        // Update capabilities to include prompts
2674        // Use Some(false) instead of None to ensure the field serializes properly
2675        if self.capabilities.prompts.is_none() {
2676            self.capabilities.prompts = Some(crate::types::PromptCapabilities {
2677                list_changed: Some(false),
2678            });
2679        }
2680
2681        self
2682    }
2683
2684    /// Register a workflow-based prompt with automatic validation.
2685    ///
2686    /// This method validates the workflow before registration and converts it
2687    /// to a prompt handler. The workflow's instructions become the prompt messages,
2688    /// and the workflow's arguments become the prompt arguments.
2689    ///
2690    /// # Arguments
2691    ///
2692    /// * `workflow` - The workflow definition to register as a prompt
2693    ///
2694    /// # Errors
2695    ///
2696    /// Returns an error if the workflow validation fails (e.g., undefined bindings,
2697    /// undefined prompt arguments, etc.).
2698    ///
2699    /// # Examples
2700    ///
2701    /// ```rust,no_run
2702    /// use pmcp::{Server, ServerBuilder};
2703    /// use pmcp::server::workflow::{SequentialWorkflow, InternalPromptMessage};
2704    /// use pmcp::types::Role;
2705    ///
2706    /// # fn main() -> pmcp::Result<()> {
2707    /// let workflow = SequentialWorkflow::new(
2708    ///     "code_review_workflow",
2709    ///     "Review code with multiple steps"
2710    /// )
2711    /// .argument("code", "Code to review", true)
2712    /// .instruction(InternalPromptMessage::new(
2713    ///     Role::System,
2714    ///     "You are a code reviewer. Review the provided code carefully."
2715    /// ));
2716    ///
2717    /// let server = Server::builder()
2718    ///     .name("code-server")
2719    ///     .version("1.0.0")
2720    ///     .prompt_workflow(workflow)?
2721    ///     .build()?;
2722    /// # Ok(())
2723    /// # }
2724    /// ```
2725    pub fn prompt_workflow(mut self, workflow: workflow::SequentialWorkflow) -> Result<Self> {
2726        // Validate the workflow before registration
2727        workflow
2728            .validate()
2729            .map_err(|e| Error::Validation(format!("Workflow validation failed: {}", e)))?;
2730
2731        // Build tool and resource registries from currently registered handlers
2732        // Note: This captures the current state of registered tools/resources
2733        let mut tools = std::collections::HashMap::new();
2734        for (name, handler) in &self.tools {
2735            if let Some(metadata) = handler.metadata() {
2736                tools.insert(
2737                    Arc::from(name.as_str()),
2738                    workflow::conversion::ToolInfo {
2739                        name: metadata.name,
2740                        description: metadata.description.unwrap_or_default(),
2741                        input_schema: metadata.input_schema,
2742                    },
2743                );
2744            }
2745        }
2746
2747        // Build tool handlers map for workflow execution
2748        // Clone Arc references for shared ownership
2749        let mut tool_handlers: std::collections::HashMap<Arc<str>, Arc<dyn ToolHandler>> =
2750            std::collections::HashMap::new();
2751        for (name, handler) in &self.tools {
2752            tool_handlers.insert(Arc::from(name.as_str()), Arc::clone(handler));
2753        }
2754
2755        // Get the workflow name before moving it
2756        let name = workflow.name().to_string();
2757
2758        // Create workflow prompt handler with tool execution and resource fetching capability
2759        // Note: Workflow prompts in ServerBuilder do not currently execute tool middleware.
2760        // For middleware support in workflow tool execution, use ServerCoreBuilder.
2761        let handler = workflow::WorkflowPromptHandler::new(
2762            workflow,
2763            tools,
2764            tool_handlers,
2765            self.resources.clone(),
2766        );
2767
2768        // Register as a prompt
2769        self.prompts.insert(name, Arc::new(handler));
2770
2771        // Update capabilities to include prompts
2772        // This ensures prompts/list returns the workflow prompts
2773        if self.capabilities.prompts.is_none() {
2774            self.capabilities.prompts = Some(crate::types::PromptCapabilities {
2775                list_changed: Some(false),
2776            });
2777        }
2778
2779        Ok(self)
2780    }
2781
2782    /// Set the resource handler.
2783    ///
2784    /// Registers a resource handler that provides access to server resources.
2785    /// Resources allow clients to read files, configurations, or other data.
2786    ///
2787    /// # Arguments
2788    ///
2789    /// * `handler` - The resource handler implementation
2790    ///
2791    /// # Examples
2792    ///
2793    /// ```rust,no_run
2794    /// use pmcp::{Server, ResourceHandler, ReadResourceResult, ListResourcesResult, ResourceInfo};
2795    /// use async_trait::async_trait;
2796    ///
2797    /// struct FileResourceHandler;
2798    ///
2799    /// #[async_trait]
2800    /// impl ResourceHandler for FileResourceHandler {
2801    ///     async fn read(&self, uri: &str, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<ReadResourceResult> {
2802    ///         // Read file content...
2803    ///         Ok(ReadResourceResult::new(vec![pmcp::Content::text("File content here")]))
2804    ///     }
2805    ///
2806    ///     async fn list(&self, _cursor: Option<String>, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<ListResourcesResult> {
2807    ///         Ok(ListResourcesResult::new(vec![
2808    ///             pmcp::ResourceInfo::new("file://example.txt", "example.txt")
2809    ///                 .with_description("Example file")
2810    ///                 .with_mime_type("text/plain"),
2811    ///         ]))
2812    ///     }
2813    /// }
2814    ///
2815    /// let server = Server::builder()
2816    ///     .name("file-server")
2817    ///     .version("1.0.0")
2818    ///     .resources(FileResourceHandler{})
2819    ///     .build()?;
2820    /// # Ok::<(), pmcp::Error>(())
2821    /// ```
2822    pub fn resources(mut self, handler: impl ResourceHandler + 'static) -> Self {
2823        self.resources = Some(Arc::new(handler));
2824
2825        // Update capabilities to include resources
2826        // Use Some(false) instead of None to ensure fields serialize properly
2827        if self.capabilities.resources.is_none() {
2828            self.capabilities.resources = Some(crate::types::ResourceCapabilities {
2829                subscribe: Some(false),
2830                list_changed: Some(false),
2831            });
2832        }
2833
2834        self
2835    }
2836
2837    /// Set the resource handler with an Arc.
2838    ///
2839    /// This variant lets the caller share the handler `Arc` between the
2840    /// builder and an external in-process handler map without writing a
2841    /// delegating wrapper. Behavior is otherwise identical to
2842    /// [`Self::resources`]: the first registration auto-enables
2843    /// `capabilities.resources`.
2844    pub fn resources_arc(mut self, handler: Arc<dyn ResourceHandler>) -> Self {
2845        self.resources = Some(handler);
2846
2847        // Update capabilities to include resources
2848        // Use Some(false) instead of None to ensure fields serialize properly
2849        if self.capabilities.resources.is_none() {
2850            self.capabilities.resources = Some(crate::types::ResourceCapabilities {
2851                subscribe: Some(false),
2852                list_changed: Some(false),
2853            });
2854        }
2855
2856        self
2857    }
2858
2859    /// Register a single SEP-2640 Agent Skill.
2860    ///
2861    /// Convenience over [`Self::skills`] for the single-skill case. The skill
2862    /// is accumulated and finalized into a `SkillsHandler` exactly once at
2863    /// [`Self::build`] time, then composed with any `.resources(...)`
2864    /// handler set on this builder.
2865    ///
2866    /// # Panics
2867    ///
2868    /// Panics at `.build()` time if multiple registered skills resolve to
2869    /// the same `skill://` URI. Use [`Self::try_skills`] with a pre-built
2870    /// [`skills::Skills`] registry to surface duplicates as a `Result`.
2871    ///
2872    /// # Examples
2873    ///
2874    /// ```rust,no_run
2875    /// # #[cfg(feature = "skills")] {
2876    /// use pmcp::{Server, server::skills::Skill};
2877    ///
2878    /// # fn example() -> pmcp::Result<()> {
2879    /// let server = Server::builder()
2880    ///     .name("my-server")
2881    ///     .version("1.0.0")
2882    ///     .skill(Skill::new("hello", "# Hello skill"))
2883    ///     .build()?;
2884    /// # Ok(())
2885    /// # }
2886    /// # }
2887    /// ```
2888    #[cfg(feature = "skills")]
2889    #[must_use]
2890    pub fn skill(self, skill: skills::Skill) -> Self {
2891        self.skills(skills::Skills::new().add(skill))
2892    }
2893
2894    /// Register a registry of SEP-2640 Agent Skills.
2895    ///
2896    /// Merges into any prior accumulated skills (a previous `.skill(...)` or
2897    /// `.skills(...)` call). The accumulated registry is finalized into a
2898    /// single `SkillsHandler` exactly once at [`Self::build`] time, then
2899    /// composed at most once with any `.resources(...)` handler.
2900    ///
2901    /// # Panics
2902    ///
2903    /// Panics at `.build()` if two registered skills resolve to the same
2904    /// `skill://` URI. Use [`Self::try_skills`] for fallible registration.
2905    #[cfg(feature = "skills")]
2906    #[must_use]
2907    pub fn skills(mut self, skills_registry: skills::Skills) -> Self {
2908        let merged = match self.pending_skills.take() {
2909            Some(prior) => prior.merge(skills_registry),
2910            None => skills_registry,
2911        };
2912        self.pending_skills = Some(merged);
2913        skills::set_skills_capabilities(&mut self.capabilities);
2914        self
2915    }
2916
2917    /// Fallible variant of [`Self::skills`] — returns `Err` immediately if
2918    /// the merged registry would contain duplicate URIs. Useful for
2919    /// runtime-dynamic registration where panicking is unacceptable.
2920    ///
2921    /// # Errors
2922    ///
2923    /// Returns `Err(pmcp::Error::Validation)` if the merged registry would
2924    /// produce duplicate `skill://` URIs.
2925    #[cfg(feature = "skills")]
2926    pub fn try_skills(mut self, skills_registry: skills::Skills) -> Result<Self> {
2927        let merged = match self.pending_skills.take() {
2928            Some(prior) => prior.merge(skills_registry),
2929            None => skills_registry,
2930        };
2931        // Probe by cloning + into_handler; discard the handler. The real
2932        // construction happens in `.build()` once everything is settled.
2933        merged.clone().into_handler()?;
2934        self.pending_skills = Some(merged);
2935        skills::set_skills_capabilities(&mut self.capabilities);
2936        Ok(self)
2937    }
2938
2939    /// Register a skill AND a parallel prompt that returns the same content.
2940    ///
2941    /// The dual-surface bootstrap: both surfaces are derived from one
2942    /// [`skills::Skill`] value so they cannot drift. The byte-equality
2943    /// between surfaces is asserted by the skills integration test.
2944    #[cfg(feature = "skills")]
2945    #[must_use]
2946    pub fn bootstrap_skill_and_prompt(
2947        self,
2948        skill: skills::Skill,
2949        prompt_name: impl Into<String>,
2950    ) -> Self {
2951        let prompt_handler = skills::SkillPromptHandler::new(skill.clone());
2952        self.skill(skill).prompt(prompt_name, prompt_handler)
2953    }
2954
2955    /// Set the sampling handler.
2956    ///
2957    /// Registers a sampling handler that provides LLM functionality.
2958    /// This allows the server to act as a language model provider.
2959    ///
2960    /// # Arguments
2961    ///
2962    /// * `handler` - The sampling handler implementation
2963    ///
2964    /// # Examples
2965    ///
2966    /// ```rust,no_run
2967    /// use pmcp::{Server, SamplingHandler, CreateMessageParams, CreateMessageResult};
2968    /// use async_trait::async_trait;
2969    ///
2970    /// struct MockLLM;
2971    ///
2972    /// #[async_trait]
2973    /// impl SamplingHandler for MockLLM {
2974    ///     async fn create_message(&self, params: CreateMessageParams, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<CreateMessageResult> {
2975    ///         // Process the messages and generate a response
2976    ///         Ok(CreateMessageResult::new(pmcp::Content::text("Generated response"), "mock-llm-v1")
2977    ///             .with_usage(pmcp::TokenUsage::new(10, 5, 15))
2978    ///             .with_stop_reason("end_of_text"))
2979    ///     }
2980    /// }
2981    ///
2982    /// let server = Server::builder()
2983    ///     .name("llm-server")
2984    ///     .version("1.0.0")
2985    ///     .sampling(MockLLM{})
2986    ///     .build()?;
2987    /// # Ok::<(), pmcp::Error>(())
2988    /// ```
2989    pub fn sampling(mut self, handler: impl SamplingHandler + 'static) -> Self {
2990        self.sampling = Some(Arc::new(handler));
2991        // Enable sampling capability
2992        self.capabilities.sampling = Some(crate::types::SamplingCapabilities::default());
2993        self
2994    }
2995
2996    /// Set the sampling handler with an Arc.
2997    ///
2998    /// This variant lets the caller share the handler `Arc` between the
2999    /// builder and an external in-process handler map without writing a
3000    /// delegating wrapper. Uses the donor's `if is_none` capability
3001    /// auto-enable so an explicit prior `.capabilities(custom)` is not
3002    /// clobbered by a later `_arc` registration.
3003    pub fn sampling_arc(mut self, handler: Arc<dyn SamplingHandler>) -> Self {
3004        self.sampling = Some(handler);
3005
3006        // Update capabilities to include sampling
3007        if self.capabilities.sampling.is_none() {
3008            self.capabilities.sampling = Some(crate::types::SamplingCapabilities::default());
3009        }
3010
3011        self
3012    }
3013
3014    /// Build the server.
3015    ///
3016    /// Constructs the final Server instance from the configured builder.
3017    /// This validates that required fields (name and version) are set.
3018    ///
3019    /// # Examples
3020    ///
3021    /// ```rust,no_run
3022    /// use pmcp::{Server, ToolHandler};
3023    /// use async_trait::async_trait;
3024    /// use serde_json::Value;
3025    ///
3026    /// struct PingTool;
3027    ///
3028    /// #[async_trait]
3029    /// impl ToolHandler for PingTool {
3030    ///     async fn handle(&self, _args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
3031    ///         Ok(serde_json::json!({"response": "pong"}))
3032    ///     }
3033    /// }
3034    ///
3035    /// let server = Server::builder()
3036    ///     .name("ping-server")
3037    ///     .version("1.0.0")
3038    ///     .tool("ping", PingTool{})
3039    ///     .build()?;
3040    ///
3041    /// // Server is now ready to run
3042    /// // server.run_stdio().await?;
3043    /// # Ok::<(), pmcp::Error>(())
3044    /// ```
3045    /// Set the authentication provider.
3046    ///
3047    /// Configures an authentication provider that will validate incoming requests.
3048    /// When set, the server will use this provider to authenticate requests before
3049    /// processing them.
3050    ///
3051    /// # Arguments
3052    ///
3053    /// * `provider` - The authentication provider implementation
3054    ///
3055    /// # Examples
3056    ///
3057    /// ```rust,no_run
3058    /// use pmcp::{Server, auth::ProxyProvider};
3059    ///
3060    /// let auth_provider = ProxyProvider::with_upstream("https://oauth.example.com");
3061    ///
3062    /// let server = Server::builder()
3063    ///     .name("secure-server")
3064    ///     .version("1.0.0")
3065    ///     .auth_provider(auth_provider)
3066    ///     .build()?;
3067    /// # Ok::<(), pmcp::Error>(())
3068    /// ```
3069    pub fn auth_provider(mut self, provider: impl auth::AuthProvider + 'static) -> Self {
3070        self.auth_provider = Some(Arc::new(provider));
3071        self
3072    }
3073
3074    /// Set the authentication provider with an Arc.
3075    ///
3076    /// This variant lets the caller share the provider `Arc` between the
3077    /// builder and an external in-process registry without writing a
3078    /// delegating wrapper. Behavior is otherwise identical to
3079    /// [`Self::auth_provider`].
3080    pub fn auth_provider_arc(mut self, provider: Arc<dyn auth::AuthProvider>) -> Self {
3081        self.auth_provider = Some(provider);
3082        self
3083    }
3084
3085    /// Set the tool authorizer.
3086    ///
3087    /// Configures a tool authorizer for fine-grained access control.
3088    /// The authorizer determines which tools authenticated users can access
3089    /// based on their authentication context.
3090    ///
3091    /// # Arguments
3092    ///
3093    /// * `authorizer` - The tool authorization implementation
3094    ///
3095    /// # Examples
3096    ///
3097    /// ```rust,no_run
3098    /// use pmcp::{Server, auth::ScopeBasedAuthorizer};
3099    ///
3100    /// let authorizer = ScopeBasedAuthorizer::new()
3101    ///     .require_scopes("sensitive_tool", vec!["admin".to_string()])
3102    ///     .default_scopes(vec!["read".to_string()]);
3103    ///
3104    /// let server = Server::builder()
3105    ///     .name("secure-server")
3106    ///     .version("1.0.0")
3107    ///     .tool_authorizer(authorizer)
3108    ///     .build()?;
3109    /// # Ok::<(), pmcp::Error>(())
3110    /// ```
3111    pub fn tool_authorizer(mut self, authorizer: impl auth::ToolAuthorizer + 'static) -> Self {
3112        if !self.tool_protections.is_empty() {
3113            // Log a warning - custom authorizer supersedes protect_tool() configurations
3114            tracing::warn!(
3115                target: "mcp.auth",
3116                "Setting a custom tool_authorizer clears any previous protect_tool() configurations"
3117            );
3118            self.tool_protections.clear();
3119        }
3120        self.tool_authorizer = Some(Arc::new(authorizer));
3121        self
3122    }
3123
3124    /// Set the tool authorizer with an Arc.
3125    ///
3126    /// This variant lets the caller share the authorizer `Arc` between
3127    /// the builder and an external in-process registry without writing a
3128    /// delegating wrapper. Mirrors [`Self::tool_authorizer`]'s
3129    /// protection-clearing semantics: if any prior `protect_tool()`
3130    /// configurations exist, they are cleared and a `tracing::warn!` is
3131    /// emitted under target `"mcp.auth"`, since a custom authorizer
3132    /// supersedes scope-based tool protections.
3133    pub fn tool_authorizer_arc(mut self, authorizer: Arc<dyn auth::ToolAuthorizer>) -> Self {
3134        if !self.tool_protections.is_empty() {
3135            // Log a warning - custom authorizer supersedes protect_tool() configurations
3136            tracing::warn!(
3137                target: "mcp.auth",
3138                "Setting a custom tool_authorizer clears any previous protect_tool() configurations"
3139            );
3140            self.tool_protections.clear();
3141        }
3142        self.tool_authorizer = Some(authorizer);
3143        self
3144    }
3145
3146    /// Protect a specific tool with required scopes.
3147    ///
3148    /// This is a convenience method that creates or updates a scope-based authorizer
3149    /// to require specific scopes for accessing the named tool.
3150    ///
3151    /// # Arguments
3152    ///
3153    /// * `tool_name` - The name of the tool to protect
3154    /// * `scopes` - The required scopes for accessing this tool
3155    ///
3156    /// # Examples
3157    ///
3158    /// ```rust,no_run
3159    /// use pmcp::Server;
3160    ///
3161    /// let server = Server::builder()
3162    ///     .name("secure-server")
3163    ///     .version("1.0.0")
3164    ///     .protect_tool("delete_data", vec!["admin".to_string(), "write".to_string()])
3165    ///     .protect_tool("read_data", vec!["read".to_string()])
3166    ///     .build()?;
3167    /// # Ok::<(), pmcp::Error>(())
3168    /// ```
3169    pub fn protect_tool(mut self, tool_name: impl Into<String>, scopes: Vec<String>) -> Self {
3170        // Store the tool protection requirements to be applied at build time
3171        self.tool_protections.insert(tool_name.into(), scopes);
3172        self
3173    }
3174
3175    /// Add tool middleware for cross-cutting concerns.
3176    ///
3177    /// Tool middleware allows you to inject cross-cutting concerns into tool execution,
3178    /// such as OAuth token injection, logging, metrics, or request transformation.
3179    /// Middleware is executed in the order it's added, both for request processing
3180    /// (before tool execution) and response processing (after tool execution).
3181    ///
3182    /// This method brings middleware support to the high-level `ServerBuilder` API,
3183    /// enabling developers to use both typed tool registration AND middleware without
3184    /// dropping down to the lower-level `ServerCoreBuilder` API.
3185    ///
3186    /// # Arguments
3187    ///
3188    /// * `middleware` - The middleware implementation to add to the chain
3189    ///
3190    /// # Examples
3191    ///
3192    /// ## OAuth Token Injection Middleware
3193    ///
3194    /// ```rust,no_run
3195    /// use pmcp::server::tool_middleware::{ToolMiddleware, ToolContext};
3196    /// use pmcp::server::cancellation::RequestHandlerExtra;
3197    /// use pmcp::Server;
3198    /// use std::sync::Arc;
3199    /// use async_trait::async_trait;
3200    /// use serde_json::Value;
3201    ///
3202    /// struct OAuthInjectionMiddleware;
3203    ///
3204    /// #[async_trait]
3205    /// impl ToolMiddleware for OAuthInjectionMiddleware {
3206    ///     async fn on_request(
3207    ///         &self,
3208    ///         _tool_name: &str,
3209    ///         _args: &mut Value,
3210    ///         extra: &mut RequestHandlerExtra,
3211    ///         _context: &ToolContext,
3212    ///     ) -> pmcp::Result<()> {
3213    ///         // Extract OAuth token from auth_context and inject into metadata
3214    ///         if let Some(auth_ctx) = extra.auth_context() {
3215    ///             if let Some(token) = &auth_ctx.token {
3216    ///                 extra.set_metadata("oauth_token".to_string(), token.clone());
3217    ///             }
3218    ///         }
3219    ///         Ok(())
3220    ///     }
3221    /// }
3222    ///
3223    /// let server = Server::builder()
3224    ///     .name("oauth-server")
3225    ///     .version("1.0.0")
3226    ///     .tool_middleware(Arc::new(OAuthInjectionMiddleware))
3227    ///     .build()?;
3228    /// # Ok::<(), pmcp::Error>(())
3229    /// ```
3230    ///
3231    /// ## Combining with Typed Tools
3232    ///
3233    /// ```rust,no_run
3234    /// # #[cfg(feature = "schema-generation")]
3235    /// # {
3236    /// use pmcp::Server;
3237    /// use schemars::JsonSchema;
3238    /// use serde::{Deserialize, Serialize};
3239    ///
3240    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
3241    /// struct ListGamesArgs {
3242    ///     filter: Option<String>,
3243    /// }
3244    ///
3245    /// let server = Server::builder()
3246    ///     .name("game-server")
3247    ///     .version("1.0.0")
3248    ///     .tool_typed_with_description(
3249    ///         "list_games",
3250    ///         "List all available games",
3251    ///         |args: ListGamesArgs, extra| {
3252    ///             Box::pin(async move {
3253    ///                 // Access OAuth token injected by middleware
3254    ///                 let _token = extra.get_metadata("oauth_token");
3255    ///                 Ok(serde_json::json!({"games": []}))
3256    ///             })
3257    ///         }
3258    ///     )
3259    ///     // .tool_middleware(Arc::new(oauth_middleware))  // Works with typed tools!
3260    ///     .build()?;
3261    /// # }
3262    /// # Ok::<(), pmcp::Error>(())
3263    /// ```
3264    ///
3265    /// # Middleware Execution Order
3266    ///
3267    /// Multiple middleware are executed in FIFO order for requests and FIFO for responses:
3268    ///
3269    /// ```text
3270    /// Request:  Middleware1 → Middleware2 → Tool Handler
3271    /// Response: Tool Handler → Middleware1 → Middleware2
3272    /// ```
3273    #[cfg(not(target_arch = "wasm32"))]
3274    pub fn tool_middleware(mut self, middleware: Arc<dyn tool_middleware::ToolMiddleware>) -> Self {
3275        self.tool_middlewares.push(middleware);
3276        self
3277    }
3278
3279    /// Enable observability for this server.
3280    ///
3281    /// This adds observability middleware that provides:
3282    /// - Distributed tracing with trace/span IDs
3283    /// - Request/response event logging
3284    /// - Metrics emission (duration, count, errors)
3285    ///
3286    /// The backend is automatically selected based on the configuration:
3287    /// - "console" - Pretty or JSON output to stdout (development)
3288    /// - "cloudwatch" - AWS `CloudWatch` EMF format (production)
3289    /// - "null" - Discards all events (testing)
3290    ///
3291    /// # Examples
3292    ///
3293    /// ```rust,no_run
3294    /// use pmcp::Server;
3295    /// use pmcp::server::observability::ObservabilityConfig;
3296    ///
3297    /// // Development: console output with pretty printing
3298    /// let server = Server::builder()
3299    ///     .name("my-server")
3300    ///     .version("1.0.0")
3301    ///     .with_observability(ObservabilityConfig::development())
3302    ///     .build()?;
3303    ///
3304    /// // Production: CloudWatch with EMF metrics
3305    /// let server = Server::builder()
3306    ///     .name("my-server")
3307    ///     .version("1.0.0")
3308    ///     .with_observability(ObservabilityConfig::production())
3309    ///     .build()?;
3310    ///
3311    /// // Auto-detect environment (Lambda vs local)
3312    /// let config = if std::env::var("AWS_LAMBDA_FUNCTION_NAME").is_ok() {
3313    ///     ObservabilityConfig::production()
3314    /// } else {
3315    ///     ObservabilityConfig::development()
3316    /// };
3317    /// let server = Server::builder()
3318    ///     .name("my-server")
3319    ///     .version("1.0.0")
3320    ///     .with_observability(config)
3321    ///     .build()?;
3322    /// # Ok::<(), pmcp::Error>(())
3323    /// ```
3324    #[cfg(not(target_arch = "wasm32"))]
3325    pub fn with_observability(mut self, config: observability::ObservabilityConfig) -> Self {
3326        if !config.enabled {
3327            return self;
3328        }
3329
3330        // Create backend based on configuration
3331        let backend: Arc<dyn observability::ObservabilityBackend> = match config.backend.as_str() {
3332            "cloudwatch" => Arc::new(observability::CloudWatchBackend::new(
3333                config.cloudwatch.clone(),
3334            )),
3335            "null" => Arc::new(observability::NullBackend),
3336            _ => Arc::new(observability::ConsoleBackend::new(config.console.pretty)),
3337        };
3338
3339        // Get server name for middleware (use placeholder if not yet set)
3340        let server_name = self.name.clone().unwrap_or_else(|| "unknown".to_string());
3341
3342        // Create and add the observability middleware
3343        let middleware =
3344            observability::McpObservabilityMiddleware::new(server_name, config, backend);
3345        self.tool_middlewares.push(Arc::new(middleware));
3346
3347        self
3348    }
3349
3350    /// Enable observability with a custom backend.
3351    ///
3352    /// Use this when you need a custom backend implementation (e.g., Datadog, custom metrics).
3353    ///
3354    /// # Examples
3355    ///
3356    /// ```rust,ignore
3357    /// use pmcp::Server;
3358    /// use pmcp::server::observability::{ObservabilityConfig, ObservabilityBackend};
3359    /// use std::sync::Arc;
3360    ///
3361    /// struct MyCustomBackend;
3362    ///
3363    /// #[async_trait]
3364    /// impl ObservabilityBackend for MyCustomBackend {
3365    ///     // ... custom implementation
3366    /// }
3367    ///
3368    /// let server = Server::builder()
3369    ///     .name("my-server")
3370    ///     .version("1.0.0")
3371    ///     .with_observability_backend(
3372    ///         ObservabilityConfig::development(),
3373    ///         Arc::new(MyCustomBackend),
3374    ///     )
3375    ///     .build()?;
3376    /// ```
3377    #[cfg(not(target_arch = "wasm32"))]
3378    pub fn with_observability_backend(
3379        mut self,
3380        config: observability::ObservabilityConfig,
3381        backend: Arc<dyn observability::ObservabilityBackend>,
3382    ) -> Self {
3383        if !config.enabled {
3384            return self;
3385        }
3386
3387        // Get server name for middleware (use placeholder if not yet set)
3388        let server_name = self.name.clone().unwrap_or_else(|| "unknown".to_string());
3389
3390        // Create and add the observability middleware
3391        let middleware =
3392            observability::McpObservabilityMiddleware::new(server_name, config, backend);
3393        self.tool_middlewares.push(Arc::new(middleware));
3394
3395        self
3396    }
3397
3398    /// Add a description to a tool (Note: Limited support).
3399    ///
3400    /// **Important**: Due to the immutable design of tool handlers, this method
3401    /// cannot retroactively add descriptions to already-registered tools.
3402    ///
3403    /// **Recommended**: Use the `*_with_description` variants instead:
3404    /// - `.tool_typed_with_description()`
3405    /// - `.tool_typed_sync_with_description()`
3406    /// - `.tool_typed_with_output_and_description()`
3407    ///
3408    /// This method is provided for API completeness but will log warnings
3409    /// when used, encouraging migration to the preferred approaches.
3410    ///
3411    /// # Preferred Examples
3412    ///
3413    /// ```rust,no_run
3414    /// # #[cfg(feature = "schema-generation")]
3415    /// # {
3416    /// use pmcp::ServerBuilder;
3417    /// use schemars::JsonSchema;
3418    /// use serde::{Deserialize, Serialize};
3419    ///
3420    /// #[derive(Debug, Deserialize, Serialize, JsonSchema)]
3421    /// struct MathArgs { a: f64, b: f64 }
3422    ///
3423    /// // Preferred: Use the direct description variants
3424    /// let server = ServerBuilder::new()
3425    ///     .name("example")
3426    ///     .tool_typed_with_description(
3427    ///         "add",
3428    ///         "Adds two numbers together",
3429    ///         |args: MathArgs, _| {
3430    ///             Box::pin(async move {
3431    ///                 Ok(serde_json::json!({ "result": args.a + args.b }))
3432    ///             })
3433    ///         }
3434    ///     )
3435    ///     .build();
3436    /// # }
3437    /// ```
3438    #[deprecated(
3439        since = "1.6.0",
3440        note = "Use tool_typed_with_description() and similar variants instead"
3441    )]
3442    pub fn with_tool_description(
3443        self,
3444        tool_name: impl Into<String>,
3445        description: impl Into<String>,
3446    ) -> Self {
3447        let tool_name = tool_name.into();
3448        let _description = description.into();
3449
3450        tracing::warn!(
3451            "with_tool_description('{}') called but cannot modify immutable tools. \
3452            Use tool_typed_with_description() variants instead.",
3453            tool_name
3454        );
3455
3456        self
3457    }
3458
3459    /// Configure HTTP middleware chain for `StreamableHttpServer`.
3460    ///
3461    /// This is a convenience method that stores the HTTP middleware chain
3462    /// so it can be retrieved later when creating a `StreamableHttpServer`.
3463    ///
3464    /// # Arguments
3465    ///
3466    /// * `middleware` - The HTTP middleware chain
3467    ///
3468    /// # Examples
3469    ///
3470    /// ```rust,no_run
3471    /// # #[cfg(feature = "streamable-http")]
3472    /// # fn example() -> Result<(), pmcp::Error> {
3473    /// use pmcp::Server;
3474    /// use pmcp::server::http_middleware::{ServerHttpLoggingMiddleware, ServerHttpMiddlewareChain};
3475    /// use std::sync::Arc;
3476    ///
3477    /// let mut http_chain = ServerHttpMiddlewareChain::new();
3478    /// http_chain.add(Arc::new(ServerHttpLoggingMiddleware::new()));
3479    ///
3480    /// let server = Server::builder()
3481    ///     .name("my-server")
3482    ///     .version("1.0.0")
3483    ///     .with_http_middleware(Arc::new(http_chain))
3484    ///     .build()?;
3485    ///
3486    /// // Later when creating StreamableHttpServer:
3487    /// // let config = StreamableHttpServerConfig {
3488    /// //     http_middleware: server.http_middleware(),
3489    /// //     ..Default::default()
3490    /// // };
3491    /// # Ok(())
3492    /// # }
3493    /// ```
3494    #[cfg(feature = "streamable-http")]
3495    pub fn with_http_middleware(
3496        mut self,
3497        middleware: Arc<http_middleware::ServerHttpMiddlewareChain>,
3498    ) -> Self {
3499        self.http_middleware = Some(middleware);
3500        self
3501    }
3502
3503    /// Add a host layer for MCP Apps metadata enrichment.
3504    ///
3505    /// Host layers enrich tool `_meta` at build time with host-specific keys.
3506    /// For example, `HostType::ChatGpt` adds `openai/outputTemplate` and
3507    /// `openai/widgetAccessible` derived from the standard `ui.resourceUri`.
3508    ///
3509    /// This is opt-in — standard MCP Apps hosts (Claude Desktop, etc.) work
3510    /// without any host layer. Duplicates are ignored.
3511    #[cfg(feature = "mcp-apps")]
3512    pub fn with_host_layer(mut self, host: crate::types::mcp_apps::HostType) -> Self {
3513        if !self.host_layers.contains(&host) {
3514            self.host_layers.push(host);
3515        }
3516        self
3517    }
3518
3519    /// Build the server.
3520    ///
3521    /// Constructs the final Server instance from the configured builder.
3522    /// This validates that required fields (name and version) are set.
3523    ///
3524    /// # Errors
3525    ///
3526    /// Returns an error if:
3527    /// - The server name is not set
3528    /// - The server version is not set
3529    pub fn build(self) -> Result<Server> {
3530        let name = self
3531            .name
3532            .ok_or_else(|| crate::Error::validation("Server name is required"))?;
3533        let version = self
3534            .version
3535            .ok_or_else(|| crate::Error::validation("Server version is required"))?;
3536
3537        // Apply tool protections
3538        let tool_authorizer = if !self.tool_protections.is_empty() {
3539            if self.tool_authorizer.is_some() {
3540                // If there's an existing authorizer and tool protections are specified,
3541                // this is a configuration error
3542                return Err(crate::Error::validation(
3543                    "Cannot use protect_tool() with a custom tool_authorizer. \
3544                     Either use protect_tool() to configure scope-based authorization, \
3545                     or provide a custom ToolAuthorizer implementation, but not both.",
3546                ));
3547            }
3548            // Create a ScopeBasedAuthorizer with all the tool protections
3549            let mut authorizer = auth::ScopeBasedAuthorizer::new();
3550            for (tool_name, scopes) in self.tool_protections {
3551                authorizer = authorizer.require_scopes(tool_name, scopes);
3552            }
3553            Some(Arc::new(authorizer) as Arc<dyn auth::ToolAuthorizer>)
3554        } else {
3555            self.tool_authorizer
3556        };
3557
3558        // Initialize tool middleware chain
3559        #[cfg(not(target_arch = "wasm32"))]
3560        let tool_middleware_chain = {
3561            let mut chain = tool_middleware::ToolMiddlewareChain::new();
3562            for middleware in self.tool_middlewares {
3563                chain.add(middleware);
3564            }
3565            Arc::new(RwLock::new(chain))
3566        };
3567
3568        // Build tool_infos cache at construction time (mirrors ServerCore pattern)
3569        let tool_infos: HashMap<String, ToolInfo> = self
3570            .tools
3571            .iter()
3572            .map(|(name, handler)| {
3573                let info = handler.metadata().unwrap_or_else(|| {
3574                    ToolInfo::new(
3575                        name.clone(),
3576                        None,
3577                        serde_json::json!({"type": "object", "properties": {}}),
3578                    )
3579                });
3580                (name.clone(), info)
3581            })
3582            .collect();
3583
3584        // Apply host layer enrichment to tool _meta (e.g., ChatGPT openai/* keys)
3585        #[cfg(feature = "mcp-apps")]
3586        let tool_infos = {
3587            let mut infos = tool_infos;
3588            for host in &self.host_layers {
3589                for info in infos.values_mut() {
3590                    if let Some(meta) = info._meta.as_mut() {
3591                        core::enrich_meta_for_host(meta, *host);
3592                    }
3593                }
3594            }
3595            infos
3596        };
3597
3598        // Build URI-to-tool-meta index for widget resource _meta propagation
3599        let uri_to_tool_meta = core::build_uri_to_tool_meta(&tool_infos);
3600
3601        // Finalize accumulated skills exactly once and compose with the
3602        // user's `.resources(...)` slot if both are set. `.resources(...)`
3603        // itself stays "last write wins" — composition lives here so the
3604        // setter's semantics are unchanged for callers that don't use
3605        // skills.
3606        #[cfg(feature = "skills")]
3607        let final_resources: Option<Arc<dyn ResourceHandler>> =
3608            builder::finalize_skills_resources(self.pending_skills, self.resources);
3609        #[cfg(not(feature = "skills"))]
3610        let final_resources = self.resources;
3611
3612        Ok(Server {
3613            info: {
3614                let mut info = Implementation::new(&name, &version);
3615                if let Some(url) = self.website_url {
3616                    info = info.with_website_url(url);
3617                }
3618                if let Some(icons) = self.icons {
3619                    info = info.with_icons(icons);
3620                }
3621                info
3622            },
3623            capabilities: self.capabilities,
3624            tools: self.tools,
3625            tool_infos,
3626            uri_to_tool_meta,
3627            prompts: self.prompts,
3628            resources: final_resources,
3629            sampling: self.sampling,
3630            client_capabilities: Arc::new(RwLock::new(None)),
3631            initialized: Arc::new(RwLock::new(false)),
3632            notification_tx: None,
3633            cancellation_manager: self.cancellation_manager,
3634            roots_manager: Arc::new(RwLock::new(self.roots_manager)),
3635            subscription_manager: Arc::new(RwLock::new(subscriptions::SubscriptionManager::new())),
3636            elicitation_manager: None,
3637            server_request_dispatcher: None,
3638            peer_handle: None,
3639            auth_provider: self.auth_provider,
3640            tool_authorizer,
3641            #[cfg(not(target_arch = "wasm32"))]
3642            tool_middleware_chain,
3643            #[cfg(feature = "streamable-http")]
3644            http_middleware: self.http_middleware,
3645        })
3646    }
3647}
3648
3649#[cfg(not(target_arch = "wasm32"))]
3650impl Default for ServerBuilder {
3651    fn default() -> Self {
3652        Self::new()
3653    }
3654}
3655
3656#[cfg(test)]
3657mod tests {
3658    use super::*;
3659    use crate::shared::Transport;
3660    use crate::types::{
3661        jsonrpc::ResponsePayload, ClientCapabilities, InitializeRequest, ServerCapabilities,
3662        TransportMessage,
3663    };
3664    use async_trait::async_trait;
3665    use serde_json::json;
3666    use std::sync::{Arc, Mutex};
3667    use tokio::time::timeout;
3668
3669    /// Mock transport for testing
3670    #[derive(Debug)]
3671    struct MockTransport {
3672        messages: Arc<Mutex<Vec<TransportMessage>>>,
3673        responses: Arc<Mutex<Vec<TransportMessage>>>,
3674    }
3675
3676    impl MockTransport {
3677        #[allow(dead_code)]
3678        fn new() -> Self {
3679            Self {
3680                messages: Arc::new(Mutex::new(Vec::new())),
3681                responses: Arc::new(Mutex::new(Vec::new())),
3682            }
3683        }
3684
3685        fn with_requests(requests: Vec<TransportMessage>) -> Self {
3686            Self {
3687                messages: Arc::new(Mutex::new(requests)),
3688                responses: Arc::new(Mutex::new(Vec::new())),
3689            }
3690        }
3691
3692        #[allow(dead_code)]
3693        fn add_request(&self, request: TransportMessage) {
3694            self.messages.lock().unwrap().push(request);
3695        }
3696
3697        #[allow(dead_code)]
3698        fn get_sent_responses(&self) -> Vec<TransportMessage> {
3699            self.responses.lock().unwrap().clone()
3700        }
3701    }
3702
3703    #[async_trait]
3704    impl Transport for MockTransport {
3705        async fn send(&mut self, message: TransportMessage) -> Result<()> {
3706            self.responses.lock().unwrap().push(message);
3707            Ok(())
3708        }
3709
3710        async fn receive(&mut self) -> Result<TransportMessage> {
3711            let mut messages = self.messages.lock().unwrap();
3712            messages
3713                .pop()
3714                .map_or_else(|| Err(Error::protocol_msg("No more messages")), Ok)
3715        }
3716
3717        async fn close(&mut self) -> Result<()> {
3718            Ok(())
3719        }
3720
3721        fn is_connected(&self) -> bool {
3722            !self.messages.lock().unwrap().is_empty()
3723        }
3724
3725        fn transport_type(&self) -> &'static str {
3726            "mock"
3727        }
3728    }
3729
3730    /// Mock tool handler for testing
3731    struct MockTool {
3732        result: Value,
3733    }
3734
3735    impl MockTool {
3736        fn new(result: Value) -> Self {
3737            Self { result }
3738        }
3739    }
3740
3741    #[async_trait]
3742    impl ToolHandler for MockTool {
3743        async fn handle(
3744            &self,
3745            _args: Value,
3746            _extra: crate::server::cancellation::RequestHandlerExtra,
3747        ) -> Result<Value> {
3748            Ok(self.result.clone())
3749        }
3750    }
3751
3752    /// Mock prompt handler for testing
3753    struct MockPrompt {
3754        result: crate::types::GetPromptResult,
3755    }
3756
3757    impl MockPrompt {
3758        fn new(result: crate::types::GetPromptResult) -> Self {
3759            Self { result }
3760        }
3761    }
3762
3763    #[async_trait]
3764    impl PromptHandler for MockPrompt {
3765        async fn handle(
3766            &self,
3767            _args: HashMap<String, String>,
3768            _extra: crate::server::cancellation::RequestHandlerExtra,
3769        ) -> Result<crate::types::GetPromptResult> {
3770            Ok(self.result.clone())
3771        }
3772    }
3773
3774    /// Mock resource handler for testing
3775    struct MockResource {
3776        resources: Vec<crate::types::ResourceInfo>,
3777        contents: HashMap<String, crate::types::ReadResourceResult>,
3778    }
3779
3780    impl MockResource {
3781        fn new() -> Self {
3782            Self {
3783                resources: Vec::new(),
3784                contents: HashMap::new(),
3785            }
3786        }
3787
3788        fn with_resource(mut self, uri: String, content: crate::types::ReadResourceResult) -> Self {
3789            self.contents.insert(uri, content);
3790            self
3791        }
3792    }
3793
3794    #[async_trait]
3795    impl ResourceHandler for MockResource {
3796        async fn read(
3797            &self,
3798            uri: &str,
3799            _extra: crate::server::cancellation::RequestHandlerExtra,
3800        ) -> Result<crate::types::ReadResourceResult> {
3801            self.contents
3802                .get(uri)
3803                .cloned()
3804                .ok_or_else(|| Error::not_found(format!("Resource '{}' not found", uri)))
3805        }
3806
3807        async fn list(
3808            &self,
3809            _cursor: Option<String>,
3810            _extra: crate::server::cancellation::RequestHandlerExtra,
3811        ) -> Result<crate::types::ListResourcesResult> {
3812            Ok(crate::types::ListResourcesResult {
3813                resources: self.resources.clone(),
3814                next_cursor: None,
3815            })
3816        }
3817    }
3818
3819    #[test]
3820    fn test_server_builder() {
3821        let server = Server::builder()
3822            .name("test-server")
3823            .version("1.0.0")
3824            .capabilities(ServerCapabilities::tools_only())
3825            .tool("test-tool", MockTool::new(json!({"result": "success"})))
3826            .build()
3827            .unwrap();
3828
3829        assert_eq!(server.info.name, "test-server");
3830        assert_eq!(server.info.version, "1.0.0");
3831        assert!(server.tools.contains_key("test-tool"));
3832    }
3833
3834    #[test]
3835    fn test_server_builder_validation() {
3836        // Missing name
3837        let result = Server::builder().version("1.0.0").build();
3838        assert!(result.is_err());
3839
3840        // Missing version
3841        let result = Server::builder().name("test-server").build();
3842        assert!(result.is_err());
3843    }
3844
3845    #[tokio::test]
3846    async fn test_server_initialization() {
3847        let init_request = TransportMessage::Request {
3848            id: RequestId::from(1i64),
3849            request: Request::Client(Box::new(ClientRequest::Initialize(InitializeRequest {
3850                protocol_version: "2024-11-05".to_string(),
3851                capabilities: ClientCapabilities::minimal(),
3852                client_info: Implementation::new("test-client", "1.0.0"),
3853            }))),
3854        };
3855
3856        let transport = MockTransport::with_requests(vec![init_request]);
3857        let server = Server::builder()
3858            .name("test-server")
3859            .version("1.0.0")
3860            .capabilities(ServerCapabilities::tools_only())
3861            .build()
3862            .unwrap();
3863
3864        // Test server run for a short time
3865        let server_handle = tokio::spawn(async move {
3866            let _ = timeout(std::time::Duration::from_millis(100), server.run(transport)).await;
3867        });
3868
3869        // Wait for server to process
3870        let _ = timeout(std::time::Duration::from_millis(200), server_handle).await;
3871    }
3872
3873    #[tokio::test]
3874    async fn test_server_capabilities() {
3875        let server = Server::builder()
3876            .name("test-server")
3877            .version("1.0.0")
3878            .capabilities(ServerCapabilities::tools_only())
3879            .build()
3880            .unwrap();
3881
3882        assert!(!server.is_initialized().await);
3883        assert!(server.get_client_capabilities().await.is_none());
3884    }
3885
3886    #[tokio::test]
3887    async fn test_server_notifications() {
3888        let server = Server::builder()
3889            .name("test-server")
3890            .version("1.0.0")
3891            .build()
3892            .unwrap();
3893
3894        // Send notification (should not panic even without transport)
3895        server
3896            .send_notification(ServerNotification::ToolsChanged)
3897            .await;
3898    }
3899
3900    #[test]
3901    fn test_server_builder_with_all_handlers() {
3902        let prompt_result = crate::types::GetPromptResult {
3903            description: Some("Test prompt".to_string()),
3904            messages: vec![],
3905            _meta: None,
3906        };
3907
3908        let resource_content =
3909            crate::types::ReadResourceResult::new(vec![crate::types::Content::text(
3910                "Hello, world!",
3911            )]);
3912
3913        let server = Server::builder()
3914            .name("test-server")
3915            .version("1.0.0")
3916            .tool("test-tool", MockTool::new(json!({"result": "success"})))
3917            .prompt("test-prompt", MockPrompt::new(prompt_result))
3918            .resources(
3919                MockResource::new().with_resource("test://uri".to_string(), resource_content),
3920            )
3921            .build()
3922            .unwrap();
3923
3924        assert!(server.tools.contains_key("test-tool"));
3925        assert!(server.prompts.contains_key("test-prompt"));
3926        assert!(server.resources.is_some());
3927    }
3928
3929    #[tokio::test]
3930    async fn test_handle_request_initialize() {
3931        let server = Server::builder()
3932            .name("test-server")
3933            .version("1.0.0")
3934            .capabilities(ServerCapabilities::tools_only())
3935            .build()
3936            .unwrap();
3937
3938        let request = Request::Client(Box::new(ClientRequest::Initialize(InitializeRequest {
3939            protocol_version: "2024-11-05".to_string(),
3940            capabilities: ClientCapabilities::default(),
3941            client_info: Implementation::new("test-client", "1.0.0"),
3942        })));
3943
3944        let response = server
3945            .handle_request(RequestId::from(1i64), request, None)
3946            .await;
3947
3948        assert_eq!(response.id, RequestId::from(1i64));
3949        match response.payload {
3950            ResponsePayload::Result(_) => {
3951                assert!(server.is_initialized().await);
3952            },
3953            ResponsePayload::Error(_) => panic!("Expected success response"),
3954        }
3955    }
3956
3957    #[tokio::test]
3958    async fn test_handle_list_tools() {
3959        let server = Server::builder()
3960            .name("test-server")
3961            .version("1.0.0")
3962            .tool("test-tool", MockTool::new(json!({"result": "success"})))
3963            .build()
3964            .unwrap();
3965
3966        let request = Request::Client(Box::new(ClientRequest::ListTools(ListToolsRequest {
3967            cursor: None,
3968        })));
3969        let response = server
3970            .handle_request(RequestId::from(1i64), request, None)
3971            .await;
3972
3973        match response.payload {
3974            ResponsePayload::Result(result) => {
3975                let tools_result: ListToolsResult = serde_json::from_value(result).unwrap();
3976                assert_eq!(tools_result.tools.len(), 1);
3977                assert_eq!(tools_result.tools[0].name, "test-tool");
3978            },
3979            ResponsePayload::Error(_) => panic!("Expected success response"),
3980        }
3981    }
3982
3983    #[tokio::test]
3984    async fn test_handle_call_tool() {
3985        let server = Server::builder()
3986            .name("test-server")
3987            .version("1.0.0")
3988            .tool("test-tool", MockTool::new(json!({"result": "success"})))
3989            .build()
3990            .unwrap();
3991
3992        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
3993            name: "test-tool".to_string(),
3994            arguments: json!({"input": "test"}),
3995            _meta: None,
3996            task: None,
3997        })));
3998
3999        let response = server
4000            .handle_request(RequestId::from(1i64), request, None)
4001            .await;
4002
4003        match response.payload {
4004            ResponsePayload::Result(result) => {
4005                let call_result: CallToolResult = serde_json::from_value(result).unwrap();
4006                assert!(!call_result.is_error);
4007                assert_eq!(call_result.content.len(), 1);
4008            },
4009            ResponsePayload::Error(_) => panic!("Expected success response"),
4010        }
4011    }
4012
4013    #[tokio::test]
4014    async fn test_handle_call_tool_rejected_is_iserror_not_protocol_error() {
4015        // A handler returning `Error::tool_rejected` must surface through the
4016        // streamable-HTTP `Server` path as a SUCCESSFUL `CallToolResult`
4017        // with `isError: true` (message → content, details → structuredContent),
4018        // NOT a JSON-RPC protocol error. This is the Code Mode policy-rejection
4019        // envelope (e.g. "SELECT missing LIMIT") observed by `pmcp-sql-server`.
4020        struct RejectingTool;
4021        #[async_trait]
4022        impl ToolHandler for RejectingTool {
4023            async fn handle(
4024                &self,
4025                _args: Value,
4026                _extra: crate::server::cancellation::RequestHandlerExtra,
4027            ) -> Result<Value> {
4028                Err(Error::tool_rejected(
4029                    "SELECT statements must declare a LIMIT",
4030                    Some(json!({ "violations": [{ "rule": "missing_limit" }] })),
4031                ))
4032            }
4033        }
4034
4035        let server = Server::builder()
4036            .name("test-server")
4037            .version("1.0.0")
4038            .tool("reject", RejectingTool)
4039            .build()
4040            .unwrap();
4041
4042        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4043            name: "reject".to_string(),
4044            arguments: json!({}),
4045            _meta: None,
4046            task: None,
4047        })));
4048
4049        let response = server
4050            .handle_request(RequestId::from(1i64), request, None)
4051            .await;
4052
4053        match response.payload {
4054            ResponsePayload::Result(result) => {
4055                let call_result: CallToolResult = serde_json::from_value(result).unwrap();
4056                assert!(call_result.is_error, "tool_rejected must set isError: true");
4057                let text = call_result
4058                    .content
4059                    .iter()
4060                    .find_map(|c| match c {
4061                        crate::types::Content::Text { text } => Some(text.clone()),
4062                        _ => None,
4063                    })
4064                    .unwrap_or_default();
4065                assert!(
4066                    text.contains("must declare a LIMIT"),
4067                    "content must carry the rejection message, got: {text}"
4068                );
4069                let sc = call_result
4070                    .structured_content
4071                    .expect("structuredContent must carry the violation detail");
4072                assert_eq!(sc["violations"][0]["rule"], "missing_limit");
4073            },
4074            ResponsePayload::Error(e) => panic!(
4075                "tool_rejected must NOT be a protocol error, got {}: {}",
4076                e.code, e.message
4077            ),
4078        }
4079    }
4080
4081    #[tokio::test]
4082    async fn test_handle_call_tool_not_found() {
4083        let server = Server::builder()
4084            .name("test-server")
4085            .version("1.0.0")
4086            .build()
4087            .unwrap();
4088
4089        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4090            name: "nonexistent-tool".to_string(),
4091            arguments: json!({}),
4092            _meta: None,
4093            task: None,
4094        })));
4095
4096        let response = server
4097            .handle_request(RequestId::from(1i64), request, None)
4098            .await;
4099
4100        match response.payload {
4101            ResponsePayload::Error(error) => {
4102                assert!(error.message.contains("not found"));
4103            },
4104            ResponsePayload::Result(_) => panic!("Expected error response"),
4105        }
4106    }
4107
4108    #[tokio::test]
4109    async fn test_handle_list_prompts() {
4110        let prompt_result = crate::types::GetPromptResult {
4111            description: Some("Test prompt".to_string()),
4112            messages: vec![],
4113            _meta: None,
4114        };
4115
4116        let server = Server::builder()
4117            .name("test-server")
4118            .version("1.0.0")
4119            .prompt("test-prompt", MockPrompt::new(prompt_result))
4120            .build()
4121            .unwrap();
4122
4123        let request = Request::Client(Box::new(ClientRequest::ListPrompts(ListPromptsRequest {
4124            cursor: None,
4125        })));
4126        let response = server
4127            .handle_request(RequestId::from(1i64), request, None)
4128            .await;
4129
4130        match response.payload {
4131            ResponsePayload::Result(result) => {
4132                let list_result: ListPromptsResult = serde_json::from_value(result).unwrap();
4133                assert_eq!(list_result.prompts.len(), 1);
4134                assert_eq!(list_result.prompts[0].name, "test-prompt");
4135            },
4136            ResponsePayload::Error(_) => panic!("Expected success response"),
4137        }
4138    }
4139
4140    #[tokio::test]
4141    async fn test_handle_get_prompt() {
4142        let prompt_result = crate::types::GetPromptResult {
4143            description: Some("Test prompt".to_string()),
4144            messages: vec![],
4145            _meta: None,
4146        };
4147
4148        let server = Server::builder()
4149            .name("test-server")
4150            .version("1.0.0")
4151            .prompt("test-prompt", MockPrompt::new(prompt_result.clone()))
4152            .build()
4153            .unwrap();
4154
4155        let request = Request::Client(Box::new(ClientRequest::GetPrompt(GetPromptRequest {
4156            name: "test-prompt".to_string(),
4157            arguments: HashMap::new(),
4158            _meta: None,
4159        })));
4160
4161        let response = server
4162            .handle_request(RequestId::from(1i64), request, None)
4163            .await;
4164
4165        match response.payload {
4166            ResponsePayload::Result(result) => {
4167                let get_result: crate::types::GetPromptResult =
4168                    serde_json::from_value(result).unwrap();
4169                assert_eq!(get_result.description, prompt_result.description);
4170            },
4171            ResponsePayload::Error(_) => panic!("Expected success response"),
4172        }
4173    }
4174
4175    #[tokio::test]
4176    async fn test_handle_list_resources() {
4177        let resource_content =
4178            crate::types::ReadResourceResult::new(vec![crate::types::Content::text(
4179                "Hello, world!",
4180            )]);
4181
4182        let server = Server::builder()
4183            .name("test-server")
4184            .version("1.0.0")
4185            .resources(
4186                MockResource::new().with_resource("test://uri".to_string(), resource_content),
4187            )
4188            .build()
4189            .unwrap();
4190
4191        let request = Request::Client(Box::new(ClientRequest::ListResources(
4192            ListResourcesRequest { cursor: None },
4193        )));
4194        let response = server
4195            .handle_request(RequestId::from(1i64), request, None)
4196            .await;
4197
4198        match response.payload {
4199            ResponsePayload::Result(result) => {
4200                let resources_result: ListResourcesResult = serde_json::from_value(result).unwrap();
4201                assert_eq!(resources_result.resources.len(), 0); // MockResource has empty list by default
4202            },
4203            ResponsePayload::Error(_) => panic!("Expected success response"),
4204        }
4205    }
4206
4207    #[tokio::test]
4208    async fn test_handle_read_resource() {
4209        let resource_content =
4210            crate::types::ReadResourceResult::new(vec![crate::types::Content::text(
4211                "Hello, world!",
4212            )]);
4213
4214        let server = Server::builder()
4215            .name("test-server")
4216            .version("1.0.0")
4217            .resources(
4218                MockResource::new()
4219                    .with_resource("test://uri".to_string(), resource_content.clone()),
4220            )
4221            .build()
4222            .unwrap();
4223
4224        let request = Request::Client(Box::new(ClientRequest::ReadResource(ReadResourceRequest {
4225            uri: "test://uri".to_string(),
4226            _meta: None,
4227        })));
4228
4229        let response = server
4230            .handle_request(RequestId::from(1i64), request, None)
4231            .await;
4232
4233        match response.payload {
4234            ResponsePayload::Result(result) => {
4235                let read_result: crate::types::ReadResourceResult =
4236                    serde_json::from_value(result).unwrap();
4237                assert_eq!(read_result.contents.len(), 1);
4238            },
4239            ResponsePayload::Error(_) => panic!("Expected success response"),
4240        }
4241    }
4242
4243    #[tokio::test]
4244    async fn test_handle_read_resource_not_found() {
4245        let server = Server::builder()
4246            .name("test-server")
4247            .version("1.0.0")
4248            .resources(MockResource::new())
4249            .build()
4250            .unwrap();
4251
4252        let request = Request::Client(Box::new(ClientRequest::ReadResource(ReadResourceRequest {
4253            uri: "nonexistent://uri".to_string(),
4254            _meta: None,
4255        })));
4256
4257        let response = server
4258            .handle_request(RequestId::from(1i64), request, None)
4259            .await;
4260
4261        match response.payload {
4262            ResponsePayload::Error(error) => {
4263                assert!(error.message.contains("not found"));
4264            },
4265            ResponsePayload::Result(_) => panic!("Expected error response"),
4266        }
4267    }
4268
4269    #[tokio::test]
4270    async fn test_handle_ping() {
4271        let server = Server::builder()
4272            .name("test-server")
4273            .version("1.0.0")
4274            .build()
4275            .unwrap();
4276
4277        let request = Request::Client(Box::new(ClientRequest::Ping));
4278        let response = server
4279            .handle_request(RequestId::from(1i64), request, None)
4280            .await;
4281
4282        match response.payload {
4283            ResponsePayload::Result(_) => {
4284                // Success
4285            },
4286            ResponsePayload::Error(_) => panic!("Expected success response"),
4287        }
4288    }
4289
4290    #[tokio::test]
4291    async fn test_handle_server_request() {
4292        let server = Server::builder()
4293            .name("test-server")
4294            .version("1.0.0")
4295            .build()
4296            .unwrap();
4297
4298        let request = Request::Server(Box::new(crate::types::ServerRequest::CreateMessage(
4299            Box::new(crate::types::CreateMessageParams {
4300                messages: vec![],
4301                model_preferences: None,
4302                system_prompt: None,
4303                include_context: crate::types::IncludeContext::None,
4304                temperature: None,
4305                max_tokens: None,
4306                stop_sequences: None,
4307                metadata: None,
4308                tools: None,
4309                tool_choice: None,
4310            }),
4311        )));
4312        let response = server
4313            .handle_request(RequestId::from(1i64), request, None)
4314            .await;
4315
4316        match response.payload {
4317            ResponsePayload::Error(error) => {
4318                assert_eq!(error.code, -32601);
4319                assert!(error.message.contains("not supported"));
4320            },
4321            ResponsePayload::Result(_) => panic!("Expected error response"),
4322        }
4323    }
4324
4325    // Tests for tool middleware support in ServerBuilder
4326    #[tokio::test]
4327    async fn test_server_builder_with_tool_middleware() {
4328        use crate::server::tool_middleware::{ToolContext, ToolMiddleware};
4329        use std::sync::atomic::{AtomicBool, Ordering};
4330
4331        // Create a simple middleware that sets a flag when called
4332        struct TestMiddleware {
4333            called: Arc<AtomicBool>,
4334        }
4335
4336        #[async_trait]
4337        impl ToolMiddleware for TestMiddleware {
4338            async fn on_request(
4339                &self,
4340                _tool_name: &str,
4341                _args: &mut Value,
4342                extra: &mut crate::server::cancellation::RequestHandlerExtra,
4343                _context: &ToolContext,
4344            ) -> Result<()> {
4345                self.called.store(true, Ordering::SeqCst);
4346                extra.set_metadata("middleware_executed".to_string(), "true".to_string());
4347                Ok(())
4348            }
4349        }
4350
4351        let middleware_called = Arc::new(AtomicBool::new(false));
4352        let middleware = Arc::new(TestMiddleware {
4353            called: Arc::clone(&middleware_called),
4354        });
4355
4356        // Build server with middleware
4357        let server = Server::builder()
4358            .name("test-server")
4359            .version("1.0.0")
4360            .tool("test_tool", MockTool::new(json!({"result": "success"})))
4361            .tool_middleware(middleware)
4362            .build()
4363            .unwrap();
4364
4365        // Call the tool
4366        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4367            name: "test_tool".to_string(),
4368            arguments: json!({}),
4369            _meta: None,
4370            task: None,
4371        })));
4372
4373        let response = server
4374            .handle_request(RequestId::from(1i64), request, None)
4375            .await;
4376
4377        // Verify middleware was called
4378        assert!(middleware_called.load(Ordering::SeqCst));
4379
4380        // Verify tool executed successfully
4381        match response.payload {
4382            ResponsePayload::Result(_) => {}, // Success
4383            ResponsePayload::Error(e) => panic!("Expected success, got error: {:?}", e),
4384        }
4385    }
4386
4387    #[tokio::test]
4388    async fn test_server_builder_multiple_middlewares() {
4389        use crate::server::tool_middleware::{ToolContext, ToolMiddleware};
4390        use std::sync::atomic::{AtomicUsize, Ordering};
4391
4392        // Middleware that increments a counter
4393        struct CounterMiddleware {
4394            counter: Arc<AtomicUsize>,
4395            id: usize,
4396        }
4397
4398        #[async_trait]
4399        impl ToolMiddleware for CounterMiddleware {
4400            async fn on_request(
4401                &self,
4402                _tool_name: &str,
4403                _args: &mut Value,
4404                extra: &mut crate::server::cancellation::RequestHandlerExtra,
4405                _context: &ToolContext,
4406            ) -> Result<()> {
4407                let count = self.counter.fetch_add(1, Ordering::SeqCst);
4408                extra.set_metadata(format!("middleware_{}_order", self.id), count.to_string());
4409                Ok(())
4410            }
4411        }
4412
4413        let counter = Arc::new(AtomicUsize::new(0));
4414        let middleware1 = Arc::new(CounterMiddleware {
4415            counter: Arc::clone(&counter),
4416            id: 1,
4417        });
4418        let middleware2 = Arc::new(CounterMiddleware {
4419            counter: Arc::clone(&counter),
4420            id: 2,
4421        });
4422
4423        // Build server with multiple middlewares
4424        let server = Server::builder()
4425            .name("test-server")
4426            .version("1.0.0")
4427            .tool("test_tool", MockTool::new(json!({"result": "success"})))
4428            .tool_middleware(middleware1)
4429            .tool_middleware(middleware2)
4430            .build()
4431            .unwrap();
4432
4433        // Call the tool
4434        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4435            name: "test_tool".to_string(),
4436            arguments: json!({}),
4437            _meta: None,
4438            task: None,
4439        })));
4440
4441        let _response = server
4442            .handle_request(RequestId::from(1i64), request, None)
4443            .await;
4444
4445        // Verify both middlewares were called in order
4446        assert_eq!(counter.load(Ordering::SeqCst), 2);
4447    }
4448
4449    #[tokio::test]
4450    async fn test_server_builder_middleware_with_typed_tools() {
4451        use crate::server::tool_middleware::{ToolContext, ToolMiddleware};
4452        use std::sync::atomic::{AtomicBool, Ordering};
4453
4454        // Middleware that injects OAuth token
4455        struct OAuthMiddleware {
4456            called: Arc<AtomicBool>,
4457        }
4458
4459        #[async_trait]
4460        impl ToolMiddleware for OAuthMiddleware {
4461            async fn on_request(
4462                &self,
4463                _tool_name: &str,
4464                _args: &mut Value,
4465                extra: &mut crate::server::cancellation::RequestHandlerExtra,
4466                _context: &ToolContext,
4467            ) -> Result<()> {
4468                self.called.store(true, Ordering::SeqCst);
4469                extra.set_metadata("oauth_token".to_string(), "test-token-123".to_string());
4470                Ok(())
4471            }
4472        }
4473
4474        // Tool that verifies OAuth token was injected
4475        struct OAuthVerifyTool;
4476
4477        #[async_trait]
4478        impl ToolHandler for OAuthVerifyTool {
4479            async fn handle(
4480                &self,
4481                _args: Value,
4482                extra: crate::server::cancellation::RequestHandlerExtra,
4483            ) -> Result<Value> {
4484                // Verify OAuth token was injected by middleware
4485                let token = extra.get_metadata("oauth_token");
4486                assert!(token.is_some());
4487                assert_eq!(token.unwrap(), "test-token-123");
4488                Ok(json!({"success": true}))
4489            }
4490        }
4491
4492        let middleware_called = Arc::new(AtomicBool::new(false));
4493        let middleware = Arc::new(OAuthMiddleware {
4494            called: Arc::clone(&middleware_called),
4495        });
4496
4497        let server = Server::builder()
4498            .name("test-server")
4499            .version("1.0.0")
4500            .tool("typed_tool", OAuthVerifyTool)
4501            .tool_middleware(middleware)
4502            .build()
4503            .unwrap();
4504
4505        // Call the typed tool
4506        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4507            name: "typed_tool".to_string(),
4508            arguments: json!({}),
4509            _meta: None,
4510            task: None,
4511        })));
4512
4513        let response = server
4514            .handle_request(RequestId::from(1i64), request, None)
4515            .await;
4516
4517        // Verify middleware was called
4518        assert!(middleware_called.load(Ordering::SeqCst));
4519
4520        // Verify tool executed successfully
4521        match response.payload {
4522            ResponsePayload::Result(_) => {}, // Success
4523            ResponsePayload::Error(e) => panic!("Expected success, got error: {:?}", e),
4524        }
4525    }
4526
4527    #[tokio::test]
4528    async fn test_server_builder_middleware_error_handling() {
4529        use crate::server::tool_middleware::{ToolContext, ToolMiddleware};
4530
4531        // Middleware that rejects requests
4532        struct RejectMiddleware;
4533
4534        #[async_trait]
4535        impl ToolMiddleware for RejectMiddleware {
4536            async fn on_request(
4537                &self,
4538                _tool_name: &str,
4539                _args: &mut Value,
4540                _extra: &mut crate::server::cancellation::RequestHandlerExtra,
4541                _context: &ToolContext,
4542            ) -> Result<()> {
4543                Err(Error::validation("Middleware rejected request"))
4544            }
4545        }
4546
4547        // Build server with rejecting middleware
4548        let server = Server::builder()
4549            .name("test-server")
4550            .version("1.0.0")
4551            .tool("test_tool", MockTool::new(json!({"result": "success"})))
4552            .tool_middleware(Arc::new(RejectMiddleware))
4553            .build()
4554            .unwrap();
4555
4556        // Call the tool
4557        let request = Request::Client(Box::new(ClientRequest::CallTool(CallToolRequest {
4558            name: "test_tool".to_string(),
4559            arguments: json!({}),
4560            _meta: None,
4561            task: None,
4562        })));
4563
4564        let response = server
4565            .handle_request(RequestId::from(1i64), request, None)
4566            .await;
4567
4568        // Verify request was rejected by middleware
4569        match response.payload {
4570            ResponsePayload::Error(e) => {
4571                assert!(e.message.contains("Middleware rejected request"));
4572            },
4573            ResponsePayload::Result(_) => panic!("Expected error from middleware"),
4574        }
4575    }
4576
4577    #[tokio::test]
4578    async fn test_server_builder_auto_capabilities_serialization() {
4579        // Test that ServerBuilder (used by Server::builder()) auto-sets capabilities
4580        // with proper serialization values
4581        let server = Server::builder()
4582            .name("test")
4583            .version("1.0.0")
4584            .tool("test-tool", MockTool::new(json!({"result": "ok"})))
4585            .prompt(
4586                "test-prompt",
4587                MockPrompt::new(crate::types::GetPromptResult {
4588                    description: None,
4589                    messages: vec![],
4590                    _meta: None,
4591                }),
4592            )
4593            .resources(MockResource::new())
4594            .build()
4595            .unwrap();
4596
4597        let caps = &server.capabilities;
4598        let json = serde_json::to_value(caps).unwrap();
4599
4600        // Verify tools capability is present and properly structured
4601        let tools = json.get("tools").expect("tools should be present in JSON");
4602        assert!(tools.is_object(), "tools should be an object");
4603        let list_changed = tools.get("listChanged");
4604        assert!(
4605            list_changed.is_some(),
4606            "listChanged should be present in tools"
4607        );
4608        assert_eq!(
4609            list_changed.unwrap(),
4610            &serde_json::json!(false),
4611            "listChanged should be false"
4612        );
4613
4614        // Verify prompts capability
4615        let prompts = json
4616            .get("prompts")
4617            .expect("prompts should be present in JSON");
4618        assert!(prompts.is_object(), "prompts should be an object");
4619        assert!(
4620            prompts.get("listChanged").is_some(),
4621            "listChanged should be present in prompts"
4622        );
4623
4624        // Verify resources capability
4625        let resources = json
4626            .get("resources")
4627            .expect("resources should be present in JSON");
4628        assert!(resources.is_object(), "resources should be an object");
4629        assert!(
4630            resources.get("listChanged").is_some() || resources.get("subscribe").is_some(),
4631            "resources should have fields"
4632        );
4633
4634        println!(
4635            "Serialized capabilities: {}",
4636            serde_json::to_string_pretty(&json).unwrap()
4637        );
4638    }
4639
4640    /// Behavioral test for `ServerBuilder::tool_authorizer_arc` — the only
4641    /// non-mechanical lift among Phase 82's six `_arc` lifts.
4642    ///
4643    /// `tool_authorizer_arc` mirrors `tool_authorizer()`'s protection-clearing
4644    /// semantics: chaining `.protect_tool(...)` BEFORE `.tool_authorizer_arc(...)`
4645    /// must clear `tool_protections` so that `.build()` does NOT hit the
4646    /// mixed-config rejection branch at mod.rs `build()` (which fires if
4647    /// `tool_protections` is non-empty AND `tool_authorizer` is set).
4648    /// This test fills the verification gap source-greps cannot — proving
4649    /// the `.clear()` call actually fires.
4650    #[tokio::test]
4651    async fn tool_authorizer_arc_clears_tool_protections_and_allows_build() {
4652        // Define a no-op custom ToolAuthorizer for the test.
4653        struct NoopAuthorizer;
4654        #[async_trait]
4655        impl crate::server::auth::ToolAuthorizer for NoopAuthorizer {
4656            async fn can_access_tool(
4657                &self,
4658                _auth: &crate::server::auth::AuthContext,
4659                _tool_name: &str,
4660            ) -> crate::Result<bool> {
4661                Ok(true)
4662            }
4663            async fn required_scopes_for_tool(
4664                &self,
4665                _tool_name: &str,
4666            ) -> crate::Result<Vec<String>> {
4667                Ok(vec![])
4668            }
4669        }
4670
4671        // Build a server with BOTH protect_tool AND tool_authorizer_arc.
4672        // Without the clearing semantic, build() would return the
4673        // "Cannot use protect_tool() with a custom tool_authorizer" error.
4674        let builder = ServerBuilder::new()
4675            .name("test")
4676            .version("1")
4677            .protect_tool("delete", vec!["admin".to_string()])
4678            .tool_authorizer_arc(Arc::new(NoopAuthorizer));
4679
4680        // ASSERT 1: tool_protections was cleared by tool_authorizer_arc(),
4681        // visible because we are inside the same module and have access
4682        // to the private field.
4683        assert!(
4684            builder.tool_protections.is_empty(),
4685            "tool_authorizer_arc() must clear tool_protections to mirror tool_authorizer()"
4686        );
4687
4688        // ASSERT 2: build() succeeds — the mixed-config rejection branch
4689        // does NOT fire because protections was cleared.
4690        let build_result = builder.build();
4691        assert!(
4692            build_result.is_ok(),
4693            "build() should succeed after tool_authorizer_arc() clears protections; got Err({:?})",
4694            build_result.err()
4695        );
4696    }
4697}
4698
4699#[cfg(test)]
4700#[cfg(all(feature = "skills", not(target_arch = "wasm32")))]
4701mod skills_builder_tests {
4702    use super::*;
4703    use crate::server::cancellation::RequestHandlerExtra;
4704    use crate::server::skills::{Skill, SkillReference, Skills};
4705    use crate::types::Content;
4706    use async_trait::async_trait;
4707
4708    // ── Test 2.1a: single skill via ServerBuilder (public path) ──────
4709    #[test]
4710    fn test_2_1a_skill_method_single_skill_via_server_builder() {
4711        let server = Server::builder()
4712            .name("test")
4713            .version("1.0")
4714            .skill(Skill::new("foo", "body"))
4715            .build()
4716            .unwrap();
4717        assert!(server.capabilities.resources.is_some());
4718    }
4719
4720    // ── Test 2.2 (ServerBuilder): extensions capability ──────────────
4721    #[test]
4722    fn test_2_2_server_builder_skills_sets_extensions_capability() {
4723        let server = Server::builder()
4724            .name("test")
4725            .version("1.0")
4726            .skills(Skills::new().add(Skill::new("a", "")))
4727            .build()
4728            .unwrap();
4729        let ext = server
4730            .capabilities
4731            .extensions
4732            .as_ref()
4733            .expect("extensions should be set");
4734        assert_eq!(
4735            ext.get("io.modelcontextprotocol/skills"),
4736            Some(&serde_json::json!({}))
4737        );
4738    }
4739
4740    // ── Test 2.3 (ServerBuilder): resources capability ───────────────
4741    #[test]
4742    fn test_2_3_server_builder_skills_sets_resources_capability() {
4743        let server = Server::builder()
4744            .name("test")
4745            .version("1.0")
4746            .skills(Skills::new().add(Skill::new("a", "")))
4747            .build()
4748            .unwrap();
4749        let r = server
4750            .capabilities
4751            .resources
4752            .as_ref()
4753            .expect("resources should be set");
4754        assert_eq!(r.subscribe, Some(false));
4755        assert_eq!(r.list_changed, Some(false));
4756    }
4757
4758    // ── Test 2.4a: skills compose with existing resources (ServerBuilder) ─
4759    struct DocsHandler;
4760    #[async_trait]
4761    impl ResourceHandler for DocsHandler {
4762        async fn read(
4763            &self,
4764            uri: &str,
4765            _extra: RequestHandlerExtra,
4766        ) -> Result<crate::types::ReadResourceResult> {
4767            Ok(crate::types::ReadResourceResult::new(vec![Content::text(
4768                format!("DOCS:{uri}"),
4769            )]))
4770        }
4771        async fn list(
4772            &self,
4773            _cursor: Option<String>,
4774            _extra: RequestHandlerExtra,
4775        ) -> Result<crate::types::ListResourcesResult> {
4776            Ok(crate::types::ListResourcesResult::new(vec![
4777                crate::types::ResourceInfo::new("docs://handbook", "handbook"),
4778            ]))
4779        }
4780    }
4781
4782    #[test]
4783    fn test_2_4a_server_builder_skills_compose_with_existing_resources() {
4784        let server = Server::builder()
4785            .name("t")
4786            .version("1.0")
4787            .resources(DocsHandler)
4788            .skill(Skill::new("a", "skill-a"))
4789            .build()
4790            .unwrap();
4791        // Capability state must reflect both surfaces.
4792        assert!(server.capabilities.resources.is_some());
4793        let ext = server.capabilities.extensions.as_ref().unwrap();
4794        assert!(ext.contains_key("io.modelcontextprotocol/skills"));
4795    }
4796
4797    // ── Test 2.7 (ServerBuilder): bootstrap_skill_and_prompt ──────────
4798    #[test]
4799    fn test_2_7_server_builder_bootstrap_skill_and_prompt() {
4800        let server = Server::builder()
4801            .name("t")
4802            .version("1.0")
4803            .bootstrap_skill_and_prompt(Skill::new("c", "body-c"), "my_prompt")
4804            .build()
4805            .unwrap();
4806        assert!(server.has_prompt("my_prompt"));
4807        assert!(server.capabilities.prompts.is_some());
4808        let ext = server
4809            .capabilities
4810            .extensions
4811            .as_ref()
4812            .expect("extensions should be set");
4813        assert!(ext.contains_key("io.modelcontextprotocol/skills"));
4814        assert!(server.capabilities.resources.is_some());
4815    }
4816
4817    // ── Test 2.8: wire-level dual-surface invariant via ServerBuilder ─
4818    #[tokio::test]
4819    async fn test_2_8_bootstrap_skill_and_prompt_byte_equal_invariant() {
4820        let skill = Skill::new("x", "A").with_reference(SkillReference::new(
4821            "ref1.md",
4822            "text/markdown",
4823            "refbody",
4824        ));
4825        let expected_text = skill.as_prompt_text();
4826
4827        let server = Server::builder()
4828            .name("t")
4829            .version("1.0")
4830            .bootstrap_skill_and_prompt(skill, "x")
4831            .build()
4832            .unwrap();
4833
4834        let prompt = server
4835            .get_prompt("x")
4836            .expect("prompt 'x' must be registered");
4837        let result = prompt
4838            .handle(HashMap::new(), RequestHandlerExtra::default())
4839            .await
4840            .unwrap();
4841        assert_eq!(result.messages.len(), 1);
4842        match &result.messages[0].content {
4843            Content::Text { text } => assert_eq!(text, &expected_text),
4844            other => panic!("expected Content::Text, got {other:?}"),
4845        }
4846    }
4847
4848    // ── Test 2.9 (ServerBuilder): duplicate URI panics at .build() ───
4849    #[test]
4850    #[should_panic(expected = "duplicate")]
4851    fn test_2_9_server_builder_skills_panics_on_duplicate_uri_at_build() {
4852        let _ = Server::builder()
4853            .name("t")
4854            .version("1.0")
4855            .skill(Skill::new("x", "a"))
4856            .skill(Skill::new("x", "b"))
4857            .build()
4858            .unwrap();
4859    }
4860
4861    // ── Test 2.9a (ServerBuilder): try_skills returns Err on duplicate ─
4862    #[test]
4863    fn test_2_9a_server_builder_try_skills_returns_err_on_duplicate() {
4864        let res = Server::builder().name("t").version("1.0").try_skills(
4865            Skills::new()
4866                .add(Skill::new("x", "a"))
4867                .add(Skill::new("x", "b")),
4868        );
4869        assert!(res.is_err());
4870        match res {
4871            Err(crate::Error::Validation(_)) => {},
4872            Err(other) => panic!("expected Validation, got {other:?}"),
4873            Ok(_) => panic!("expected Err for duplicate"),
4874        }
4875    }
4876
4877    // ── Test 2.10 (ServerBuilder): capability merge preserves extensions ─
4878    #[test]
4879    fn test_2_10_server_builder_capability_merge_preserves_pre_existing_extensions() {
4880        let mut caps = crate::types::ServerCapabilities::default();
4881        let mut ext = HashMap::new();
4882        ext.insert("some.other/ext".to_string(), serde_json::json!({"foo": 1}));
4883        caps.extensions = Some(ext);
4884
4885        let server = Server::builder()
4886            .name("t")
4887            .version("1.0")
4888            .capabilities(caps)
4889            .skill(Skill::new("a", ""))
4890            .build()
4891            .unwrap();
4892        let ext = server.capabilities.extensions.as_ref().unwrap();
4893        assert!(ext.contains_key("some.other/ext"));
4894        assert!(ext.contains_key("io.modelcontextprotocol/skills"));
4895    }
4896
4897    // ── Test 2.11 (ServerBuilder): accumulator — all skills reachable ─
4898    #[test]
4899    fn test_2_11_server_builder_accumulator_repeated_skill_calls_all_reachable() {
4900        // Build a server with three .skill calls and confirm prompts/caps wire up.
4901        let server = Server::builder()
4902            .name("t")
4903            .version("1.0")
4904            .skill(Skill::new("a", "body-a"))
4905            .skill(Skill::new("b", "body-b"))
4906            .bootstrap_skill_and_prompt(Skill::new("c", "body-c"), "c_prompt")
4907            .build()
4908            .unwrap();
4909        assert!(server.has_prompt("c_prompt"));
4910        assert!(server.capabilities.resources.is_some());
4911    }
4912
4913    // ── Test 2.5a (ServerBuilder): .resources() semantics unchanged ──
4914    #[test]
4915    fn test_2_5a_server_builder_resources_replace_unchanged_no_skills() {
4916        struct A;
4917        #[async_trait]
4918        impl ResourceHandler for A {
4919            async fn read(
4920                &self,
4921                _uri: &str,
4922                _extra: RequestHandlerExtra,
4923            ) -> Result<crate::types::ReadResourceResult> {
4924                Ok(crate::types::ReadResourceResult::new(vec![Content::text(
4925                    "A",
4926                )]))
4927            }
4928            async fn list(
4929                &self,
4930                _cursor: Option<String>,
4931                _extra: RequestHandlerExtra,
4932            ) -> Result<crate::types::ListResourcesResult> {
4933                Ok(crate::types::ListResourcesResult::new(vec![]))
4934            }
4935        }
4936        struct B;
4937        #[async_trait]
4938        impl ResourceHandler for B {
4939            async fn read(
4940                &self,
4941                _uri: &str,
4942                _extra: RequestHandlerExtra,
4943            ) -> Result<crate::types::ReadResourceResult> {
4944                Ok(crate::types::ReadResourceResult::new(vec![Content::text(
4945                    "B",
4946                )]))
4947            }
4948            async fn list(
4949                &self,
4950                _cursor: Option<String>,
4951                _extra: RequestHandlerExtra,
4952            ) -> Result<crate::types::ListResourcesResult> {
4953                Ok(crate::types::ListResourcesResult::new(vec![]))
4954            }
4955        }
4956
4957        let server = Server::builder()
4958            .name("t")
4959            .version("1.0")
4960            .resources(A)
4961            .resources(B)
4962            .build()
4963            .unwrap();
4964        // No skills registered → no composition. Capabilities reflect resources only.
4965        assert!(server.capabilities.resources.is_some());
4966        // Skills extension should NOT have been auto-set.
4967        let ext = server.capabilities.extensions.as_ref();
4968        if let Some(ext_map) = ext {
4969            assert!(!ext_map.contains_key("io.modelcontextprotocol/skills"));
4970        }
4971    }
4972}