use std::marker::PhantomData;
use rust_mcp_schema::{Implementation, ServerCapabilities, ServerCapabilitiesTools};
use crate::{McpServer, Phase, ToolRegistry};
pub(crate) struct ServerConfig {
pub info: Implementation,
pub capabilities: ServerCapabilities,
pub instructions: Option<String>,
}
pub struct McpServerBuilder<R: ToolRegistry> {
name: String,
version: String,
title: Option<String>,
instructions: Option<String>,
_marker: PhantomData<R>,
}
impl<R: ToolRegistry> McpServerBuilder<R> {
pub(crate) fn new() -> Self {
Self {
name: "unnamed-mcp-server".into(),
version: "0.0.0".into(),
title: None,
instructions: None,
_marker: PhantomData,
}
}
pub fn name<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.name = name.into();
self
}
pub fn version<S: Into<String>>(&mut self, version: S) -> &mut Self {
self.version = version.into();
self
}
pub fn title<S: Into<String>>(&mut self, title: S) -> &mut Self {
self.title = Some(title.into());
self
}
pub fn instructions<S: Into<String>>(&mut self, instructions: S) -> &mut Self {
self.instructions = Some(instructions.into());
self
}
pub fn build(&self) -> McpServer<R> {
let capabilities = ServerCapabilities {
tools: if R::ENABLED {
Some(ServerCapabilitiesTools {
list_changed: Some(false),
})
} else {
None
},
completions: None,
experimental: None,
logging: None,
prompts: None,
resources: None,
};
McpServer {
config: ServerConfig {
info: Implementation {
name: self.name.clone(),
version: self.version.clone(),
title: self.title.clone(),
},
capabilities,
instructions: self.instructions.clone(),
},
phase: Phase::WaitingForInitialize,
_marker: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use crate::{JsonRpcError, McpServer, ToolDefinitions, ToolRegistry};
enum TestTools {}
impl ToolRegistry for TestTools {
fn parse(name: &str, _arguments: serde_json::Value) -> Result<Self, JsonRpcError> {
Err(JsonRpcError::MethodNotFound {
msg: format!("unknown tool: {name}"),
})
}
fn definitions() -> ToolDefinitions {
ToolDefinitions::new(vec![])
}
}
#[test]
fn default_values() {
let server = McpServer::<TestTools>::builder().build();
assert!(!server.is_ready());
}
#[test]
fn tools_disabled_for_no_tools() {
use crate::NoTools;
let server = McpServer::<NoTools>::builder().build();
assert!(server.config.capabilities.tools.is_none());
}
#[test]
fn tools_enabled_for_registry_with_tools() {
let server = McpServer::<TestTools>::builder().build();
assert!(server.config.capabilities.tools.is_some());
let tools = server
.config
.capabilities
.tools
.as_ref()
.expect("tools capability");
assert_eq!(tools.list_changed, Some(false));
}
#[test]
fn builder_sets_fields() {
let server = McpServer::<TestTools>::builder()
.name("test-server")
.version("1.2.3")
.title("Test Server")
.instructions("Use this server for testing.")
.build();
assert_eq!(server.config.info.name, "test-server");
assert_eq!(server.config.info.version, "1.2.3");
assert_eq!(server.config.info.title.as_deref(), Some("Test Server"));
assert_eq!(
server.config.instructions.as_deref(),
Some("Use this server for testing.")
);
}
}