pub struct McpServerBuilder { /* private fields */ }Expand description
Builder for MCP servers
Implementations§
Source§impl McpServerBuilder
impl McpServerBuilder
Sourcepub fn instructions(self, instructions: impl Into<String>) -> Self
pub fn instructions(self, instructions: impl Into<String>) -> Self
Sets usage instructions for MCP clients
Sourcepub fn tool<T: McpTool + 'static>(self, tool: T) -> Self
pub fn tool<T: McpTool + 'static>(self, tool: T) -> Self
Registers a tool that clients can execute
Sourcepub fn tool_fn<F, T>(self, func: F) -> Self
pub fn tool_fn<F, T>(self, func: F) -> Self
Register a function tool created with #[mcp_tool] macro
This method provides a more intuitive way to register function tools.
The #[mcp_tool] macro generates a constructor function with the same name
as your async function, so you can use the function name directly.
§Example
use turul_mcp_server::prelude::*;
use std::collections::HashMap;
// Manual tool implementation without derive macros
#[derive(Clone, Default)]
struct AddTool;
// Implement all required traits for ToolDefinition
impl turul_mcp_builders::traits::HasBaseMetadata for AddTool {
fn name(&self) -> &str { "add" }
fn title(&self) -> Option<&str> { Some("Add Numbers") }
}
impl turul_mcp_builders::traits::HasDescription for AddTool {
fn description(&self) -> Option<&str> {
Some("Add two numbers together")
}
}
impl turul_mcp_builders::traits::HasInputSchema for AddTool {
fn input_schema(&self) -> &turul_mcp_protocol::ToolSchema {
use turul_mcp_protocol::schema::JsonSchema;
static SCHEMA: std::sync::OnceLock<turul_mcp_protocol::ToolSchema> = std::sync::OnceLock::new();
SCHEMA.get_or_init(|| {
let mut props = HashMap::new();
props.insert("a".to_string(), JsonSchema::number().with_description("First number"));
props.insert("b".to_string(), JsonSchema::number().with_description("Second number"));
turul_mcp_protocol::ToolSchema::object()
.with_properties(props)
.with_required(vec!["a".to_string(), "b".to_string()])
})
}
}
impl turul_mcp_builders::traits::HasOutputSchema for AddTool {
fn output_schema(&self) -> Option<&turul_mcp_protocol::ToolSchema> { None }
}
impl turul_mcp_builders::traits::HasAnnotations for AddTool {
fn annotations(&self) -> Option<&turul_mcp_protocol::tools::ToolAnnotations> { None }
}
impl turul_mcp_builders::traits::HasToolMeta for AddTool {
fn tool_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
}
#[async_trait]
impl McpTool for AddTool {
async fn call(&self, args: serde_json::Value, _session: Option<SessionContext>)
-> McpResult<turul_mcp_protocol::tools::CallToolResult> {
let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
let result = a + b;
Ok(turul_mcp_protocol::tools::CallToolResult::success(vec![
turul_mcp_protocol::ToolResult::text(format!("{} + {} = {}", a, b, result))
]))
}
}
let server = McpServer::builder()
.name("math-server")
.tool_fn(|| AddTool::default()) // Function returns working tool instance
.build()?;Sourcepub fn tools<T: McpTool + 'static, I: IntoIterator<Item = T>>(
self,
tools: I,
) -> Self
pub fn tools<T: McpTool + 'static, I: IntoIterator<Item = T>>( self, tools: I, ) -> Self
Registers multiple tools in a batch
Sourcepub fn middleware(self, middleware: Arc<dyn McpMiddleware>) -> Self
pub fn middleware(self, middleware: Arc<dyn McpMiddleware>) -> Self
Add middleware to the request/response processing chain
This method is additive - each call adds a new middleware to the stack. Middleware execute in the order they are registered (FIFO):
- Before dispatch: First registered executes first (FIFO order)
- After dispatch: First registered executes last (LIFO/reverse order)
Multiple middleware can be composed by calling this method multiple times. Middleware works identically across all transports (HTTP, Lambda, etc.).
§Behavior with Other Builder Methods
.test_mode(): Does NOT affect middleware - middleware always executes- Non-HTTP builds: Middleware is available but requires manual wiring
§Examples
§Single Middleware
use turul_mcp_server::prelude::*;
use async_trait::async_trait;
use std::sync::Arc;
struct LoggingMiddleware;
#[async_trait]
impl McpMiddleware for LoggingMiddleware {
async fn before_dispatch(
&self,
ctx: &mut RequestContext<'_>,
_session: Option<&dyn turul_mcp_session_storage::SessionView>,
_injection: &mut SessionInjection,
) -> Result<(), MiddlewareError> {
println!("Request: {}", ctx.method());
Ok(())
}
}
let server = McpServer::builder()
.name("my-server")
.middleware(Arc::new(LoggingMiddleware))
.build()?;§Multiple Middleware Composition
use turul_mcp_server::prelude::*;
use async_trait::async_trait;
use std::sync::Arc;
use serde_json::json;
// Execution order:
// Before dispatch: Auth → Logging → RateLimit
// After dispatch: RateLimit → Logging → Auth (reverse)
let server = McpServer::builder()
.name("my-server")
.middleware(Arc::new(AuthMiddleware)) // 1st before, 3rd after
.middleware(Arc::new(LoggingMiddleware)) // 2nd before, 2nd after
.middleware(Arc::new(RateLimitMiddleware)) // 3rd before, 1st after
.build()?;Sourcepub fn resource<R: McpResource + 'static>(self, resource: R) -> Self
pub fn resource<R: McpResource + 'static>(self, resource: R) -> Self
Register a resource with the server
Automatically detects if the resource URI contains template variables (e.g., {ticker}, {id})
and registers it as either a static resource or template resource accordingly.
This eliminates the need to manually call .template_resource() for templated URIs.
§Examples
use turul_mcp_server::prelude::*;
use std::collections::HashMap;
// Manual resource implementation without derive macros
#[derive(Clone)]
struct ConfigResource {
data: String,
}
// Implement all required traits for ResourceDefinition
impl turul_mcp_builders::traits::HasResourceMetadata for ConfigResource {
fn name(&self) -> &str { "config" }
fn title(&self) -> Option<&str> { Some("Configuration") }
}
impl turul_mcp_builders::traits::HasResourceDescription for ConfigResource {
fn description(&self) -> Option<&str> {
Some("Application configuration file")
}
}
impl turul_mcp_builders::traits::HasResourceUri for ConfigResource {
fn uri(&self) -> &str { "file:///config.json" }
}
impl turul_mcp_builders::traits::HasResourceMimeType for ConfigResource {
fn mime_type(&self) -> Option<&str> { Some("application/json") }
}
impl turul_mcp_builders::traits::HasResourceSize for ConfigResource {
fn size(&self) -> Option<u64> { Some(self.data.len() as u64) }
}
impl turul_mcp_builders::traits::HasResourceAnnotations for ConfigResource {
fn annotations(&self) -> Option<&turul_mcp_protocol::meta::Annotations> { None }
}
impl turul_mcp_builders::traits::HasResourceMeta for ConfigResource {
fn resource_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
}
#[async_trait]
impl McpResource for ConfigResource {
async fn read(&self, _params: Option<serde_json::Value>, _session: Option<&SessionContext>)
-> McpResult<Vec<turul_mcp_protocol::ResourceContent>> {
Ok(vec![turul_mcp_protocol::ResourceContent::text(
self.uri(),
&self.data
)])
}
}
let config = ConfigResource {
data: r#"{"debug": true, "port": 8080}"#.to_string(),
};
let server = McpServer::builder()
.name("resource-server")
.resource(config) // Working resource with actual data
.build()?;Sourcepub fn resource_fn<F, R>(self, func: F) -> Selfwhere
F: Fn() -> R,
R: McpResource + 'static,
pub fn resource_fn<F, R>(self, func: F) -> Selfwhere
F: Fn() -> R,
R: McpResource + 'static,
Register a function resource created with #[mcp_resource] macro
This method provides a more intuitive way to register function resources.
The #[mcp_resource] macro generates a constructor function with the same name
as your async function, so you can use the function name directly.
§Example
use turul_mcp_server::prelude::*;
use std::collections::HashMap;
// Manual resource implementation without derive macros
#[derive(Clone)]
struct DataResource {
content: String,
}
// Implement all required traits for ResourceDefinition (same as resource() example)
impl turul_mcp_builders::traits::HasResourceMetadata for DataResource {
fn name(&self) -> &str { "data" }
fn title(&self) -> Option<&str> { Some("Data File") }
}
impl turul_mcp_builders::traits::HasResourceDescription for DataResource {
fn description(&self) -> Option<&str> { Some("Sample data file") }
}
impl turul_mcp_builders::traits::HasResourceUri for DataResource {
fn uri(&self) -> &str { "file:///data/sample.json" }
}
impl turul_mcp_builders::traits::HasResourceMimeType for DataResource {
fn mime_type(&self) -> Option<&str> { Some("application/json") }
}
impl turul_mcp_builders::traits::HasResourceSize for DataResource {
fn size(&self) -> Option<u64> { Some(self.content.len() as u64) }
}
impl turul_mcp_builders::traits::HasResourceAnnotations for DataResource {
fn annotations(&self) -> Option<&turul_mcp_protocol::meta::Annotations> { None }
}
impl turul_mcp_builders::traits::HasResourceMeta for DataResource {
fn resource_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
}
#[async_trait]
impl McpResource for DataResource {
async fn read(&self, _params: Option<serde_json::Value>, _session: Option<&SessionContext>)
-> McpResult<Vec<turul_mcp_protocol::ResourceContent>> {
Ok(vec![turul_mcp_protocol::ResourceContent::text(
self.uri(),
&self.content
)])
}
}
let server = McpServer::builder()
.name("data-server")
.resource_fn(|| DataResource {
content: r#"{"items": [1, 2, 3]}"#.to_string()
}) // Function returns working resource instance
.build()?;Sourcepub fn resources<R: McpResource + 'static, I: IntoIterator<Item = R>>(
self,
resources: I,
) -> Self
pub fn resources<R: McpResource + 'static, I: IntoIterator<Item = R>>( self, resources: I, ) -> Self
Register multiple resources
Sourcepub fn template_resource<R: McpResource + 'static>(
self,
template: UriTemplate,
resource: R,
) -> Self
pub fn template_resource<R: McpResource + 'static>( self, template: UriTemplate, resource: R, ) -> Self
Register a resource with explicit URI template support
Note: This method is now optional. The .resource() method automatically detects
template URIs and handles them appropriately. Use this method only when you need
explicit control over template parsing or want to add custom validators.
§Example
use turul_mcp_server::prelude::*;
use turul_mcp_server::uri_template::{UriTemplate, VariableValidator};
use std::collections::HashMap;
// Manual template resource implementation
#[derive(Clone)]
struct TemplateResource {
base_path: String,
}
// Implement all required traits for ResourceDefinition
impl turul_mcp_builders::traits::HasResourceMetadata for TemplateResource {
fn name(&self) -> &str { "template-data" }
fn title(&self) -> Option<&str> { Some("Template Data") }
}
impl turul_mcp_builders::traits::HasResourceDescription for TemplateResource {
fn description(&self) -> Option<&str> { Some("Template-based data resource") }
}
impl turul_mcp_builders::traits::HasResourceUri for TemplateResource {
fn uri(&self) -> &str { "file:///data/{id}.json" }
}
impl turul_mcp_builders::traits::HasResourceMimeType for TemplateResource {
fn mime_type(&self) -> Option<&str> { Some("application/json") }
}
impl turul_mcp_builders::traits::HasResourceSize for TemplateResource {
fn size(&self) -> Option<u64> { None } // Size varies by template
}
impl turul_mcp_builders::traits::HasResourceAnnotations for TemplateResource {
fn annotations(&self) -> Option<&turul_mcp_protocol::meta::Annotations> { None }
}
impl turul_mcp_builders::traits::HasResourceMeta for TemplateResource {
fn resource_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
}
#[async_trait]
impl McpResource for TemplateResource {
async fn read(&self, params: Option<serde_json::Value>, _session: Option<&SessionContext>)
-> McpResult<Vec<turul_mcp_protocol::ResourceContent>> {
let id = params
.as_ref()
.and_then(|p| p.get("id"))
.and_then(|v| v.as_str())
.unwrap_or("default");
let content = format!(r#"{{"id": "{}", "data": "sample content for {}"}}"#, id, id);
Ok(vec![turul_mcp_protocol::ResourceContent::text(
&format!("file:///data/{}.json", id),
&content
)])
}
}
let template = UriTemplate::new("file:///data/{id}.json")?
.with_validator("id", VariableValidator::user_id());
let resource = TemplateResource {
base_path: "/data".to_string(),
};
let server = McpServer::builder()
.name("template-server")
.template_resource(template, resource) // Working template resource
.build()?;Sourcepub fn prompt<P: McpPrompt + 'static>(self, prompt: P) -> Self
pub fn prompt<P: McpPrompt + 'static>(self, prompt: P) -> Self
Registers a prompt template for conversation generation
Sourcepub fn prompts<P: McpPrompt + 'static, I: IntoIterator<Item = P>>(
self,
prompts: I,
) -> Self
pub fn prompts<P: McpPrompt + 'static, I: IntoIterator<Item = P>>( self, prompts: I, ) -> Self
Register multiple prompts
Sourcepub fn elicitation<E: McpElicitation + 'static>(self, elicitation: E) -> Self
pub fn elicitation<E: McpElicitation + 'static>(self, elicitation: E) -> Self
Register an elicitation provider with the server
Sourcepub fn elicitations<E: McpElicitation + 'static, I: IntoIterator<Item = E>>(
self,
elicitations: I,
) -> Self
pub fn elicitations<E: McpElicitation + 'static, I: IntoIterator<Item = E>>( self, elicitations: I, ) -> Self
Register multiple elicitation providers
Sourcepub fn sampling_provider<S: McpSampling + 'static>(self, sampling: S) -> Self
pub fn sampling_provider<S: McpSampling + 'static>(self, sampling: S) -> Self
Register a sampling provider with the server
Sourcepub fn sampling_providers<S: McpSampling + 'static, I: IntoIterator<Item = S>>(
self,
sampling: I,
) -> Self
pub fn sampling_providers<S: McpSampling + 'static, I: IntoIterator<Item = S>>( self, sampling: I, ) -> Self
Register multiple sampling providers
Sourcepub fn completion_provider<C: McpCompletion + 'static>(
self,
completion: C,
) -> Self
pub fn completion_provider<C: McpCompletion + 'static>( self, completion: C, ) -> Self
Register a completion provider with the server
Sourcepub fn completion_providers<C: McpCompletion + 'static, I: IntoIterator<Item = C>>(
self,
completions: I,
) -> Self
pub fn completion_providers<C: McpCompletion + 'static, I: IntoIterator<Item = C>>( self, completions: I, ) -> Self
Register multiple completion providers
Sourcepub fn logger<L: McpLogger + 'static>(self, logger: L) -> Self
pub fn logger<L: McpLogger + 'static>(self, logger: L) -> Self
Register a logger with the server
Sourcepub fn loggers<L: McpLogger + 'static, I: IntoIterator<Item = L>>(
self,
loggers: I,
) -> Self
pub fn loggers<L: McpLogger + 'static, I: IntoIterator<Item = L>>( self, loggers: I, ) -> Self
Register multiple loggers
Sourcepub fn root_provider<R: McpRoot + 'static>(self, root: R) -> Self
pub fn root_provider<R: McpRoot + 'static>(self, root: R) -> Self
Register a root provider with the server
Sourcepub fn root_providers<R: McpRoot + 'static, I: IntoIterator<Item = R>>(
self,
roots: I,
) -> Self
pub fn root_providers<R: McpRoot + 'static, I: IntoIterator<Item = R>>( self, roots: I, ) -> Self
Register multiple root providers
Sourcepub fn notification_provider<N: McpNotification + 'static>(
self,
notification: N,
) -> Self
pub fn notification_provider<N: McpNotification + 'static>( self, notification: N, ) -> Self
Register a notification provider with the server
Sourcepub fn notification_providers<N: McpNotification + 'static, I: IntoIterator<Item = N>>(
self,
notifications: I,
) -> Self
pub fn notification_providers<N: McpNotification + 'static, I: IntoIterator<Item = N>>( self, notifications: I, ) -> Self
Register multiple notification providers
Sourcepub fn sampler<S: McpSampling + 'static>(self, sampling: S) -> Self
pub fn sampler<S: McpSampling + 'static>(self, sampling: S) -> Self
Register a sampler - convenient alias for sampling_provider Automatically uses “sampling/createMessage” method
Sourcepub fn completer<C: McpCompletion + 'static>(self, completion: C) -> Self
pub fn completer<C: McpCompletion + 'static>(self, completion: C) -> Self
Register a completer - convenient alias for completion_provider Automatically uses “completion/complete” method
Sourcepub fn notification_type<N: McpNotification + 'static + Default>(self) -> Self
pub fn notification_type<N: McpNotification + 'static + Default>(self) -> Self
Register a notification by type - type determines method automatically
This enables the .notification::<T>() pattern from universal-turul-mcp-server
Sourcepub fn handler<H: McpHandler + 'static>(self, handler: H) -> Self
pub fn handler<H: McpHandler + 'static>(self, handler: H) -> Self
Register a handler with the server
Sourcepub fn handlers<H: McpHandler + 'static, I: IntoIterator<Item = H>>(
self,
handlers: I,
) -> Self
pub fn handlers<H: McpHandler + 'static, I: IntoIterator<Item = H>>( self, handlers: I, ) -> Self
Register multiple handlers
Sourcepub fn with_completion(self) -> Self
pub fn with_completion(self) -> Self
Add completion support
Sourcepub fn with_prompts(self) -> Self
pub fn with_prompts(self) -> Self
Add prompts support
Sourcepub fn with_resources(self) -> Self
pub fn with_resources(self) -> Self
Add resources support
Note: This method is now optional. The framework automatically calls this
when resources are registered via .resource() or .template_resource().
You only need to call this explicitly if you want to enable resource capabilities
without registering any resources.
Sourcepub fn with_logging(self) -> Self
pub fn with_logging(self) -> Self
Add logging support
Sourcepub fn with_roots(self) -> Self
pub fn with_roots(self) -> Self
Add roots support
Sourcepub fn with_sampling(self) -> Self
pub fn with_sampling(self) -> Self
Add sampling support
Sourcepub fn with_elicitation(self) -> Self
pub fn with_elicitation(self) -> Self
Add elicitation support with default mock provider
Sourcepub fn with_elicitation_provider<P: ElicitationProvider + 'static>(
self,
provider: P,
) -> Self
pub fn with_elicitation_provider<P: ElicitationProvider + 'static>( self, provider: P, ) -> Self
Add elicitation support with custom provider
Sourcepub fn with_notifications(self) -> Self
pub fn with_notifications(self) -> Self
Add notifications support
Sourcepub fn session_timeout_minutes(self, minutes: u64) -> Self
pub fn session_timeout_minutes(self, minutes: u64) -> Self
Configure session timeout (in minutes, default: 30)
Sourcepub fn session_cleanup_interval_seconds(self, seconds: u64) -> Self
pub fn session_cleanup_interval_seconds(self, seconds: u64) -> Self
Configure session cleanup interval (in seconds, default: 60)
Sourcepub fn strict_lifecycle(self, strict: bool) -> Self
pub fn strict_lifecycle(self, strict: bool) -> Self
Enable strict MCP lifecycle enforcement
When enabled, the server will reject all operations (tools, resources, etc.)
until the client sends notifications/initialized after receiving the
initialize response.
Default: false (lenient mode) - for compatibility with existing clients Production: consider true - for strict MCP spec compliance
§Example
use turul_mcp_server::McpServer;
let server = McpServer::builder()
.name("strict-server")
.version("1.0.0")
.strict_lifecycle(true) // Enable strict enforcement
.build()?;Sourcepub fn with_strict_lifecycle(self) -> Self
pub fn with_strict_lifecycle(self) -> Self
Enable strict MCP lifecycle enforcement (convenience method)
Equivalent to .strict_lifecycle(true). Enables strict enforcement where
all operations are rejected until notifications/initialized is received.
Sourcepub fn test_mode(self) -> Self
pub fn test_mode(self) -> Self
Enable test mode - disables security middleware for test servers
In test mode, ResourcesReadHandler is created without security middleware, allowing custom URI schemes for testing (binary://, memory://, error://, etc.). Production servers should NOT use test mode as it bypasses security controls.
§Example
use turul_mcp_server::McpServer;
let server = McpServer::builder()
.name("test-server")
.version("1.0.0")
.test_mode() // Disable security for testing
.with_resources()
.build()?;Sourcepub fn with_long_sessions(self) -> Self
pub fn with_long_sessions(self) -> Self
Configure sessions with recommended defaults for long-running sessions
Sourcepub fn with_short_sessions(self) -> Self
pub fn with_short_sessions(self) -> Self
Configure sessions with recommended defaults for short-lived sessions
Sourcepub fn with_session_storage<S: SessionStorage<Error = SessionStorageError> + 'static>(
self,
storage: Arc<S>,
) -> Self
pub fn with_session_storage<S: SessionStorage<Error = SessionStorageError> + 'static>( self, storage: Arc<S>, ) -> Self
Configure session storage backend (defaults to InMemory if not specified)
Sourcepub fn bind_address(self, addr: SocketAddr) -> Self
pub fn bind_address(self, addr: SocketAddr) -> Self
Set HTTP bind address (requires “http” feature)
Trait Implementations§
Auto Trait Implementations§
impl Freeze for McpServerBuilder
impl !RefUnwindSafe for McpServerBuilder
impl Send for McpServerBuilder
impl Sync for McpServerBuilder
impl Unpin for McpServerBuilder
impl !UnwindSafe for McpServerBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more