mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-18 14:44:21 +01:00
feat: add tool annotations for build-in tools (#1939)
This commit is contained in:
@@ -5,6 +5,7 @@ use goose::config::extensions::name_to_key;
|
||||
use goose::config::{Config, ConfigError, ExperimentManager, ExtensionEntry, ExtensionManager};
|
||||
use goose::message::Message;
|
||||
use goose::providers::{create, providers};
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::Tool;
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashMap;
|
||||
@@ -349,6 +350,13 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
|
||||
"location": {"type": "string"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Get weather".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
vec![sample_tool]
|
||||
} else {
|
||||
|
||||
@@ -13,7 +13,7 @@ use mcp_core::{
|
||||
prompt::Prompt,
|
||||
protocol::ServerCapabilities,
|
||||
resource::Resource,
|
||||
tool::Tool,
|
||||
tool::{Tool, ToolAnnotations},
|
||||
Content,
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
@@ -74,6 +74,13 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Web Scrape".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}),
|
||||
);
|
||||
|
||||
let computer_control_desc = match std::env::consts::OS {
|
||||
@@ -139,6 +146,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let quick_script_desc = match std::env::consts::OS {
|
||||
@@ -189,6 +197,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let cache_tool = Tool::new(
|
||||
@@ -215,6 +224,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let pdf_tool = Tool::new(
|
||||
@@ -242,6 +252,13 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("PDF process".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let docx_tool = Tool::new(
|
||||
@@ -340,6 +357,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let make_presentation_tool = Tool::new(
|
||||
@@ -380,6 +398,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let xlsx_tool = Tool::new(
|
||||
@@ -441,6 +460,7 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
// choose_app_strategy().cache_dir()
|
||||
|
||||
@@ -17,13 +17,16 @@ use tokio::process::Command;
|
||||
use url::Url;
|
||||
|
||||
use include_dir::{include_dir, Dir};
|
||||
use mcp_core::prompt::{Prompt, PromptArgument, PromptTemplate};
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
resource::Resource,
|
||||
tool::Tool,
|
||||
};
|
||||
use mcp_core::{
|
||||
prompt::{Prompt, PromptArgument, PromptTemplate},
|
||||
tool::ToolAnnotations,
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
|
||||
@@ -161,6 +164,7 @@ impl DeveloperRouter {
|
||||
"command": {"type": "string"}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let text_editor_tool = Tool::new(
|
||||
@@ -199,6 +203,7 @@ impl DeveloperRouter {
|
||||
"file_text": {"type": "string"}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let list_windows_tool = Tool::new(
|
||||
@@ -213,6 +218,13 @@ impl DeveloperRouter {
|
||||
"required": [],
|
||||
"properties": {}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List available windows".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let screen_capture_tool = Tool::new(
|
||||
@@ -241,6 +253,13 @@ impl DeveloperRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Capture a full screen".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let image_processor_tool = Tool::new(
|
||||
@@ -263,6 +282,13 @@ impl DeveloperRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Process Image".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// Get base instructions and working directory
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod storage;
|
||||
use anyhow::{Context, Error};
|
||||
use base64::Engine;
|
||||
use indoc::indoc;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use oauth_pkce::PkceOAuth2Client;
|
||||
use regex::Regex;
|
||||
use serde_json::{json, Value};
|
||||
@@ -209,6 +210,13 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Search GDrive".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let read_tool = Tool::new(
|
||||
@@ -232,6 +240,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["uri"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Read GDrive".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let upload_tool = Tool::new(
|
||||
@@ -270,6 +285,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["name", "mimeType"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Upload file to GDrive".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let create_file_tool = Tool::new(
|
||||
@@ -313,6 +335,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["name", "fileType"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Create new file in GDrive".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let move_file_tool = Tool::new(
|
||||
@@ -339,6 +368,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId", "currentFolderId", "newFolderId"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Move file".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let update_tool = Tool::new(
|
||||
@@ -373,6 +409,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId", "mimeType"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Update a file".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let update_file_tool = Tool::new(
|
||||
@@ -408,6 +451,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId", "fileType"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Update a file".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let sheets_tool = Tool::new(
|
||||
@@ -468,6 +518,7 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["spreadsheetId", "operation"],
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let get_comments_tool = Tool::new(
|
||||
@@ -486,6 +537,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List file comments".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let create_comment_tool = Tool::new(
|
||||
@@ -508,6 +566,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId", "comment"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Create file comment".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let reply_tool = Tool::new(
|
||||
@@ -538,6 +603,13 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId", "commentId", "content"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Reply to a comment".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let list_drives_tool = Tool::new(
|
||||
@@ -555,6 +627,13 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List shared google drives".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let instructions = indoc::formatdoc! {r#"
|
||||
|
||||
@@ -237,6 +237,7 @@ impl JetBrainsProxy {
|
||||
name: name.to_string(),
|
||||
description: first_sentence,
|
||||
input_schema,
|
||||
annotations: None,
|
||||
})
|
||||
} else {
|
||||
debug!("Skipping invalid tool entry: {:?}", t);
|
||||
|
||||
@@ -16,7 +16,7 @@ use mcp_core::{
|
||||
prompt::Prompt,
|
||||
protocol::ServerCapabilities,
|
||||
resource::Resource,
|
||||
tool::{Tool, ToolCall},
|
||||
tool::{Tool, ToolAnnotations, ToolCall},
|
||||
Content,
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
@@ -52,6 +52,13 @@ impl MemoryRouter {
|
||||
},
|
||||
"required": ["category", "data", "is_global"]
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Remember Memory".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let retrieve_memories = Tool::new(
|
||||
@@ -65,6 +72,13 @@ impl MemoryRouter {
|
||||
},
|
||||
"required": ["category", "is_global"]
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Retrieve Memory".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let remove_memory_category = Tool::new(
|
||||
@@ -78,6 +92,13 @@ impl MemoryRouter {
|
||||
},
|
||||
"required": ["category", "is_global"]
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Remove Memory Category".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let remove_specific_memory = Tool::new(
|
||||
@@ -92,6 +113,13 @@ impl MemoryRouter {
|
||||
},
|
||||
"required": ["category", "memory_content", "is_global"]
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Remove Specific Memory".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let instructions = formatdoc! {r#"
|
||||
|
||||
@@ -10,7 +10,7 @@ use mcp_core::{
|
||||
protocol::ServerCapabilities,
|
||||
resource::Resource,
|
||||
role::Role,
|
||||
tool::Tool,
|
||||
tool::{Tool, ToolAnnotations},
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
@@ -45,6 +45,13 @@ impl TutorialRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Load Tutorial".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// Get base instructions and available tutorials
|
||||
|
||||
@@ -58,7 +58,7 @@ async fn main() -> Result<()> {
|
||||
.complete(
|
||||
"You are a helpful assistant. Please describe any text you see in the image.",
|
||||
&messages,
|
||||
&[Tool::new("view_image", "View an image", input_schema)],
|
||||
&[Tool::new("view_image", "View an image", input_schema, None)],
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@ impl Capabilities {
|
||||
format!("{}__{}", name, tool.name),
|
||||
&tool.description,
|
||||
tool.input_schema,
|
||||
tool.annotations,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::agents::capabilities::Capabilities;
|
||||
use crate::message::{Message, MessageContent, ToolRequest};
|
||||
use chrono::Utc;
|
||||
use indoc::indoc;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::{tool::Tool, TextContent};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
@@ -50,6 +51,13 @@ fn create_read_only_tool() -> Tool {
|
||||
},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Check tool operation".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use anyhow::{anyhow, Result};
|
||||
use indoc::indoc;
|
||||
use mcp_core::prompt::Prompt;
|
||||
use mcp_core::protocol::GetPromptResult;
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::tool::{Tool, ToolAnnotations};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
/// Reference implementation of an Agent
|
||||
@@ -102,6 +102,13 @@ impl Agent for ReferenceAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Read a resource".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let list_resources_tool = Tool::new(
|
||||
@@ -120,6 +127,13 @@ impl Agent for ReferenceAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List resources".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
if capabilities.supports_resources() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/// truncation method. Still cannot read resources.
|
||||
use async_trait::async_trait;
|
||||
use futures::stream::BoxStream;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -199,6 +200,13 @@ impl Agent for SummarizeAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Read a resource".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let list_resources_tool = Tool::new(
|
||||
@@ -217,6 +225,13 @@ impl Agent for SummarizeAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List resources".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
if capabilities.supports_resources() {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/// It makes no attempt to handle context limits, and cannot read resources
|
||||
use async_trait::async_trait;
|
||||
use futures::stream::BoxStream;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -195,6 +196,13 @@ impl Agent for TruncateAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Read a resource".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
let list_resources_tool = Tool::new(
|
||||
@@ -213,6 +221,13 @@ impl Agent for TruncateAgent {
|
||||
"extension_name": {"type": "string", "description": "Optional extension name"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List resources".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
|
||||
if capabilities.supports_resources() {
|
||||
|
||||
@@ -530,6 +530,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
Tool::new(
|
||||
"weather",
|
||||
@@ -543,6 +544,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -675,6 +675,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = format_tools(&[tool])?;
|
||||
@@ -766,6 +767,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let tool2 = Tool::new(
|
||||
@@ -781,6 +783,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let result = format_tools(&[tool1, tool2]);
|
||||
|
||||
@@ -351,6 +351,7 @@ mod tests {
|
||||
input_schema: json!({
|
||||
"properties": params
|
||||
}),
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,6 +495,7 @@ mod tests {
|
||||
input_schema: json!({
|
||||
"properties": {}
|
||||
}),
|
||||
annotations: None,
|
||||
}];
|
||||
let result = format_tools(&tools);
|
||||
assert_eq!(result.len(), 1);
|
||||
|
||||
@@ -550,6 +550,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = format_tools(&[tool])?;
|
||||
@@ -641,6 +642,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let tool2 = Tool::new(
|
||||
@@ -656,6 +658,7 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let result = format_tools(&[tool1, tool2]);
|
||||
|
||||
@@ -310,6 +310,7 @@ mod tests {
|
||||
},
|
||||
"required": ["location"]
|
||||
}),
|
||||
annotations: None,
|
||||
}];
|
||||
|
||||
let token_count_without_tools = counter.count_chat_tokens(system_prompt, &messages, &[]);
|
||||
|
||||
@@ -127,6 +127,7 @@ impl ProviderTester {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let message = Message::user().with_text("What's the weather like in San Francisco?");
|
||||
|
||||
@@ -245,7 +245,7 @@ where
|
||||
capabilities: ClientCapabilities,
|
||||
) -> Result<InitializeResult, Error> {
|
||||
let params = InitializeParams {
|
||||
protocol_version: "2024-11-05".to_string(),
|
||||
protocol_version: "2025-03-26".to_string(),
|
||||
client_info: info,
|
||||
capabilities,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,102 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
/// Additional properties describing a tool to clients.
|
||||
///
|
||||
/// NOTE: all properties in ToolAnnotations are **hints**.
|
||||
/// They are not guaranteed to provide a faithful description of
|
||||
/// tool behavior (including descriptive properties like `title`).
|
||||
///
|
||||
/// Clients should never make tool use decisions based on ToolAnnotations
|
||||
/// received from untrusted servers.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ToolAnnotations {
|
||||
/// A human-readable title for the tool.
|
||||
pub title: Option<String>,
|
||||
|
||||
/// If true, the tool does not modify its environment.
|
||||
///
|
||||
/// Default: false
|
||||
#[serde(default)]
|
||||
pub read_only_hint: bool,
|
||||
|
||||
/// If true, the tool may perform destructive updates to its environment.
|
||||
/// If false, the tool performs only additive updates.
|
||||
///
|
||||
/// (This property is meaningful only when `read_only_hint == false`)
|
||||
///
|
||||
/// Default: true
|
||||
#[serde(default = "default_true")]
|
||||
pub destructive_hint: bool,
|
||||
|
||||
/// If true, calling the tool repeatedly with the same arguments
|
||||
/// will have no additional effect on its environment.
|
||||
///
|
||||
/// (This property is meaningful only when `read_only_hint == false`)
|
||||
///
|
||||
/// Default: false
|
||||
#[serde(default)]
|
||||
pub idempotent_hint: bool,
|
||||
|
||||
/// If true, this tool may interact with an "open world" of external
|
||||
/// entities. If false, the tool's domain of interaction is closed.
|
||||
/// For example, the world of a web search tool is open, whereas that
|
||||
/// of a memory tool is not.
|
||||
///
|
||||
/// Default: true
|
||||
#[serde(default = "default_true")]
|
||||
pub open_world_hint: bool,
|
||||
}
|
||||
|
||||
impl Default for ToolAnnotations {
|
||||
fn default() -> Self {
|
||||
ToolAnnotations {
|
||||
title: None,
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Implement builder methods for `ToolAnnotations`
|
||||
impl ToolAnnotations {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_title(mut self, title: impl Into<String>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_read_only(mut self, read_only: bool) -> Self {
|
||||
self.read_only_hint = read_only;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_destructive(mut self, destructive: bool) -> Self {
|
||||
self.destructive_hint = destructive;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_idempotent(mut self, idempotent: bool) -> Self {
|
||||
self.idempotent_hint = idempotent;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_open_world(mut self, open_world: bool) -> Self {
|
||||
self.open_world_hint = open_world;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A tool that can be used by a model.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -13,11 +109,18 @@ pub struct Tool {
|
||||
pub description: String,
|
||||
/// A JSON Schema object defining the expected parameters for the tool
|
||||
pub input_schema: Value,
|
||||
/// Optional additional tool information.
|
||||
pub annotations: Option<ToolAnnotations>,
|
||||
}
|
||||
|
||||
impl Tool {
|
||||
/// Create a new tool with the given name and description
|
||||
pub fn new<N, D>(name: N, description: D, input_schema: Value) -> Self
|
||||
pub fn new<N, D>(
|
||||
name: N,
|
||||
description: D,
|
||||
input_schema: Value,
|
||||
annotations: Option<ToolAnnotations>,
|
||||
) -> Self
|
||||
where
|
||||
N: Into<String>,
|
||||
D: Into<String>,
|
||||
@@ -26,6 +129,7 @@ impl Tool {
|
||||
name: name.into(),
|
||||
description: description.into(),
|
||||
input_schema,
|
||||
annotations,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use anyhow::Result;
|
||||
use mcp_core::content::Content;
|
||||
use mcp_core::handler::{PromptError, ResourceError};
|
||||
use mcp_core::prompt::{Prompt, PromptArgument};
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::{handler::ToolError, protocol::ServerCapabilities, resource::Resource, tool::Tool};
|
||||
use mcp_server::router::{CapabilitiesBuilder, RouterService};
|
||||
use mcp_server::{ByteTransport, Router, Server};
|
||||
@@ -76,6 +77,13 @@ impl Router for CounterRouter {
|
||||
"properties": {},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Increment Tool".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
),
|
||||
Tool::new(
|
||||
"decrement".to_string(),
|
||||
@@ -85,6 +93,13 @@ impl Router for CounterRouter {
|
||||
"properties": {},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Decrement Tool".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
),
|
||||
Tool::new(
|
||||
"get_value".to_string(),
|
||||
@@ -94,6 +109,13 @@ impl Router for CounterRouter {
|
||||
"properties": {},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Get Value Tool".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ pub trait Router: Send + Sync + 'static {
|
||||
) -> impl Future<Output = Result<JsonRpcResponse, RouterError>> + Send {
|
||||
async move {
|
||||
let result = InitializeResult {
|
||||
protocol_version: "2024-11-05".to_string(),
|
||||
protocol_version: "2025-03-26".to_string(),
|
||||
capabilities: self.capabilities().clone(),
|
||||
server_info: Implementation {
|
||||
name: self.name(),
|
||||
|
||||
Reference in New Issue
Block a user