use std::sync::Arc;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
use crate::error::Result;
use crate::prompt::{PromptDefinitionMetadata, PromptManager, PromptTemplate};
use crate::resource::{
ResourceContent, ResourceDefinition, ResourceDefinitionMetadata, ResourceManager,
};
use crate::tool::{
DuplicateBehavior, ToolDefinition, ToolDefinitionMetadata, ToolManager, ToolResponse,
};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerMetadata {
pub id: Uuid,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
pub version: String,
pub created_at: DateTime<Utc>,
}
impl ServerMetadata {
pub fn new(
name: impl Into<String>,
instructions: Option<String>,
version: impl Into<String>,
) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
instructions,
version: version.into(),
created_at: Utc::now(),
}
}
}
pub struct FastMcpServer {
metadata: ServerMetadata,
tool_manager: ToolManager,
resource_manager: ResourceManager,
prompt_manager: PromptManager,
}
impl FastMcpServer {
pub fn builder() -> FastMcpServerBuilder {
FastMcpServerBuilder::default()
}
pub fn metadata(&self) -> ServerMetadata {
self.metadata.clone()
}
pub fn register_tool(&self, tool: ToolDefinition) -> Result<()> {
self.tool_manager.register(tool)
}
pub fn list_tools(&self) -> Vec<ToolDefinitionMetadata> {
self.tool_manager.list()
}
pub async fn call_tool(&self, name: &str, arguments: Value) -> Result<ToolResponse> {
self.tool_manager.call(name, arguments).await
}
pub fn register_resource(&self, resource: ResourceDefinition) -> Result<()> {
self.resource_manager.register(resource)
}
pub fn list_resources(&self) -> Vec<ResourceDefinitionMetadata> {
self.resource_manager.list()
}
pub async fn read_resource(&self, uri: &str) -> Result<ResourceContent> {
self.resource_manager.read(uri).await
}
pub fn register_prompt(&self, prompt: PromptTemplate) -> Result<()> {
self.prompt_manager.register(prompt)
}
pub fn list_prompts(&self) -> Vec<PromptDefinitionMetadata> {
self.prompt_manager.list()
}
pub fn instantiate_prompt(
&self,
name: &str,
arguments: Option<&Value>,
) -> Result<Vec<crate::prompt::PromptMessage>> {
let prompt = self.prompt_manager.get(name)?;
prompt.instantiate(arguments)
}
pub fn into_shared(self) -> Arc<Self> {
Arc::new(self)
}
}
#[derive(Default)]
pub struct FastMcpServerBuilder {
name: Option<String>,
instructions: Option<String>,
version: Option<String>,
tool_duplicates: DuplicateBehavior,
resource_duplicates: DuplicateBehavior,
prompt_duplicates: DuplicateBehavior,
}
impl FastMcpServerBuilder {
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
self.instructions = Some(instructions.into());
self
}
pub fn version(mut self, version: impl Into<String>) -> Self {
self.version = Some(version.into());
self
}
pub fn tool_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
self.tool_duplicates = behavior;
self
}
pub fn resource_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
self.resource_duplicates = behavior;
self
}
pub fn prompt_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
self.prompt_duplicates = behavior;
self
}
pub fn build(self) -> FastMcpServer {
let name = self
.name
.unwrap_or_else(|| format!("fastmcp-{}", Uuid::new_v4()));
let version = self.version.unwrap_or_else(|| "0.1.0-preview".into());
let server = FastMcpServer {
metadata: ServerMetadata::new(name, self.instructions, version),
tool_manager: ToolManager::new(self.tool_duplicates),
resource_manager: ResourceManager::new(self.resource_duplicates),
prompt_manager: PromptManager::new(self.prompt_duplicates),
};
#[cfg(feature = "auto-register")]
{
crate::tool::register_discovered_tools(&server);
}
server
}
}