Skip to main content

ServerBuilder

Struct ServerBuilder 

Source
pub struct ServerBuilder { /* private fields */ }
Available on non-WebAssembly only.
Expand description

Builder for creating servers.

Implementations§

Source§

impl ServerBuilder

Source

pub fn new() -> Self

Create a new server builder.

Creates a new ServerBuilder with default capabilities and no handlers. Use the builder methods to configure the server before calling build().

§Examples
use pmcp::ServerBuilder;

let builder = ServerBuilder::new();

This is equivalent to using the default implementation:

use pmcp::ServerBuilder;

let builder = ServerBuilder::default();
Source

pub fn name(self, name: impl Into<String>) -> Self

Set the server name.

The server name identifies this MCP server implementation. This is required and will be sent to clients during initialization.

§Arguments
  • name - The name of the server
§Examples
use pmcp::Server;

let server = Server::builder()
    .name("file-manager")
    .version("1.0.0")
    .build()?;
Source

pub fn version(self, version: impl Into<String>) -> Self

Set the server version.

The server version identifies this specific version of the MCP server. This is required and will be sent to clients during initialization.

§Arguments
  • version - The version string (e.g., “1.0.0”, “2.1.3-beta”)
§Examples
use pmcp::Server;

let server = Server::builder()
    .name("data-processor")
    .version("2.1.0")
    .build()?;
Source

pub fn website_url(self, url: impl Into<String>) -> Self

Set the website URL for the server implementation (MCP 2025-11-25).

Source

pub fn with_icons(self, icons: Vec<IconInfo>) -> Self

Set icons for the server implementation (MCP 2025-11-25).

Source

pub fn capabilities(self, capabilities: ServerCapabilities) -> Self

Set server capabilities.

Configures the capabilities that this server supports. Capabilities inform clients about which MCP features are available.

§Arguments
  • capabilities - The server capabilities to advertise
§Examples
use pmcp::{Server, ServerCapabilities, ToolCapabilities};

let mut capabilities = ServerCapabilities::default();
capabilities.tools = Some(ToolCapabilities {
    list_changed: Some(true),
});

let server = Server::builder()
    .name("advanced-server")
    .version("1.0.0")
    .capabilities(capabilities)
    .build()?;
Source

pub fn tool( self, name: impl Into<String>, handler: impl ToolHandler + 'static, ) -> Self

Add a tool handler.

Registers a tool that clients can call via the tools/call method. Tools are the primary way servers provide functionality to clients.

§Arguments
  • name - The name of the tool (used by clients to call it)
  • handler - The handler implementation for this tool
§Examples
use pmcp::{Server, ToolHandler};
use async_trait::async_trait;
use serde_json::Value;

struct FileListTool;

#[async_trait]
impl ToolHandler for FileListTool {
    async fn handle(&self, args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
        let path = args["path"].as_str().unwrap_or(".");
        // List files in path...
        Ok(serde_json::json!({"files": ["file1.txt", "file2.txt"]}))
    }
}

let server = Server::builder()
    .name("file-server")
    .version("1.0.0")
    .tool("list_files", FileListTool{})
    .build()?;
Source

pub fn tool_arc( self, name: impl Into<String>, handler: Arc<dyn ToolHandler>, ) -> Self

Add a tool handler with an Arc.

This variant lets the caller share the handler Arc between the builder and an external in-process handler map (e.g., a downstream toolkit’s handler registry) without writing a delegating wrapper shim. Behavior is otherwise identical to Self::tool: the first registration auto-enables capabilities.tools.

Source

pub fn mcp_server<T: McpServer>(self, server: T) -> Self

Register all tools and prompts from an #[mcp_server] annotated type.

This is the ergonomic counterpart to individually registering tools and prompts. The server instance provides shared state via &self to all tool and prompt methods.

§Examples
use pmcp::ServerBuilder;

#[mcp_server]
impl MyServer {
    #[mcp_tool(description = "Query data")]
    async fn query(&self, args: QueryArgs) -> Result<Value> { /* ... */ }

    #[mcp_prompt(description = "Generate query")]
    async fn query_prompt(&self, args: PromptArgs) -> Result<GetPromptResult> { /* ... */ }
}

let server = MyServer { db };
let builder = ServerBuilder::new()
    .name("my-server")
    .mcp_server(server);
Source

pub fn tool_typed<T, F, Fut>(self, name: impl Into<String>, handler: F) -> Self
where T: DeserializeOwned + JsonSchema + Send + Sync + 'static, F: Fn(T, RequestHandlerExtra) -> Fut + Send + Sync + 'static, Fut: Future<Output = Result<Value>> + Send + 'static,

Available on crate feature schema-generation only.

Add a type-safe tool handler with automatic schema generation.

This method provides first-class support for creating tools with:

  • Automatic JSON schema generation from Rust types
  • Compile-time type safety
  • Runtime validation
  • Field descriptions from doc comments
§Example
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct EchoArgs {
    /// The message to echo
    message: String,
    /// Optional prefix
    prefix: Option<String>,
}

let server = ServerBuilder::new()
    .name("example")
    .tool_typed("echo", |args: EchoArgs, _| {
        Box::pin(async move {
            let message = match args.prefix {
                Some(p) => format!("{}: {}", p, args.message),
                None => args.message,
            };
            Ok(serde_json::json!({ "message": message }))
        })
    })
    .build();
Source

pub fn tool_typed_with_description<T, F, Fut>( self, name: impl Into<String>, description: impl Into<String>, handler: F, ) -> Self
where T: DeserializeOwned + JsonSchema + Send + Sync + 'static, F: Fn(T, RequestHandlerExtra) -> Fut + Send + Sync + 'static, Fut: Future<Output = Result<Value>> + Send + 'static,

Available on crate feature schema-generation only.

Add a type-safe tool handler with automatic schema generation and description.

This is a convenience overload that allows setting a description directly without needing to chain .with_description().

§Example
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct EchoArgs {
    /// The message to echo
    message: String,
    /// Optional prefix
    prefix: Option<String>,
}

let server = ServerBuilder::new()
    .name("example")
    .tool_typed_with_description(
        "echo",
        "Echoes back a message with an optional prefix",
        |args: EchoArgs, _| {
            Box::pin(async move {
                let message = match args.prefix {
                    Some(p) => format!("{}: {}", p, args.message),
                    None => args.message,
                };
                Ok(serde_json::json!({ "message": message }))
            })
        }
    );
Source

pub fn tool_typed_sync<T, F>(self, name: impl Into<String>, handler: F) -> Self
where T: DeserializeOwned + JsonSchema + Send + Sync + 'static, F: Fn(T, RequestHandlerExtra) -> Result<Value> + Send + Sync + 'static,

Available on crate feature schema-generation only.

Add a synchronous type-safe tool handler with automatic schema generation.

Similar to tool_typed but for synchronous handlers.

§Example
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct MathArgs {
    /// First number
    a: f64,
    /// Second number
    b: f64,
    /// Operation to perform
    op: String,
}

let server = ServerBuilder::new()
    .name("example")
    .tool_typed_sync("calculator", |args: MathArgs, _| {
        let result = match args.op.as_str() {
            "add" => args.a + args.b,
            "subtract" => args.a - args.b,
            "multiply" => args.a * args.b,
            "divide" => args.a / args.b,
            _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
        };
        Ok(serde_json::json!({ "result": result }))
    })
    .build();
Source

pub fn tool_typed_sync_with_description<T, F>( self, name: impl Into<String>, description: impl Into<String>, handler: F, ) -> Self
where T: DeserializeOwned + JsonSchema + Send + Sync + 'static, F: Fn(T, RequestHandlerExtra) -> Result<Value> + Send + Sync + 'static,

Available on crate feature schema-generation only.

Add a synchronous type-safe tool handler with automatic schema generation and description.

This is a convenience overload that allows setting a description directly without needing to chain .with_description().

§Example
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct MathArgs {
    /// First number
    a: f64,
    /// Second number
    b: f64,
    /// Operation to perform
    op: String,
}

let server = ServerBuilder::new()
    .name("example")
    .tool_typed_sync_with_description(
        "calculator",
        "Performs synchronous mathematical operations",
        |args: MathArgs, _| {
            let result = match args.op.as_str() {
                "add" => args.a + args.b,
                "subtract" => args.a - args.b,
                "multiply" => args.a * args.b,
                "divide" => args.a / args.b,
                _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
            };
            Ok(serde_json::json!({ "result": result }))
        }
    );
Source

pub fn tool_typed_with_output<TIn, TOut>( self, name: impl Into<String>, handler: impl Fn(TIn, RequestHandlerExtra) -> Pin<Box<dyn Future<Output = Result<TOut>> + Send>> + Send + Sync + 'static, ) -> Self
where TIn: DeserializeOwned + JsonSchema + Send + Sync + 'static, TOut: Serialize + JsonSchema + Send + Sync + 'static,

Available on crate feature schema-generation only.

Add a type-safe tool handler with both input and output typing.

This method provides full type safety for both input and output types, which is useful for testing, documentation, and API contracts. Note that output schemas are not part of the MCP protocol but can be valuable for development and integration testing.

§Type Parameters
  • TIn - Input type that implements JsonSchema, Deserialize, Send, Sync
  • TOut - Output type that implements JsonSchema, Serialize, Send, Sync
§Example
use pmcp::{ServerBuilder, TypedToolWithOutput};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(JsonSchema, Deserialize)]
struct MathInput { a: f64, b: f64, op: String }

#[derive(JsonSchema, Serialize)]
struct MathOutput { result: f64, operation: String }

let server = ServerBuilder::new()
    .name("example")
    .tool_typed_with_output::<MathInput, MathOutput>("math", |args, _| {
        Box::pin(async move {
            let result = match args.op.as_str() {
                "add" => args.a + args.b,
                "subtract" => args.a - args.b,
                _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
            };
            Ok(MathOutput {
                result,
                operation: args.op,
            })
        })
    });
Source

pub fn tool_typed_with_output_and_description<TIn, TOut>( self, name: impl Into<String>, description: impl Into<String>, handler: impl Fn(TIn, RequestHandlerExtra) -> Pin<Box<dyn Future<Output = Result<TOut>> + Send>> + Send + Sync + 'static, ) -> Self
where TIn: DeserializeOwned + JsonSchema + Send + Sync + 'static, TOut: Serialize + JsonSchema + Send + Sync + 'static,

Available on crate feature schema-generation only.

Add a type-safe tool handler with both input and output typing and description.

This is a convenience overload that allows setting a description directly without needing to chain .with_description().

§Example
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(JsonSchema, Deserialize)]
struct MathInput { a: f64, b: f64, op: String }

#[derive(JsonSchema, Serialize)]
struct MathOutput { result: f64, operation: String }

let server = ServerBuilder::new()
    .name("example")
    .tool_typed_with_output_and_description::<MathInput, MathOutput>(
        "math",
        "Performs basic mathematical operations on two numbers",
        |args, _| {
            Box::pin(async move {
                let result = match args.op.as_str() {
                    "add" => args.a + args.b,
                    "subtract" => args.a - args.b,
                    _ => return Err(pmcp::Error::Validation("Unknown operation".into())),
                };
                Ok(MathOutput { result, operation: args.op })
            })
        }
    );
Source

pub fn prompt( self, name: impl Into<String>, handler: impl PromptHandler + 'static, ) -> Self

Add a prompt handler.

Registers a prompt that clients can retrieve via the prompts/get method. Prompts provide templates that clients can use for various tasks.

§Arguments
  • name - The name of the prompt (used by clients to retrieve it)
  • handler - The handler implementation for this prompt
§Examples
use pmcp::{Server, PromptHandler, GetPromptResult, PromptMessage, Content};
use async_trait::async_trait;
use std::collections::HashMap;

struct CodeReviewPrompt;

#[async_trait]
impl PromptHandler for CodeReviewPrompt {
    async fn handle(&self, args: HashMap<String, String>, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<GetPromptResult> {
        let language = args.get("language").map(|s| s.as_str()).unwrap_or("unknown");
        Ok(GetPromptResult::new(
            vec![PromptMessage::user(pmcp::Content::text(format!(
                "Please review this {} code:",
                language
            )))],
            Some(format!("Code review prompt for {}", language)),
        ))
    }
}

let server = Server::builder()
    .name("code-server")
    .version("1.0.0")
    .prompt("code_review", CodeReviewPrompt{})
    .build()?;
Source

pub fn prompt_arc( self, name: impl Into<String>, handler: Arc<dyn PromptHandler>, ) -> Self

Add a prompt handler with an Arc.

This variant lets the caller share the handler Arc between the builder and an external in-process handler map (e.g., a downstream toolkit’s handler registry) without writing a delegating wrapper shim. Behavior is otherwise identical to Self::prompt: the first registration auto-enables capabilities.prompts.

Source

pub fn prompt_workflow(self, workflow: SequentialWorkflow) -> Result<Self>

Register a workflow-based prompt with automatic validation.

This method validates the workflow before registration and converts it to a prompt handler. The workflow’s instructions become the prompt messages, and the workflow’s arguments become the prompt arguments.

§Arguments
  • workflow - The workflow definition to register as a prompt
§Errors

Returns an error if the workflow validation fails (e.g., undefined bindings, undefined prompt arguments, etc.).

§Examples
use pmcp::{Server, ServerBuilder};
use pmcp::server::workflow::{SequentialWorkflow, InternalPromptMessage};
use pmcp::types::Role;

let workflow = SequentialWorkflow::new(
    "code_review_workflow",
    "Review code with multiple steps"
)
.argument("code", "Code to review", true)
.instruction(InternalPromptMessage::new(
    Role::System,
    "You are a code reviewer. Review the provided code carefully."
));

let server = Server::builder()
    .name("code-server")
    .version("1.0.0")
    .prompt_workflow(workflow)?
    .build()?;
Source

pub fn resources(self, handler: impl ResourceHandler + 'static) -> Self

Set the resource handler.

Registers a resource handler that provides access to server resources. Resources allow clients to read files, configurations, or other data.

§Arguments
  • handler - The resource handler implementation
§Examples
use pmcp::{Server, ResourceHandler, ReadResourceResult, ListResourcesResult, ResourceInfo};
use async_trait::async_trait;

struct FileResourceHandler;

#[async_trait]
impl ResourceHandler for FileResourceHandler {
    async fn read(&self, uri: &str, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<ReadResourceResult> {
        // Read file content...
        Ok(ReadResourceResult::new(vec![pmcp::Content::text("File content here")]))
    }

    async fn list(&self, _cursor: Option<String>, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<ListResourcesResult> {
        Ok(ListResourcesResult::new(vec![
            pmcp::ResourceInfo::new("file://example.txt", "example.txt")
                .with_description("Example file")
                .with_mime_type("text/plain"),
        ]))
    }
}

let server = Server::builder()
    .name("file-server")
    .version("1.0.0")
    .resources(FileResourceHandler{})
    .build()?;
Source

pub fn resources_arc(self, handler: Arc<dyn ResourceHandler>) -> Self

Set the resource handler with an Arc.

This variant lets the caller share the handler Arc between the builder and an external in-process handler map without writing a delegating wrapper. Behavior is otherwise identical to Self::resources: the first registration auto-enables capabilities.resources.

Source

pub fn sampling(self, handler: impl SamplingHandler + 'static) -> Self

Set the sampling handler.

Registers a sampling handler that provides LLM functionality. This allows the server to act as a language model provider.

§Arguments
  • handler - The sampling handler implementation
§Examples
use pmcp::{Server, SamplingHandler, CreateMessageParams, CreateMessageResult};
use async_trait::async_trait;

struct MockLLM;

#[async_trait]
impl SamplingHandler for MockLLM {
    async fn create_message(&self, params: CreateMessageParams, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<CreateMessageResult> {
        // Process the messages and generate a response
        Ok(CreateMessageResult::new(pmcp::Content::text("Generated response"), "mock-llm-v1")
            .with_usage(pmcp::TokenUsage::new(10, 5, 15))
            .with_stop_reason("end_of_text"))
    }
}

let server = Server::builder()
    .name("llm-server")
    .version("1.0.0")
    .sampling(MockLLM{})
    .build()?;
Source

pub fn sampling_arc(self, handler: Arc<dyn SamplingHandler>) -> Self

Set the sampling handler with an Arc.

This variant lets the caller share the handler Arc between the builder and an external in-process handler map without writing a delegating wrapper. Uses the donor’s if is_none capability auto-enable so an explicit prior .capabilities(custom) is not clobbered by a later _arc registration.

Source

pub fn auth_provider(self, provider: impl AuthProvider + 'static) -> Self

Build the server.

Constructs the final Server instance from the configured builder. This validates that required fields (name and version) are set.

§Examples
use pmcp::{Server, ToolHandler};
use async_trait::async_trait;
use serde_json::Value;

struct PingTool;

#[async_trait]
impl ToolHandler for PingTool {
    async fn handle(&self, _args: Value, _extra: pmcp::RequestHandlerExtra) -> pmcp::Result<Value> {
        Ok(serde_json::json!({"response": "pong"}))
    }
}

let server = Server::builder()
    .name("ping-server")
    .version("1.0.0")
    .tool("ping", PingTool{})
    .build()?;

// Server is now ready to run
// server.run_stdio().await?;

Set the authentication provider.

Configures an authentication provider that will validate incoming requests. When set, the server will use this provider to authenticate requests before processing them.

§Arguments
  • provider - The authentication provider implementation
§Examples
use pmcp::{Server, auth::ProxyProvider};

let auth_provider = ProxyProvider::with_upstream("https://oauth.example.com");

let server = Server::builder()
    .name("secure-server")
    .version("1.0.0")
    .auth_provider(auth_provider)
    .build()?;
Source

pub fn auth_provider_arc(self, provider: Arc<dyn AuthProvider>) -> Self

Set the authentication provider with an Arc.

This variant lets the caller share the provider Arc between the builder and an external in-process registry without writing a delegating wrapper. Behavior is otherwise identical to Self::auth_provider.

Source

pub fn tool_authorizer(self, authorizer: impl ToolAuthorizer + 'static) -> Self

Set the tool authorizer.

Configures a tool authorizer for fine-grained access control. The authorizer determines which tools authenticated users can access based on their authentication context.

§Arguments
  • authorizer - The tool authorization implementation
§Examples
use pmcp::{Server, auth::ScopeBasedAuthorizer};

let authorizer = ScopeBasedAuthorizer::new()
    .require_scopes("sensitive_tool", vec!["admin".to_string()])
    .default_scopes(vec!["read".to_string()]);

let server = Server::builder()
    .name("secure-server")
    .version("1.0.0")
    .tool_authorizer(authorizer)
    .build()?;
Source

pub fn tool_authorizer_arc(self, authorizer: Arc<dyn ToolAuthorizer>) -> Self

Set the tool authorizer with an Arc.

This variant lets the caller share the authorizer Arc between the builder and an external in-process registry without writing a delegating wrapper. Mirrors Self::tool_authorizer’s protection-clearing semantics: if any prior protect_tool() configurations exist, they are cleared and a tracing::warn! is emitted under target "mcp.auth", since a custom authorizer supersedes scope-based tool protections.

Source

pub fn protect_tool( self, tool_name: impl Into<String>, scopes: Vec<String>, ) -> Self

Protect a specific tool with required scopes.

This is a convenience method that creates or updates a scope-based authorizer to require specific scopes for accessing the named tool.

§Arguments
  • tool_name - The name of the tool to protect
  • scopes - The required scopes for accessing this tool
§Examples
use pmcp::Server;

let server = Server::builder()
    .name("secure-server")
    .version("1.0.0")
    .protect_tool("delete_data", vec!["admin".to_string(), "write".to_string()])
    .protect_tool("read_data", vec!["read".to_string()])
    .build()?;
Source

pub fn tool_middleware(self, middleware: Arc<dyn ToolMiddleware>) -> Self

Add tool middleware for cross-cutting concerns.

Tool middleware allows you to inject cross-cutting concerns into tool execution, such as OAuth token injection, logging, metrics, or request transformation. Middleware is executed in the order it’s added, both for request processing (before tool execution) and response processing (after tool execution).

This method brings middleware support to the high-level ServerBuilder API, enabling developers to use both typed tool registration AND middleware without dropping down to the lower-level ServerCoreBuilder API.

§Arguments
  • middleware - The middleware implementation to add to the chain
§Examples
§OAuth Token Injection Middleware
use pmcp::server::tool_middleware::{ToolMiddleware, ToolContext};
use pmcp::server::cancellation::RequestHandlerExtra;
use pmcp::Server;
use std::sync::Arc;
use async_trait::async_trait;
use serde_json::Value;

struct OAuthInjectionMiddleware;

#[async_trait]
impl ToolMiddleware for OAuthInjectionMiddleware {
    async fn on_request(
        &self,
        _tool_name: &str,
        _args: &mut Value,
        extra: &mut RequestHandlerExtra,
        _context: &ToolContext,
    ) -> pmcp::Result<()> {
        // Extract OAuth token from auth_context and inject into metadata
        if let Some(auth_ctx) = extra.auth_context() {
            if let Some(token) = &auth_ctx.token {
                extra.set_metadata("oauth_token".to_string(), token.clone());
            }
        }
        Ok(())
    }
}

let server = Server::builder()
    .name("oauth-server")
    .version("1.0.0")
    .tool_middleware(Arc::new(OAuthInjectionMiddleware))
    .build()?;
§Combining with Typed Tools
use pmcp::Server;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct ListGamesArgs {
    filter: Option<String>,
}

let server = Server::builder()
    .name("game-server")
    .version("1.0.0")
    .tool_typed_with_description(
        "list_games",
        "List all available games",
        |args: ListGamesArgs, extra| {
            Box::pin(async move {
                // Access OAuth token injected by middleware
                let _token = extra.get_metadata("oauth_token");
                Ok(serde_json::json!({"games": []}))
            })
        }
    )
    // .tool_middleware(Arc::new(oauth_middleware))  // Works with typed tools!
    .build()?;
§Middleware Execution Order

Multiple middleware are executed in FIFO order for requests and FIFO for responses:

Request:  Middleware1 → Middleware2 → Tool Handler
Response: Tool Handler → Middleware1 → Middleware2
Source

pub fn with_observability(self, config: ObservabilityConfig) -> Self

Enable observability for this server.

This adds observability middleware that provides:

  • Distributed tracing with trace/span IDs
  • Request/response event logging
  • Metrics emission (duration, count, errors)

The backend is automatically selected based on the configuration:

  • “console” - Pretty or JSON output to stdout (development)
  • “cloudwatch” - AWS CloudWatch EMF format (production)
  • “null” - Discards all events (testing)
§Examples
use pmcp::Server;
use pmcp::server::observability::ObservabilityConfig;

// Development: console output with pretty printing
let server = Server::builder()
    .name("my-server")
    .version("1.0.0")
    .with_observability(ObservabilityConfig::development())
    .build()?;

// Production: CloudWatch with EMF metrics
let server = Server::builder()
    .name("my-server")
    .version("1.0.0")
    .with_observability(ObservabilityConfig::production())
    .build()?;

// Auto-detect environment (Lambda vs local)
let config = if std::env::var("AWS_LAMBDA_FUNCTION_NAME").is_ok() {
    ObservabilityConfig::production()
} else {
    ObservabilityConfig::development()
};
let server = Server::builder()
    .name("my-server")
    .version("1.0.0")
    .with_observability(config)
    .build()?;
Source

pub fn with_observability_backend( self, config: ObservabilityConfig, backend: Arc<dyn ObservabilityBackend>, ) -> Self

Enable observability with a custom backend.

Use this when you need a custom backend implementation (e.g., Datadog, custom metrics).

§Examples
use pmcp::Server;
use pmcp::server::observability::{ObservabilityConfig, ObservabilityBackend};
use std::sync::Arc;

struct MyCustomBackend;

#[async_trait]
impl ObservabilityBackend for MyCustomBackend {
    // ... custom implementation
}

let server = Server::builder()
    .name("my-server")
    .version("1.0.0")
    .with_observability_backend(
        ObservabilityConfig::development(),
        Arc::new(MyCustomBackend),
    )
    .build()?;
Source

pub fn with_tool_description( self, tool_name: impl Into<String>, description: impl Into<String>, ) -> Self

👎Deprecated since 1.6.0:

Use tool_typed_with_description() and similar variants instead

Add a description to a tool (Note: Limited support).

Important: Due to the immutable design of tool handlers, this method cannot retroactively add descriptions to already-registered tools.

Recommended: Use the *_with_description variants instead:

  • .tool_typed_with_description()
  • .tool_typed_sync_with_description()
  • .tool_typed_with_output_and_description()

This method is provided for API completeness but will log warnings when used, encouraging migration to the preferred approaches.

§Preferred Examples
use pmcp::ServerBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct MathArgs { a: f64, b: f64 }

// Preferred: Use the direct description variants
let server = ServerBuilder::new()
    .name("example")
    .tool_typed_with_description(
        "add",
        "Adds two numbers together",
        |args: MathArgs, _| {
            Box::pin(async move {
                Ok(serde_json::json!({ "result": args.a + args.b }))
            })
        }
    )
    .build();
Source

pub fn with_http_middleware( self, middleware: Arc<ServerHttpMiddlewareChain>, ) -> Self

Available on crate feature streamable-http only.

Configure HTTP middleware chain for StreamableHttpServer.

This is a convenience method that stores the HTTP middleware chain so it can be retrieved later when creating a StreamableHttpServer.

§Arguments
  • middleware - The HTTP middleware chain
§Examples
use pmcp::Server;
use pmcp::server::http_middleware::{ServerHttpLoggingMiddleware, ServerHttpMiddlewareChain};
use std::sync::Arc;

let mut http_chain = ServerHttpMiddlewareChain::new();
http_chain.add(Arc::new(ServerHttpLoggingMiddleware::new()));

let server = Server::builder()
    .name("my-server")
    .version("1.0.0")
    .with_http_middleware(Arc::new(http_chain))
    .build()?;

// Later when creating StreamableHttpServer:
// let config = StreamableHttpServerConfig {
//     http_middleware: server.http_middleware(),
//     ..Default::default()
// };
Source

pub fn with_host_layer(self, host: HostType) -> Self

Available on crate feature mcp-apps only.

Add a host layer for MCP Apps metadata enrichment.

Host layers enrich tool _meta at build time with host-specific keys. For example, HostType::ChatGpt adds openai/outputTemplate and openai/widgetAccessible derived from the standard ui.resourceUri.

This is opt-in — standard MCP Apps hosts (Claude Desktop, etc.) work without any host layer. Duplicates are ignored.

Source

pub fn build(self) -> Result<Server>

Build the server.

Constructs the final Server instance from the configured builder. This validates that required fields (name and version) are set.

§Errors

Returns an error if:

  • The server name is not set
  • The server version is not set

Trait Implementations§

Source§

impl Debug for ServerBuilder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ServerBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,