diff --git a/Cargo.lock b/Cargo.lock index 0e6c66c81644..6c4a3b643fae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5425,22 +5425,6 @@ dependencies = [ "utoipa", ] -[[package]] -name = "mcp-macros" -version = "0.1.0" -dependencies = [ - "async-trait", - "convert_case", - "mcp-core", - "proc-macro2", - "quote", - "schemars", - "serde", - "serde_json", - "syn 2.0.99", - "tokio", -] - [[package]] name = "mcp-server" version = "0.1.0" @@ -5449,7 +5433,6 @@ dependencies = [ "async-trait", "futures", "mcp-core", - "mcp-macros", "pin-project", "rmcp", "schemars", diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index d92dfa2b5700..befc27f0d920 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -11,7 +11,6 @@ use goose::permission::permission_confirmation::PrincipalType; use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata}; use goose::session::info::SessionInfo; use goose::session::SessionMetadata; -use mcp_core::handler::ToolResultSchema; use mcp_core::tool::{Tool, ToolAnnotations}; use rmcp::model::ResourceContents; use rmcp::model::{Annotations, Content, EmbeddedResource, ImageContent, Role, TextContent}; @@ -348,7 +347,6 @@ derive_utoipa!(ResourceContents as ResourceContentsSchema); TextContentSchema, ToolResponse, ToolRequest, - ToolResultSchema, ToolConfirmationRequest, ThinkingContent, RedactedThinkingContent, diff --git a/crates/mcp-core/src/handler.rs b/crates/mcp-core/src/handler.rs index 2a4c1a77ee2f..4724cb97edfd 100644 --- a/crates/mcp-core/src/handler.rs +++ b/crates/mcp-core/src/handler.rs @@ -1,11 +1,7 @@ -use async_trait::async_trait; -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; #[allow(unused_imports)] // this is used in schema below use serde_json::json; -use serde_json::Value; use thiserror::Error; -use utoipa::ToSchema; #[non_exhaustive] #[derive(Error, Debug, Clone, Deserialize, Serialize, PartialEq)] @@ -22,18 +18,6 @@ pub enum ToolError { pub type ToolResult = std::result::Result; -// Define schema manually without generics issues -#[derive(ToSchema)] -#[schema(example = json!({"success": true, "data": {}}))] -pub struct ToolResultSchema { - #[schema(example = "Operation completed successfully")] - pub message: Option, - #[schema(example = true)] - pub success: bool, - #[schema(value_type = Object)] - pub data: Option, -} - #[derive(Error, Debug)] pub enum ResourceError { #[error("Execution failed: {0}")] @@ -51,38 +35,3 @@ pub enum PromptError { #[error("Prompt not found: {0}")] NotFound(String), } - -/// Trait for implementing MCP tools -#[async_trait] -pub trait ToolHandler: Send + Sync + 'static { - /// The name of the tool - fn name(&self) -> &'static str; - - /// A description of what the tool does - fn description(&self) -> &'static str; - - /// JSON schema describing the tool's parameters - fn schema(&self) -> Value; - - /// Execute the tool with the given parameters - async fn call(&self, params: Value) -> ToolResult; -} - -/// Trait for implementing MCP resources -#[async_trait] -pub trait ResourceTemplateHandler: Send + Sync + 'static { - /// The URL template for this resource - fn template() -> &'static str; - - /// JSON schema describing the resource parameters - fn schema() -> Value; - - /// Get the resource value - async fn get(&self, params: Value) -> ToolResult; -} - -/// Helper function to generate JSON schema for a type -pub fn generate_schema() -> ToolResult { - let schema = schemars::schema_for!(T); - serde_json::to_value(schema).map_err(|e| ToolError::SchemaError(e.to_string())) -} diff --git a/crates/mcp-macros/Cargo.toml b/crates/mcp-macros/Cargo.toml deleted file mode 100644 index 0b5903a6f2eb..000000000000 --- a/crates/mcp-macros/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "mcp-macros" -version = "0.1.0" -edition = "2021" - -[lints] -workspace = true - -[lib] -proc-macro = true - -[dependencies] -syn = { version = "2.0", features = ["full", "extra-traits"] } -quote = "1.0" -proc-macro2 = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -mcp-core = { path = "../mcp-core" } -async-trait = "0.1" -schemars = "0.8" -convert_case = "0.6.0" - -[dev-dependencies] -tokio = { version = "1.43", features = ["full"] } -async-trait = "0.1" -serde_json = "1.0" -schemars = "0.8" diff --git a/crates/mcp-macros/examples/calculator.rs b/crates/mcp-macros/examples/calculator.rs deleted file mode 100644 index 64533464a589..000000000000 --- a/crates/mcp-macros/examples/calculator.rs +++ /dev/null @@ -1,53 +0,0 @@ -use mcp_core::handler::{ToolError, ToolHandler}; -use mcp_macros::tool; - -#[tokio::main] -async fn main() -> std::result::Result<(), Box> { - // Create an instance of our tool - let calculator = Calculator; - - // Print tool information - println!("Tool name: {}", calculator.name()); - println!("Tool description: {}", calculator.description()); - println!("Tool schema: {}", calculator.schema()); - - // Test the tool with some sample input - let input = serde_json::json!({ - "x": 5, - "y": 3, - "operation": "multiply" - }); - - let result = calculator.call(input).await?; - println!("Result: {}", result); - - Ok(()) -} - -#[tool( - name = "calculator", - description = "Perform basic arithmetic operations", - params( - x = "First number in the calculation", - y = "Second number in the calculation", - operation = "The operation to perform (add, subtract, multiply, divide)" - ) -)] -async fn calculator(x: i32, y: i32, operation: String) -> Result { - match operation.as_str() { - "add" => Ok(x + y), - "subtract" => Ok(x - y), - "multiply" => Ok(x * y), - "divide" => { - if y == 0 { - Err(ToolError::ExecutionError("Division by zero".into())) - } else { - Ok(x / y) - } - } - _ => Err(ToolError::InvalidParameters(format!( - "Unknown operation: {}", - operation - ))), - } -} diff --git a/crates/mcp-macros/src/lib.rs b/crates/mcp-macros/src/lib.rs deleted file mode 100644 index d918d07cb1d7..000000000000 --- a/crates/mcp-macros/src/lib.rs +++ /dev/null @@ -1,152 +0,0 @@ -use convert_case::{Case, Casing}; -use proc_macro::TokenStream; -use quote::{format_ident, quote}; -use std::collections::HashMap; -use syn::{ - parse::Parse, parse::ParseStream, parse_macro_input, punctuated::Punctuated, Expr, ExprLit, - FnArg, ItemFn, Lit, Meta, Pat, PatType, Token, -}; - -struct MacroArgs { - name: Option, - description: Option, - param_descriptions: HashMap, -} - -impl Parse for MacroArgs { - fn parse(input: ParseStream) -> syn::Result { - let mut name = None; - let mut description = None; - let mut param_descriptions = HashMap::new(); - - let meta_list: Punctuated = Punctuated::parse_terminated(input)?; - - for meta in meta_list { - match meta { - Meta::NameValue(nv) => { - let ident = nv.path.get_ident().unwrap().to_string(); - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = nv.value - { - match ident.as_str() { - "name" => name = Some(lit_str.value()), - "description" => description = Some(lit_str.value()), - _ => {} - } - } - } - Meta::List(list) if list.path.is_ident("params") => { - let nested: Punctuated = - list.parse_args_with(Punctuated::parse_terminated)?; - - for meta in nested { - if let Meta::NameValue(nv) = meta { - if let Expr::Lit(ExprLit { - lit: Lit::Str(lit_str), - .. - }) = nv.value - { - let param_name = nv.path.get_ident().unwrap().to_string(); - param_descriptions.insert(param_name, lit_str.value()); - } - } - } - } - _ => {} - } - } - - Ok(MacroArgs { - name, - description, - param_descriptions, - }) - } -} - -#[proc_macro_attribute] -pub fn tool(args: TokenStream, input: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as MacroArgs); - let input_fn = parse_macro_input!(input as ItemFn); - - // Extract function details - let fn_name = &input_fn.sig.ident; - let fn_name_str = fn_name.to_string(); - - // Generate PascalCase struct name from the function name - let struct_name = format_ident!("{}", { fn_name_str.to_case(Case::Pascal) }); - - // Use provided name or function name as default - let tool_name = args.name.unwrap_or(fn_name_str); - let tool_description = args.description.unwrap_or_default(); - - // Extract parameter names, types, and descriptions - let mut param_defs = Vec::new(); - let mut param_names = Vec::new(); - - for arg in input_fn.sig.inputs.iter() { - if let FnArg::Typed(PatType { pat, ty, .. }) = arg { - if let Pat::Ident(param_ident) = &**pat { - let param_name = ¶m_ident.ident; - let param_name_str = param_name.to_string(); - let description = args - .param_descriptions - .get(¶m_name_str) - .map(|s| s.as_str()) - .unwrap_or(""); - - param_names.push(param_name); - param_defs.push(quote! { - #[schemars(description = #description)] - #param_name: #ty - }); - } - } - } - - // Generate the implementation - let params_struct_name = format_ident!("{}Parameters", struct_name); - let expanded = quote! { - #[derive(serde::Deserialize, schemars::JsonSchema)] - struct #params_struct_name { - #(#param_defs,)* - } - - #input_fn - - #[derive(Default)] - struct #struct_name; - - #[async_trait::async_trait] - impl mcp_core::handler::ToolHandler for #struct_name { - fn name(&self) -> &'static str { - #tool_name - } - - fn description(&self) -> &'static str { - #tool_description - } - - fn schema(&self) -> serde_json::Value { - mcp_core::handler::generate_schema::<#params_struct_name>() - .expect("Failed to generate schema") - } - - async fn call(&self, params: serde_json::Value) -> Result { - let params: #params_struct_name = serde_json::from_value(params) - .map_err(|e| mcp_core::handler::ToolError::InvalidParameters(e.to_string()))?; - - // Extract parameters and call the function - let result = #fn_name(#(params.#param_names,)*).await - .map_err(|e| mcp_core::handler::ToolError::ExecutionError(e.to_string()))?; - - Ok(serde_json::to_value(result).expect("should serialize")) - - } - } - }; - - TokenStream::from(expanded) -} diff --git a/crates/mcp-server/Cargo.toml b/crates/mcp-server/Cargo.toml index c6392b757f77..7058b5377fd5 100644 --- a/crates/mcp-server/Cargo.toml +++ b/crates/mcp-server/Cargo.toml @@ -10,7 +10,6 @@ workspace = true anyhow = "1.0.94" thiserror = "1.0" mcp-core = { path = "../mcp-core" } -mcp-macros = { path = "../mcp-macros" } rmcp = { workspace = true } serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.133" diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 971f05dbb766..60aa50422810 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -2332,17 +2332,16 @@ } }, "ResourceContents": { - "oneOf": [ + "anyOf": [ { "type": "object", "required": [ - "uri", - "text" + "text", + "uri" ], "properties": { "mime_type": { - "type": "string", - "nullable": true + "type": "string" }, "text": { "type": "string" @@ -2355,16 +2354,15 @@ { "type": "object", "required": [ - "uri", - "blob" + "blob", + "uri" ], "properties": { "blob": { "type": "string" }, "mime_type": { - "type": "string", - "nullable": true + "type": "string" }, "uri": { "type": "string" @@ -2961,31 +2959,6 @@ } } }, - "ToolResultSchema": { - "type": "object", - "required": [ - "success", - "data" - ], - "properties": { - "data": { - "type": "object" - }, - "message": { - "type": "string", - "example": "Operation completed successfully", - "nullable": true - }, - "success": { - "type": "boolean", - "example": true - } - }, - "example": { - "data": {}, - "success": true - } - }, "UpdateScheduleRequest": { "type": "object", "required": [ diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index 53230d066b87..464618a57919 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -463,12 +463,12 @@ export type RedactedThinkingContent = { }; export type ResourceContents = { - mime_type?: string | null; + mime_type?: string; text: string; uri: string; } | { blob: string; - mime_type?: string | null; + mime_type?: string; uri: string; }; @@ -760,14 +760,6 @@ export type ToolResponse = { }; }; -export type ToolResultSchema = { - data: { - [key: string]: unknown; - }; - message?: string | null; - success: boolean; -}; - export type UpdateScheduleRequest = { cron: string; };