use crate::handler::{PromptHandler, ResourceHandler, ServerHandler, TaskHandler, ToolHandler};
use mcpkit_core::capability::ServerCapabilities;
#[derive(Debug, Clone, Copy, Default)]
pub struct NotRegistered;
#[derive(Debug)]
pub struct Registered<T>(pub T);
pub struct ServerBuilder<H, Tools, Resources, Prompts, Tasks> {
handler: H,
tools: Tools,
resources: Resources,
prompts: Prompts,
tasks: Tasks,
capabilities: ServerCapabilities,
}
impl<H: ServerHandler>
ServerBuilder<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>
{
#[must_use]
pub fn new(handler: H) -> Self {
let capabilities = handler.capabilities();
Self {
handler,
tools: NotRegistered,
resources: NotRegistered,
prompts: NotRegistered,
tasks: NotRegistered,
capabilities,
}
}
}
impl<H, T, R, P, K> ServerBuilder<H, T, R, P, K>
where
H: ServerHandler,
{
#[must_use]
pub fn capabilities(mut self, caps: ServerCapabilities) -> Self {
self.capabilities = caps;
self
}
#[must_use]
pub const fn get_capabilities(&self) -> &ServerCapabilities {
&self.capabilities
}
}
impl<H, R, P, K> ServerBuilder<H, NotRegistered, R, P, K>
where
H: ServerHandler,
{
#[must_use]
pub fn with_tools<TH: ToolHandler>(
self,
tools: TH,
) -> ServerBuilder<H, Registered<TH>, R, P, K> {
let mut capabilities = self.capabilities.with_tools();
if capabilities.tasks.is_some() {
capabilities = capabilities.with_task_tools();
}
ServerBuilder {
handler: self.handler,
tools: Registered(tools),
resources: self.resources,
prompts: self.prompts,
tasks: self.tasks,
capabilities,
}
}
}
#[cfg(feature = "schema-validation")]
impl<H, TH, R, P, K> ServerBuilder<H, Registered<TH>, R, P, K>
where
H: ServerHandler,
TH: ToolHandler,
{
#[must_use]
pub fn validate_tool_io(
self,
) -> ServerBuilder<H, Registered<crate::validation::ValidatingToolHandler<TH>>, R, P, K> {
self.wrap_tool_validation(crate::validation::ValidationMode::both())
}
#[must_use]
pub fn validate_tool_inputs(
self,
) -> ServerBuilder<H, Registered<crate::validation::ValidatingToolHandler<TH>>, R, P, K> {
self.wrap_tool_validation(crate::validation::ValidationMode::inputs_only())
}
#[must_use]
pub fn validate_tool_outputs(
self,
) -> ServerBuilder<H, Registered<crate::validation::ValidatingToolHandler<TH>>, R, P, K> {
self.wrap_tool_validation(crate::validation::ValidationMode::outputs_only())
}
fn wrap_tool_validation(
self,
mode: crate::validation::ValidationMode,
) -> ServerBuilder<H, Registered<crate::validation::ValidatingToolHandler<TH>>, R, P, K> {
ServerBuilder {
handler: self.handler,
tools: Registered(crate::validation::ValidatingToolHandler::new(
self.tools.0,
mode,
)),
resources: self.resources,
prompts: self.prompts,
tasks: self.tasks,
capabilities: self.capabilities,
}
}
}
impl<H, T, P, K> ServerBuilder<H, T, NotRegistered, P, K>
where
H: ServerHandler,
{
#[must_use]
pub fn with_resources<RH: ResourceHandler>(
self,
resources: RH,
) -> ServerBuilder<H, T, Registered<RH>, P, K> {
ServerBuilder {
handler: self.handler,
tools: self.tools,
resources: Registered(resources),
prompts: self.prompts,
tasks: self.tasks,
capabilities: self.capabilities.with_resources(),
}
}
}
impl<H, T, R, K> ServerBuilder<H, T, R, NotRegistered, K>
where
H: ServerHandler,
{
#[must_use]
pub fn with_prompts<PH: PromptHandler>(
self,
prompts: PH,
) -> ServerBuilder<H, T, R, Registered<PH>, K> {
ServerBuilder {
handler: self.handler,
tools: self.tools,
resources: self.resources,
prompts: Registered(prompts),
tasks: self.tasks,
capabilities: self.capabilities.with_prompts(),
}
}
}
impl<H, T, R, P> ServerBuilder<H, T, R, P, NotRegistered>
where
H: ServerHandler,
{
#[must_use]
pub fn with_tasks<KH: TaskHandler>(
self,
tasks: KH,
) -> ServerBuilder<H, T, R, P, Registered<KH>> {
let mut capabilities = self.capabilities.with_tasks();
if capabilities.tools.is_some() {
capabilities = capabilities.with_task_tools();
}
ServerBuilder {
handler: self.handler,
tools: self.tools,
resources: self.resources,
prompts: self.prompts,
tasks: Registered(tasks),
capabilities,
}
}
}
impl<H, T, R, P, K> ServerBuilder<H, T, R, P, K>
where
H: ServerHandler + Send + Sync + 'static,
T: Send + Sync + 'static,
R: Send + Sync + 'static,
P: Send + Sync + 'static,
K: Send + Sync + 'static,
{
#[must_use]
pub fn build(self) -> Server<H, T, R, P, K> {
Server {
handler: self.handler,
tools: self.tools,
resources: self.resources,
prompts: self.prompts,
tasks: self.tasks,
capabilities: self.capabilities,
list_page_size: None,
completion: None,
}
}
}
pub struct Server<H, T, R, P, K> {
handler: H,
pub(crate) tools: T,
pub(crate) resources: R,
pub(crate) prompts: P,
pub(crate) tasks: K,
capabilities: ServerCapabilities,
pub(crate) list_page_size: Option<usize>,
pub(crate) completion: Option<std::sync::Arc<dyn crate::dispatch::DynCompletionHandler>>,
}
impl<H, T, R, P, K> Server<H, T, R, P, K>
where
H: ServerHandler,
{
#[must_use]
pub const fn capabilities(&self) -> &ServerCapabilities {
&self.capabilities
}
#[must_use]
pub const fn list_page_size(mut self, page_size: usize) -> Self {
self.list_page_size = Some(page_size);
self
}
#[must_use]
pub fn with_completion<C: crate::handler::CompletionHandler + 'static>(
mut self,
completion: C,
) -> Self {
self.completion = Some(std::sync::Arc::new(completion));
self.capabilities = self.capabilities.with_completions();
self
}
#[must_use]
pub const fn handler(&self) -> &H {
&self.handler
}
#[must_use]
pub fn server_info(&self) -> mcpkit_core::capability::ServerInfo {
self.handler.server_info()
}
}
impl<H, TH, R, P, K> Server<H, Registered<TH>, R, P, K>
where
H: ServerHandler,
TH: ToolHandler,
{
#[must_use]
pub const fn tool_handler(&self) -> &TH {
&self.tools.0
}
}
impl<H, T, RH, P, K> Server<H, T, Registered<RH>, P, K>
where
H: ServerHandler,
RH: ResourceHandler,
{
#[must_use]
pub const fn resource_handler(&self) -> &RH {
&self.resources.0
}
}
impl<H, T, R, PH, K> Server<H, T, R, Registered<PH>, K>
where
H: ServerHandler,
PH: PromptHandler,
{
#[must_use]
pub const fn prompt_handler(&self) -> &PH {
&self.prompts.0
}
}
impl<H, T, R, P, KH> Server<H, T, R, P, Registered<KH>>
where
H: ServerHandler,
KH: TaskHandler,
{
#[must_use]
pub const fn task_handler(&self) -> &KH {
&self.tasks.0
}
}
pub type FullServer<H, TH, RH, PH, KH> =
Server<H, Registered<TH>, Registered<RH>, Registered<PH>, Registered<KH>>;
pub type MinimalServer<H> = Server<H, NotRegistered, NotRegistered, NotRegistered, NotRegistered>;
#[cfg(test)]
mod tests {
use super::*;
use crate::context::Context;
use crate::handler::ToolHandler;
use mcpkit_core::capability::ServerInfo;
use mcpkit_core::error::McpError;
use mcpkit_core::types::{Tool, ToolOutput};
use serde_json::Value;
struct TestHandler;
impl ServerHandler for TestHandler {
fn server_info(&self) -> ServerInfo {
ServerInfo::new("test", "1.0.0")
}
fn capabilities(&self) -> ServerCapabilities {
ServerCapabilities::default()
}
}
struct TestToolHandler;
impl ToolHandler for TestToolHandler {
async fn list_tools(&self, _ctx: &Context<'_>) -> Result<Vec<Tool>, McpError> {
Ok(vec![])
}
async fn call_tool(
&self,
_name: &str,
_args: serde_json::Map<String, Value>,
_ctx: &Context<'_>,
) -> Result<ToolOutput, McpError> {
Ok(ToolOutput::text("test"))
}
}
#[test]
fn test_server_builder_minimal() {
let server = ServerBuilder::new(TestHandler).build();
assert_eq!(server.server_info().name, "test");
assert_eq!(server.server_info().version, "1.0.0");
}
#[test]
fn test_server_builder_with_tools() {
let server = ServerBuilder::new(TestHandler)
.with_tools(TestToolHandler)
.build();
assert!(server.capabilities().has_tools());
let _tool_handler: &TestToolHandler = server.tool_handler();
}
struct TestTaskHandler;
impl crate::handler::TaskHandler for TestTaskHandler {
async fn list_tasks(
&self,
_ctx: &Context<'_>,
) -> Result<mcpkit_core::types::ListTasksResult, McpError> {
Ok(vec![].into())
}
async fn get_task(
&self,
_id: &mcpkit_core::types::TaskId,
_ctx: &Context<'_>,
) -> Result<Option<mcpkit_core::types::GetTaskResult>, McpError> {
Ok(None)
}
async fn cancel_task(
&self,
_id: &mcpkit_core::types::TaskId,
_ctx: &Context<'_>,
) -> Result<Option<mcpkit_core::types::CancelTaskResult>, McpError> {
Ok(None)
}
}
#[test]
fn test_server_builder_with_tasks_advertises_capability() {
let server = ServerBuilder::new(TestHandler)
.with_tasks(TestTaskHandler)
.build();
assert!(server.capabilities().has_tasks());
}
#[test]
fn tasks_capability_shape_is_registration_order_independent() {
fn tools_call(caps: &mcpkit_core::capability::ServerCapabilities) -> serde_json::Value {
serde_json::to_value(caps).unwrap()["tasks"].clone()
}
let tasks_first = ServerBuilder::new(TestHandler)
.with_tasks(TestTaskHandler)
.with_tools(TestToolHandler)
.build();
let tools_first = ServerBuilder::new(TestHandler)
.with_tools(TestToolHandler)
.with_tasks(TestTaskHandler)
.build();
let expected = serde_json::json!({
"list": {},
"cancel": {},
"requests": { "tools": { "call": {} } }
});
assert_eq!(tools_call(tasks_first.capabilities()), expected);
assert_eq!(tools_call(tools_first.capabilities()), expected);
let tasks_only = ServerBuilder::new(TestHandler)
.with_tasks(TestTaskHandler)
.build();
assert_eq!(
tools_call(tasks_only.capabilities()),
serde_json::json!({ "list": {}, "cancel": {} })
);
}
#[test]
fn test_typestate_prevents_double_registration() {
let _server = ServerBuilder::new(TestHandler)
.with_tools(TestToolHandler)
.build();
}
#[test]
fn test_builder_order_independence() {
let server1 = ServerBuilder::new(TestHandler)
.with_tools(TestToolHandler)
.build();
let _server2: Server<
TestHandler,
Registered<TestToolHandler>,
NotRegistered,
NotRegistered,
NotRegistered,
> = ServerBuilder::new(TestHandler)
.with_tools(TestToolHandler)
.build();
assert!(server1.capabilities().has_tools());
}
}