use std::sync::Arc;
use crate::dispatcher::{CachePolicies, VersionDispatcher};
use crate::extension::Extension;
use crate::router::MethodRouter;
use crate::session::SessionBackend;
use crate::tasks::TaskBackend;
use crate::traits::{McpServerCore, WithCompletions, WithPrompts, WithResources, WithTools};
pub struct ServerBuilder<S> {
server: S,
router: MethodRouter<S>,
tasks: bool,
strict_elicitation_keys: bool,
session_idle_timeout: Option<std::time::Duration>,
session_backend: Option<Arc<dyn SessionBackend>>,
task_backend: Option<Arc<dyn TaskBackend>>,
extensions: Vec<Arc<dyn Extension>>,
cache: Option<CachePolicies>,
state_key: Option<[u8; 32]>,
visibility: Option<Arc<dyn crate::VisibilityPolicy>>,
}
impl<S: McpServerCore> ServerBuilder<S> {
#[must_use]
pub fn new(server: S) -> Self {
Self {
server,
router: MethodRouter::new(),
tasks: false,
strict_elicitation_keys: false,
session_idle_timeout: None,
session_backend: None,
task_backend: None,
extensions: Vec::new(),
cache: None,
state_key: None,
visibility: None,
}
}
#[must_use]
pub fn from_parts(server: S, router: MethodRouter<S>) -> Self {
Self {
server,
router,
tasks: false,
strict_elicitation_keys: false,
session_idle_timeout: None,
session_backend: None,
task_backend: None,
extensions: Vec::new(),
cache: None,
state_key: None,
visibility: None,
}
}
#[must_use]
pub fn cache_policy(mut self, cache: impl Into<CachePolicies>) -> Self {
self.cache = Some(cache.into());
self
}
#[must_use]
pub fn session_idle_timeout(mut self, timeout: std::time::Duration) -> Self {
self.session_idle_timeout = Some(timeout);
self
}
#[must_use]
pub fn with_tasks(mut self) -> Self {
self.tasks = true;
self
}
#[must_use]
pub fn with_task_backend(mut self, backend: Arc<dyn TaskBackend>) -> Self {
self.task_backend = Some(backend);
self
}
#[must_use]
pub fn with_session_backend(mut self, backend: Arc<dyn SessionBackend>) -> Self {
self.session_backend = Some(backend);
self
}
#[must_use]
pub fn strict_elicitation_keys(mut self) -> Self {
self.strict_elicitation_keys = true;
self
}
#[must_use]
pub fn with_logging(mut self) -> Self {
self.router = self.router.with_logging();
self
}
#[must_use]
pub fn with_extension(mut self, extension: Arc<dyn Extension>) -> Self {
self.extensions.push(extension);
self
}
#[must_use]
pub fn with_visibility(mut self, policy: Arc<dyn crate::VisibilityPolicy>) -> Self {
self.visibility = Some(policy);
self
}
#[must_use]
pub fn with_tools(mut self) -> Self
where
S: WithTools,
{
self.router = self.router.with_tools();
self
}
#[must_use]
pub fn with_resources(mut self) -> Self
where
S: WithResources,
{
self.router = self.router.with_resources();
self
}
#[must_use]
pub fn with_prompts(mut self) -> Self
where
S: WithPrompts,
{
self.router = self.router.with_prompts();
self
}
#[must_use]
pub fn with_completions(mut self) -> Self
where
S: WithCompletions,
{
self.router = self.router.with_completions();
self
}
#[must_use]
pub fn with_state_key(mut self, key: [u8; 32]) -> Self {
self.state_key = Some(key);
self
}
pub(crate) fn into_parts(self) -> (S, MethodRouter<S>) {
(self.server, self.router)
}
pub(crate) fn dispatcher_setting(&self) -> Option<&'static str> {
let Self {
server: _,
router: _,
tasks,
strict_elicitation_keys,
session_idle_timeout,
session_backend,
task_backend,
extensions,
cache,
state_key,
visibility,
} = self;
if *tasks {
return Some("with_tasks");
}
if *strict_elicitation_keys {
return Some("strict_elicitation_keys");
}
if session_idle_timeout.is_some() {
return Some("session_idle_timeout");
}
if session_backend.is_some() {
return Some("with_session_backend");
}
if task_backend.is_some() {
return Some("with_task_backend");
}
if !extensions.is_empty() {
return Some("with_extension");
}
if cache.is_some() {
return Some("cache_policy");
}
if state_key.is_some() {
return Some("with_state_key");
}
if visibility.is_some() {
return Some("with_visibility");
}
None
}
#[must_use]
pub fn build(self) -> VersionDispatcher<S> {
let mut dispatcher = VersionDispatcher::new(self.server, self.router);
if let Some(backend) = self.task_backend {
dispatcher = dispatcher.with_task_backend(backend);
} else if self.tasks {
dispatcher = dispatcher.with_task_support();
}
if self.strict_elicitation_keys {
dispatcher = dispatcher.strict_elicitation_keys();
}
if let Some(backend) = self.session_backend {
dispatcher = dispatcher.with_session_backend(backend);
} else if let Some(timeout) = self.session_idle_timeout {
dispatcher = dispatcher.with_session_idle_timeout(timeout);
}
for extension in self.extensions {
dispatcher = dispatcher.with_extension(extension);
}
if let Some(cache) = self.cache {
dispatcher = dispatcher.with_cache_policy(cache);
}
if let Some(key) = self.state_key {
dispatcher = dispatcher.with_state_key(key);
}
if let Some(policy) = self.visibility {
dispatcher = dispatcher.with_visibility(policy);
}
dispatcher
}
}
pub trait IntoServerBuilder: McpServerCore + Sized {
fn into_server(self) -> ServerBuilder<Self> {
ServerBuilder::new(self)
}
}
impl<S: McpServerCore> IntoServerBuilder for S {}