use {
crate::{
core::McpServer,
handler::{
McpContext, McpHandler, McpNotification, PromptContent, PromptInfo, ResourceContent,
ResourceInfo, ToolDefinition,
},
},
anyhow::Result,
async_trait::async_trait,
serde_json::{json, Value},
std::{collections::HashMap, sync::Arc},
tokio::sync::{mpsc, RwLock},
tracing::info,
};
pub struct ToolContext {
pub notification_sender: Option<mpsc::UnboundedSender<McpNotification>>,
}
#[async_trait]
pub trait McpTool: Send + Sync {
fn definition(&self) -> ExtendedToolDefinition;
async fn execute(&self, arguments: Value, context: &ToolContext) -> Result<Value>;
}
#[derive(Debug, Clone)]
pub struct ExtendedToolDefinition {
pub name: String,
pub description: String,
pub input_schema: Value,
pub output_schema: Value,
}
#[async_trait]
pub trait McpResourceProvider: Send + Sync {
async fn list_resources(&self) -> Result<Vec<ResourceInfo>>;
async fn read_resource(&self, uri: &str) -> Result<ResourceContent>;
}
#[async_trait]
pub trait McpPromptProvider: Send + Sync {
async fn list_prompts(&self) -> Result<Vec<PromptInfo>>;
async fn get_prompt(&self, name: &str, arguments: Option<Value>) -> Result<PromptContent>;
}
pub struct McpServerBuilder {
tools: HashMap<String, Arc<dyn McpTool>>,
resource_provider: Option<Arc<dyn McpResourceProvider>>,
prompt_provider: Option<Arc<dyn McpPromptProvider>>,
capabilities: ServerCapabilities,
}
#[derive(Debug, Clone, Default)]
pub struct ServerCapabilities {
pub experimental: HashMap<String, Value>,
}
impl Default for McpServerBuilder {
fn default() -> Self {
Self::new()
}
}
impl McpServerBuilder {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
resource_provider: None,
prompt_provider: None,
capabilities: ServerCapabilities::default(),
}
}
pub fn add_tool(mut self, tool: impl McpTool + 'static) -> Self {
let definition = tool.definition();
self.tools.insert(definition.name.clone(), Arc::new(tool));
self
}
pub fn with_resources(mut self, provider: impl McpResourceProvider + 'static) -> Self {
self.resource_provider = Some(Arc::new(provider));
self
}
pub fn with_prompts(mut self, provider: impl McpPromptProvider + 'static) -> Self {
self.prompt_provider = Some(Arc::new(provider));
self
}
pub fn with_experimental_capability(mut self, name: String, value: Value) -> Self {
self.capabilities.experimental.insert(name, value);
self
}
pub async fn build(self) -> Result<HighLevelMcpServer> {
let (notification_tx, notification_rx) = mpsc::unbounded_channel();
let custom_handler = Arc::new(CustomMcpHandler {
tools: Arc::new(RwLock::new(self.tools)),
resource_provider: self.resource_provider,
prompt_provider: self.prompt_provider,
capabilities: self.capabilities,
notification_sender: Some(notification_tx),
});
Ok(HighLevelMcpServer {
custom_handler,
notification_receiver: notification_rx,
})
}
}
pub struct HighLevelMcpServer {
custom_handler: Arc<CustomMcpHandler>,
notification_receiver: mpsc::UnboundedReceiver<McpNotification>,
}
impl HighLevelMcpServer {
pub async fn add_tool(&self, tool: impl McpTool + 'static) -> Result<()> {
let definition = tool.definition();
self.custom_handler
.tools
.write()
.await
.insert(definition.name.clone(), Arc::new(tool));
Ok(())
}
pub fn notification_sender(&self) -> Option<mpsc::UnboundedSender<McpNotification>> {
self.custom_handler.notification_sender.clone()
}
pub async fn start(self, port: u16) -> Result<()> {
let mut core_server = McpServer::with_handler(self.custom_handler).await?;
info!("🚀 Starting MCP server with custom tools on port {}", port);
core_server.start(port).await
}
}
struct CustomMcpHandler {
tools: Arc<RwLock<HashMap<String, Arc<dyn McpTool>>>>,
resource_provider: Option<Arc<dyn McpResourceProvider>>,
prompt_provider: Option<Arc<dyn McpPromptProvider>>,
capabilities: ServerCapabilities,
notification_sender: Option<mpsc::UnboundedSender<McpNotification>>,
}
impl CustomMcpHandler {
pub fn send_notification(&self, notification: McpNotification) -> Result<()> {
if let Some(sender) = &self.notification_sender {
sender.send(notification)?;
}
Ok(())
}
fn get_capabilities(&self) -> Value {
let mut capabilities = json!({});
capabilities["tools"] = json!({
"listChanged": false
});
if self.resource_provider.is_some() {
capabilities["resources"] = json!({
"listChanged": false
});
}
if self.prompt_provider.is_some() {
capabilities["prompts"] = json!({
"listChanged": false
});
}
if !self.capabilities.experimental.is_empty() {
capabilities["experimental"] = json!(self.capabilities.experimental);
}
capabilities
}
async fn handle_tools_list(&self) -> Result<Value> {
let tools = self.tools.read().await;
let tool_definitions: Vec<Value> = tools
.values()
.map(|tool| {
let def = tool.definition();
json!({
"name": def.name,
"description": def.description,
"inputSchema": def.input_schema,
})
})
.collect();
Ok(json!({
"tools": tool_definitions
}))
}
async fn handle_tool_call(&self, name: &str, arguments: Value) -> Result<Value> {
let tools = self.tools.read().await;
if let Some(tool) = tools.get(name) {
let context = ToolContext {
notification_sender: self.notification_sender.clone(),
};
let result = tool.execute(arguments, &context).await?;
Ok(json!({
"content": [
{
"type": "text",
"text": serde_json::to_string(&result)?
}
]
}))
} else {
Err(anyhow::anyhow!("Unknown tool: {}", name))
}
}
async fn handle_resources_list(&self) -> Result<Value> {
if let Some(provider) = &self.resource_provider {
let resources = provider.list_resources().await?;
let resource_list: Vec<Value> = resources
.into_iter()
.map(|r| {
let mut resource = json!({
"uri": r.uri,
"name": r.name,
});
if let Some(desc) = r.description {
resource["description"] = json!(desc);
}
if let Some(mime) = r.mime_type {
resource["mimeType"] = json!(mime);
}
resource
})
.collect();
Ok(json!({
"resources": resource_list
}))
} else {
Err(anyhow::anyhow!("Resources not supported"))
}
}
async fn handle_resource_read(&self, uri: &str) -> Result<Value> {
if let Some(provider) = &self.resource_provider {
let content = provider.read_resource(uri).await?;
Ok(json!({
"contents": [
{
"uri": content.uri,
"mimeType": content.mime_type,
"text": content.content,
}
]
}))
} else {
Err(anyhow::anyhow!("Resources not supported"))
}
}
async fn handle_prompts_list(&self) -> Result<Value> {
if let Some(provider) = &self.prompt_provider {
let prompts = provider.list_prompts().await?;
let prompt_list: Vec<Value> = prompts
.into_iter()
.map(|p| {
let mut prompt = json!({
"name": p.name,
});
if let Some(desc) = p.description {
prompt["description"] = json!(desc);
}
if !p.arguments.is_empty() {
let args: Vec<Value> = p
.arguments
.into_iter()
.map(|a| {
let mut arg = json!({
"name": a.name,
"required": a.required,
});
if let Some(desc) = a.description {
arg["description"] = json!(desc);
}
arg
})
.collect();
prompt["arguments"] = json!(args);
}
prompt
})
.collect();
Ok(json!({
"prompts": prompt_list
}))
} else {
Err(anyhow::anyhow!("Prompts not supported"))
}
}
async fn handle_prompt_get(&self, name: &str, arguments: Option<Value>) -> Result<Value> {
if let Some(provider) = &self.prompt_provider {
let content = provider.get_prompt(name, arguments).await?;
let messages: Vec<Value> = content
.messages
.into_iter()
.map(|m| {
json!({
"role": m.role,
"content": {
"type": "text",
"text": m.content,
}
})
})
.collect();
Ok(json!({
"messages": messages
}))
} else {
Err(anyhow::anyhow!("Prompts not supported"))
}
}
}
#[async_trait]
impl McpHandler for CustomMcpHandler {
async fn initialize(&self, _params: Value, _context: &McpContext) -> Result<Value> {
Ok(json!({
"protocolVersion": "2025-06-18",
"capabilities": self.get_capabilities(),
"serverInfo": {
"name": "toy-notes-server",
"version": "0.1.0"
}
}))
}
async fn list_tools(&self, _context: &McpContext) -> Result<Vec<ToolDefinition>> {
let tools = self.tools.read().await;
let tool_definitions: Vec<ToolDefinition> = tools
.values()
.map(|tool| {
let def = tool.definition();
ToolDefinition {
name: def.name,
description: def.description,
input_schema: def.input_schema,
}
})
.collect();
Ok(tool_definitions)
}
async fn call_tool(
&self,
name: &str,
arguments: Value,
_context: &McpContext,
) -> Result<Value> {
let tools = self.tools.read().await;
if let Some(tool) = tools.get(name) {
let context = ToolContext {
notification_sender: self.notification_sender.clone(),
};
let result = tool.execute(arguments, &context).await?;
Ok(json!({
"content": [
{
"type": "text",
"text": serde_json::to_string(&result)?
}
]
}))
} else {
Err(anyhow::anyhow!("Unknown tool: {}", name))
}
}
async fn list_resources(&self, _context: &McpContext) -> Result<Vec<ResourceInfo>> {
if let Some(provider) = &self.resource_provider {
provider.list_resources().await
} else {
Ok(vec![])
}
}
async fn read_resource(&self, uri: &str, _context: &McpContext) -> Result<ResourceContent> {
if let Some(provider) = &self.resource_provider {
provider.read_resource(uri).await
} else {
Err(anyhow::anyhow!("Resource not found: {}", uri))
}
}
async fn list_prompts(&self, _context: &McpContext) -> Result<Vec<PromptInfo>> {
if let Some(provider) = &self.prompt_provider {
provider.list_prompts().await
} else {
Ok(vec![])
}
}
async fn get_prompt(
&self,
name: &str,
arguments: Option<Value>,
_context: &McpContext,
) -> Result<PromptContent> {
if let Some(provider) = &self.prompt_provider {
provider.get_prompt(name, arguments).await
} else {
Err(anyhow::anyhow!("Prompt not found: {}", name))
}
}
}