alexhancock/rmcp-tools-annotations (#3617)
This commit is contained in:
@@ -15,9 +15,9 @@ use goose::config::{
|
||||
};
|
||||
use goose::message::Message;
|
||||
use goose::providers::{create, providers};
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::Tool;
|
||||
use serde_json::{json, Value};
|
||||
use rmcp::model::{Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
|
||||
@@ -387,21 +387,21 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
|
||||
let sample_tool = Tool::new(
|
||||
"get_weather".to_string(),
|
||||
"Get current temperature for a given location.".to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["location"],
|
||||
"properties": {
|
||||
"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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Get weather".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
vec![sample_tool]
|
||||
} else {
|
||||
vec![]
|
||||
@@ -411,9 +411,8 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
|
||||
.complete(
|
||||
"You are an AI agent called Goose. You use tools of connected extensions to solve problems.",
|
||||
&messages,
|
||||
&tools
|
||||
)
|
||||
.await;
|
||||
&tools.into_iter().collect::<Vec<_>>()
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok((_message, _usage)) => {
|
||||
@@ -1270,7 +1269,10 @@ pub async fn configure_tool_permissions_dialog() -> Result<(), Box<dyn Error>> {
|
||||
.map(|tool| {
|
||||
ToolInfo::new(
|
||||
&tool.name,
|
||||
&tool.description,
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default(),
|
||||
get_parameter_names(&tool),
|
||||
permission_manager.get_user_permission(&tool.name),
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ use base64::Engine;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use indoc::{formatdoc, indoc};
|
||||
use reqwest::{Client, Url};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
collections::HashMap, fs, future::Future, path::PathBuf, pin::Pin, sync::Arc, sync::Mutex,
|
||||
};
|
||||
@@ -14,11 +14,13 @@ use std::os::unix::fs::PermissionsExt;
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
tool::{Tool, ToolAnnotations},
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
use rmcp::model::{AnnotateAble, Content, JsonRpcMessage, Prompt, RawResource, Resource};
|
||||
use rmcp::model::{
|
||||
AnnotateAble, Content, JsonRpcMessage, Prompt, RawResource, Resource, Tool, ToolAnnotations,
|
||||
};
|
||||
use rmcp::object;
|
||||
|
||||
mod docx_tool;
|
||||
mod pdf_tool;
|
||||
@@ -58,7 +60,7 @@ impl ComputerControllerRouter {
|
||||
The content is cached locally and can be accessed later using the cache_path
|
||||
returned in the response.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["url"],
|
||||
"properties": {
|
||||
@@ -74,14 +76,14 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Web Scrape".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Web Scrape".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(true),
|
||||
});
|
||||
|
||||
let computer_control_desc = match std::env::consts::OS {
|
||||
"windows" => indoc! {r#"
|
||||
@@ -131,7 +133,7 @@ impl ComputerControllerRouter {
|
||||
let computer_control_tool = Tool::new(
|
||||
"computer_control",
|
||||
computer_control_desc.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["script"],
|
||||
"properties": {
|
||||
@@ -146,7 +148,6 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let quick_script_desc = match std::env::consts::OS {
|
||||
@@ -177,7 +178,7 @@ impl ComputerControllerRouter {
|
||||
let quick_script_tool = Tool::new(
|
||||
"automation_script",
|
||||
quick_script_desc.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["language", "script"],
|
||||
"properties": {
|
||||
@@ -197,7 +198,6 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let cache_tool = Tool::new(
|
||||
@@ -209,7 +209,7 @@ impl ComputerControllerRouter {
|
||||
- delete: Delete a cached file
|
||||
- clear: Clear all cached files
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["command"],
|
||||
"properties": {
|
||||
@@ -224,7 +224,6 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let pdf_tool = Tool::new(
|
||||
@@ -237,7 +236,7 @@ impl ComputerControllerRouter {
|
||||
|
||||
Use this when there is a .pdf file or files that need to be processed.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["path", "operation"],
|
||||
"properties": {
|
||||
@@ -252,14 +251,14 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("PDF process".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("PDF process".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let docx_tool = Tool::new(
|
||||
"docx_tool",
|
||||
@@ -276,7 +275,7 @@ impl ComputerControllerRouter {
|
||||
|
||||
Use this when there is a .docx file that needs to be processed or created.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["path", "operation"],
|
||||
"properties": {
|
||||
@@ -357,7 +356,6 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let xlsx_tool = Tool::new(
|
||||
@@ -375,7 +373,7 @@ impl ComputerControllerRouter {
|
||||
|
||||
Use this when working with Excel spreadsheets to analyze or modify data.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["path", "operation"],
|
||||
"properties": {
|
||||
@@ -419,7 +417,6 @@ impl ComputerControllerRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
// choose_app_strategy().cache_dir()
|
||||
|
||||
@@ -6,7 +6,7 @@ use anyhow::Result;
|
||||
use base64::Engine;
|
||||
use etcetera::{choose_app_strategy, AppStrategy};
|
||||
use indoc::formatdoc;
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
@@ -25,7 +25,6 @@ use include_dir::{include_dir, Dir};
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
tool::{Tool, ToolAnnotations},
|
||||
};
|
||||
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
@@ -33,7 +32,7 @@ use mcp_server::Router;
|
||||
|
||||
use rmcp::model::{
|
||||
Content, JsonRpcMessage, JsonRpcNotification, JsonRpcVersion2_0, Notification, Prompt,
|
||||
PromptArgument, PromptTemplate, Resource, Role,
|
||||
PromptArgument, PromptTemplate, Resource, Role, Tool, ToolAnnotations,
|
||||
};
|
||||
use rmcp::object;
|
||||
|
||||
@@ -166,14 +165,13 @@ impl DeveloperRouter {
|
||||
let bash_tool = Tool::new(
|
||||
"shell".to_string(),
|
||||
shell_tool_desc.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["command"],
|
||||
"properties": {
|
||||
"command": {"type": "string"}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let glob_tool = Tool::new(
|
||||
@@ -193,22 +191,21 @@ impl DeveloperRouter {
|
||||
|
||||
Use this tool when you need to locate files by name patterns rather than content.
|
||||
"#}.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["pattern"],
|
||||
"properties": {
|
||||
"pattern": {"type": "string", "description": "The glob pattern to search for"},
|
||||
"path": {"type": "string", "description": "The directory to search in (defaults to current directory)"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Search files by pattern".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Search files by pattern".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let grep_tool = Tool::new(
|
||||
"grep".to_string(),
|
||||
@@ -242,21 +239,20 @@ impl DeveloperRouter {
|
||||
properly filters results to respect ignored files.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["command"],
|
||||
"properties": {
|
||||
"command": {"type": "string", "description": "The search command to execute (rg, grep, find, etc.)"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Search file contents".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Search file contents".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
// Create text editor tool with different descriptions based on editor API configuration
|
||||
let (text_editor_desc, str_replace_command) = if let Some(ref editor) = editor_model {
|
||||
@@ -307,7 +303,7 @@ impl DeveloperRouter {
|
||||
let text_editor_tool = Tool::new(
|
||||
"text_editor".to_string(),
|
||||
text_editor_desc.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["command", "path"],
|
||||
"properties": {
|
||||
@@ -336,7 +332,6 @@ impl DeveloperRouter {
|
||||
"file_text": {"type": "string"}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let list_windows_tool = Tool::new(
|
||||
@@ -346,19 +341,19 @@ impl DeveloperRouter {
|
||||
Returns a list of window titles that can be used with the window_title parameter
|
||||
of the screen_capture tool.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("List available windows".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let screen_capture_tool = Tool::new(
|
||||
"screen_capture",
|
||||
@@ -370,7 +365,7 @@ impl DeveloperRouter {
|
||||
|
||||
Only one of display or window_title should be specified.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
@@ -385,15 +380,14 @@ impl DeveloperRouter {
|
||||
"description": "Optional: the exact title of the window to capture. use the list_windows tool to find the available windows."
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Capture a full screen".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Capture a full screen".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let image_processor_tool = Tool::new(
|
||||
"image_processor",
|
||||
@@ -405,7 +399,7 @@ impl DeveloperRouter {
|
||||
|
||||
This allows processing image files for use in the conversation.
|
||||
"#},
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["path"],
|
||||
"properties": {
|
||||
@@ -415,14 +409,14 @@ impl DeveloperRouter {
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Process Image".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Process Image".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
// Get base instructions and working directory
|
||||
let cwd = std::env::current_dir().expect("should have a current working dir");
|
||||
@@ -2342,20 +2336,34 @@ mod tests {
|
||||
// Should use traditional description with str_replace command
|
||||
assert!(text_editor_tool
|
||||
.description
|
||||
.contains("Replace a string in a file with a new string"));
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc
|
||||
.contains("Replace a string in a file with a new string")));
|
||||
assert!(text_editor_tool
|
||||
.description
|
||||
.contains("the `old_str` needs to exactly match one"));
|
||||
assert!(text_editor_tool.description.contains("str_replace"));
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc
|
||||
.contains("the `old_str` needs to exactly match one")));
|
||||
assert!(text_editor_tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc.contains("str_replace")));
|
||||
|
||||
// Should not contain editor API description or edit_file command
|
||||
assert!(!text_editor_tool
|
||||
.description
|
||||
.contains("Edit the file with the new content"));
|
||||
assert!(!text_editor_tool.description.contains("edit_file"));
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc
|
||||
.contains("Edit the file with the new content")));
|
||||
assert!(!text_editor_tool
|
||||
.description
|
||||
.contains("work out how to place old_str with it intelligently"));
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc.contains("edit_file")));
|
||||
assert!(!text_editor_tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc
|
||||
.contains("work out how to place old_str with it intelligently")));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
@@ -7,17 +7,18 @@ use base64::Engine;
|
||||
use chrono::NaiveDate;
|
||||
use indoc::indoc;
|
||||
use lazy_static::lazy_static;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
tool::Tool,
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
use oauth_pkce::PkceOAuth2Client;
|
||||
use regex::Regex;
|
||||
use rmcp::model::{AnnotateAble, Content, JsonRpcMessage, Prompt, RawResource, Resource};
|
||||
use rmcp::model::{
|
||||
AnnotateAble, Content, JsonRpcMessage, Prompt, RawResource, Resource, Tool, ToolAnnotations,
|
||||
};
|
||||
use rmcp::object;
|
||||
use serde_json::{json, Value};
|
||||
use std::io::Cursor;
|
||||
use std::{env, fs, future::Future, path::Path, pin::Pin, sync::Arc};
|
||||
@@ -219,8 +220,8 @@ impl GoogleDriveRouter {
|
||||
indoc! {r#"
|
||||
List or search for files or labels in google drive by name, given an input search query. At least one of ('name', 'mimeType', or 'parent') are required for file searches.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driveType": {
|
||||
@@ -257,15 +258,14 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
"required": ["driveType"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Search GDrive".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Search GDrive".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let read_tool = Tool::new(
|
||||
"read".to_string(),
|
||||
@@ -280,8 +280,8 @@ impl GoogleDriveRouter {
|
||||
Pass in "gdrive:///1QG8d8wtWe7ZfmG93sW-1h2WXDJDUkOi-9hDnvJLmWrc"
|
||||
Do not include any other path parameters when using URI.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"uri": {
|
||||
@@ -297,23 +297,22 @@ impl GoogleDriveRouter {
|
||||
"description": "Whether or not to include images as base64 encoded strings, defaults to false",
|
||||
}
|
||||
},
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Read GDrive".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Read GDrive".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false)
|
||||
});
|
||||
|
||||
let create_file_tool = Tool::new(
|
||||
"create_file".to_string(),
|
||||
indoc! {r#"
|
||||
Create a new file, including Document, Spreadsheet, Slides, folder, or shortcut, in Google Drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
@@ -346,23 +345,22 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
"required": ["name", "mimeType"],
|
||||
}),
|
||||
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,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Create new file in GDrive".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let move_file_tool = Tool::new(
|
||||
"move_file".to_string(),
|
||||
indoc! {r#"
|
||||
Move a Google Drive file, folder, or shortcut to a new parent folder. You cannot move a folder to a different drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -379,23 +377,22 @@ 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,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Move file".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let update_file_tool = Tool::new(
|
||||
"update_file".to_string(),
|
||||
indoc! {r#"
|
||||
Update an existing file in Google Drive with new content or edit the file's labels.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -486,15 +483,14 @@ impl GoogleDriveRouter {
|
||||
"body": ["mimeType"],
|
||||
"path": ["mimeType"]
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Update a file's contents or labels".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Update a file's contents or labels".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let sheets_tool = Tool::new(
|
||||
"sheets_tool".to_string(),
|
||||
@@ -509,8 +505,8 @@ impl GoogleDriveRouter {
|
||||
- add_sheet: Add a new sheet (tab) to a spreadsheet
|
||||
- clear_values: Clear values from a range
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spreadsheetId": {
|
||||
@@ -553,15 +549,14 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
"required": ["spreadsheetId", "operation"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Work with Google Sheets data using various operations.".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Work with Google Sheets data using various operations.".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let docs_tool = Tool::new(
|
||||
"docs_tool".to_string(),
|
||||
@@ -575,8 +570,8 @@ impl GoogleDriveRouter {
|
||||
- create_paragraph: Create a new paragraph
|
||||
- delete_content: Delete content between positions
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"documentId": {
|
||||
@@ -610,15 +605,14 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
"required": ["documentId", "operation"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Work with Google Docs data using various operations.".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Work with Google Docs data using various operations.".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let get_comments_tool = Tool::new(
|
||||
"get_comments".to_string(),
|
||||
@@ -626,7 +620,7 @@ impl GoogleDriveRouter {
|
||||
List comments for a file in google drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -636,14 +630,14 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("List file comments".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let manage_comment_tool = Tool::new(
|
||||
"manage_comment".to_string(),
|
||||
@@ -654,8 +648,8 @@ impl GoogleDriveRouter {
|
||||
- create: Create a comment for the latest revision of a Google Drive file. The Google Drive API only supports unanchored comments (they don't refer to a specific location in the file).
|
||||
- reply: Add a reply to a comment thread, or resolve a comment.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -681,15 +675,14 @@ impl GoogleDriveRouter {
|
||||
}
|
||||
},
|
||||
"required": ["fileId", "operation", "content"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Manage file comment".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Manage file comment".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let list_drives_tool = Tool::new(
|
||||
"list_drives".to_string(),
|
||||
@@ -697,7 +690,7 @@ impl GoogleDriveRouter {
|
||||
List shared Google drives.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name_contains": {
|
||||
@@ -706,14 +699,14 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("List shared google drives".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let get_permissions_tool = Tool::new(
|
||||
"get_permissions".to_string(),
|
||||
@@ -721,7 +714,7 @@ impl GoogleDriveRouter {
|
||||
List sharing permissions for a file, folder, or shared drive.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -731,14 +724,14 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
"required": ["fileId"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("List sharing permissions".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("List sharing permissions".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let sharing_tool = Tool::new(
|
||||
"sharing".to_string(),
|
||||
@@ -750,8 +743,8 @@ impl GoogleDriveRouter {
|
||||
- update: Update an existing permission to a different role. (You cannot change the type or to whom it is targeted).
|
||||
- delete: Delete an existing permission.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
.to_string(),
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fileId": {
|
||||
@@ -787,15 +780,14 @@ impl GoogleDriveRouter {
|
||||
},
|
||||
},
|
||||
"required": ["fileId", "operation"],
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Manage file sharing".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Manage file sharing".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let instructions = indoc::formatdoc! {r#"
|
||||
Google Drive MCP Server Instructions
|
||||
|
||||
@@ -4,13 +4,13 @@ use indoc::formatdoc;
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
tool::{Tool, ToolAnnotations, ToolCall},
|
||||
tool::ToolCall,
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
use rmcp::model::JsonRpcMessage;
|
||||
use rmcp::model::{Content, Prompt, Resource};
|
||||
use serde_json::{json, Value};
|
||||
use rmcp::model::{Content, JsonRpcMessage, Prompt, Resource, Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
@@ -41,7 +41,7 @@ impl MemoryRouter {
|
||||
let remember_memory = Tool::new(
|
||||
"remember_memory",
|
||||
"Stores a memory with optional tags in a specified category",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string"},
|
||||
@@ -51,19 +51,19 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Remember Memory".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let retrieve_memories = Tool::new(
|
||||
"retrieve_memories",
|
||||
"Retrieves all memories from a specified category",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string"},
|
||||
@@ -71,19 +71,19 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Retrieve Memory".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let remove_memory_category = Tool::new(
|
||||
"remove_memory_category",
|
||||
"Removes all memories within a specified category",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string"},
|
||||
@@ -91,19 +91,19 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Remove Memory Category".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let remove_specific_memory = Tool::new(
|
||||
"remove_specific_memory",
|
||||
"Removes a specific memory within a specified category",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"category": {"type": "string"},
|
||||
@@ -112,14 +112,14 @@ 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,
|
||||
}),
|
||||
);
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Remove Specific Memory".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
let instructions = formatdoc! {r#"
|
||||
This extension allows storage and retrieval of categorized information with tagging support. It's designed to help
|
||||
|
||||
@@ -4,12 +4,12 @@ use indoc::formatdoc;
|
||||
use mcp_core::{
|
||||
handler::{PromptError, ResourceError, ToolError},
|
||||
protocol::ServerCapabilities,
|
||||
tool::{Tool, ToolAnnotations},
|
||||
};
|
||||
use mcp_server::router::CapabilitiesBuilder;
|
||||
use mcp_server::Router;
|
||||
use rmcp::model::{Content, JsonRpcMessage, Prompt, Resource, Role};
|
||||
use serde_json::{json, Value};
|
||||
use rmcp::model::{Content, JsonRpcMessage, Prompt, Resource, Role, Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
use serde_json::Value;
|
||||
use std::{future::Future, pin::Pin};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
@@ -31,7 +31,7 @@ impl TutorialRouter {
|
||||
let load_tutorial = Tool::new(
|
||||
"load_tutorial".to_string(),
|
||||
"Load a specific tutorial by name. The tutorial will be returned as markdown content that provides step by step instructions.".to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
@@ -40,15 +40,14 @@ impl TutorialRouter {
|
||||
"description": "Name of the tutorial to load, e.g. 'getting-started' or 'developer-mcp'"
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Load Tutorial".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
);
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Load Tutorial".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
});
|
||||
|
||||
// Get base instructions and available tutorials
|
||||
let available_tutorials = Self::get_available_tutorials();
|
||||
|
||||
@@ -207,7 +207,10 @@ async fn get_tools(
|
||||
|
||||
ToolInfo::new(
|
||||
&tool.name,
|
||||
&tool.description,
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default(),
|
||||
get_parameter_names(&tool),
|
||||
permission,
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::state::AppState;
|
||||
use axum::{extract::State, routing::post, Json, Router};
|
||||
use goose::agents::{extension::Envs, ExtensionConfig};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use rmcp::model::Tool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing;
|
||||
|
||||
@@ -80,7 +81,7 @@ enum ExtensionConfigRequest {
|
||||
/// The name to identify this extension
|
||||
name: String,
|
||||
/// The tools provided by this extension
|
||||
tools: Vec<mcp_core::tool::Tool>,
|
||||
tools: Vec<Tool>,
|
||||
/// Optional instructions for using the tools
|
||||
instructions: Option<String>,
|
||||
},
|
||||
|
||||
@@ -404,7 +404,6 @@ mod tests {
|
||||
errors::ProviderError,
|
||||
},
|
||||
};
|
||||
use mcp_core::tool::Tool;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MockProvider {
|
||||
@@ -421,7 +420,7 @@ mod tests {
|
||||
&self,
|
||||
_system: &str,
|
||||
_messages: &[Message],
|
||||
_tools: &[Tool],
|
||||
_tools: &[rmcp::model::Tool],
|
||||
) -> anyhow::Result<(Message, ProviderUsage), ProviderError> {
|
||||
Ok((
|
||||
Message::assistant().with_text("Mock response"),
|
||||
|
||||
@@ -5,8 +5,9 @@ use goose::{
|
||||
message::Message,
|
||||
providers::{bedrock::BedrockProvider, databricks::DatabricksProvider, openai::OpenAiProvider},
|
||||
};
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use rmcp::model::Content;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::{Content, Tool};
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
use std::fs;
|
||||
|
||||
@@ -42,7 +43,7 @@ async fn main() -> Result<()> {
|
||||
];
|
||||
|
||||
// Get a response from the model about the image
|
||||
let input_schema = json!({
|
||||
let input_schema = object!({
|
||||
"type": "object",
|
||||
"required": ["path"],
|
||||
"properties": {
|
||||
@@ -57,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, None)],
|
||||
&[Tool::new("view_image", "View an image", input_schema)],
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -42,8 +42,9 @@ use crate::providers::errors::ProviderError;
|
||||
use crate::recipe::{Author, Recipe, Response, Settings, SubRecipe};
|
||||
use crate::scheduler_trait::SchedulerTrait;
|
||||
use crate::tool_monitor::{ToolCall, ToolMonitor};
|
||||
use mcp_core::{protocol::GetPromptResult, tool::Tool, ToolError, ToolResult};
|
||||
use mcp_core::{protocol::GetPromptResult, ToolError, ToolResult};
|
||||
use regex::Regex;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{Content, JsonRpcMessage, Prompt};
|
||||
use serde_json::Value;
|
||||
use tokio::sync::{mpsc, Mutex, RwLock};
|
||||
@@ -538,10 +539,10 @@ impl Agent {
|
||||
let mut frontend_tools = self.frontend_tools.lock().await;
|
||||
for tool in tools {
|
||||
let frontend_tool = FrontendTool {
|
||||
name: tool.name.clone(),
|
||||
name: tool.name.to_string(),
|
||||
tool: tool.clone(),
|
||||
};
|
||||
frontend_tools.insert(tool.name.clone(), frontend_tool);
|
||||
frontend_tools.insert(tool.name.to_string(), frontend_tool);
|
||||
}
|
||||
// Store instructions if provided, using "frontend" as the key
|
||||
let mut frontend_instructions = self.frontend_instructions.lock().await;
|
||||
@@ -1181,7 +1182,10 @@ impl Agent {
|
||||
.map(|tool| {
|
||||
ToolInfo::new(
|
||||
&tool.name,
|
||||
&tool.description,
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default(),
|
||||
get_parameter_names(&tool),
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mcp_client::client::Error as ClientError;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tracing::warn;
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::config::{Config, ExtensionConfigManager};
|
||||
use crate::prompt_template;
|
||||
use mcp_client::client::{ClientCapabilities, ClientInfo, McpClient, McpClientTrait};
|
||||
use mcp_client::transport::{SseTransport, StdioTransport, StreamableHttpTransport, Transport};
|
||||
use mcp_core::{Tool, ToolCall, ToolError};
|
||||
use rmcp::model::{Content, Prompt, Resource, ResourceContents};
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{Content, Prompt, Resource, ResourceContents, Tool};
|
||||
use serde_json::Value;
|
||||
|
||||
// By default, we set it to Jan 1, 2020 if the resource does not have a timestamp
|
||||
@@ -380,13 +380,18 @@ impl ExtensionManager {
|
||||
let mut client_tools = client_guard.list_tools(None).await?;
|
||||
|
||||
loop {
|
||||
for tool in client_tools.tools {
|
||||
tools.push(Tool::new(
|
||||
format!("{}__{}", name, tool.name),
|
||||
&tool.description,
|
||||
tool.input_schema,
|
||||
tool.annotations,
|
||||
));
|
||||
for client_tool in client_tools.tools {
|
||||
let mut tool = Tool::new(
|
||||
format!("{}__{}", name, client_tool.name),
|
||||
client_tool.description.unwrap_or_default(),
|
||||
client_tool.input_schema,
|
||||
);
|
||||
|
||||
if tool.annotations.is_some() {
|
||||
tool = tool.annotate(client_tool.annotations.unwrap())
|
||||
}
|
||||
|
||||
tools.push(tool);
|
||||
}
|
||||
|
||||
// Exit loop when there are no more pages
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
use crate::agents::tool_execution::ToolCallResult;
|
||||
use crate::recipe::Response;
|
||||
use indoc::formatdoc;
|
||||
use mcp_core::{
|
||||
tool::{Tool, ToolAnnotations},
|
||||
ToolCall, ToolError,
|
||||
};
|
||||
use rmcp::model::Content;
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{Content, Tool, ToolAnnotations};
|
||||
use serde_json::Value;
|
||||
|
||||
pub const FINAL_OUTPUT_TOOL_NAME: &str = "recipe__final_output";
|
||||
@@ -64,15 +61,21 @@ impl FinalOutputTool {
|
||||
Tool::new(
|
||||
FINAL_OUTPUT_TOOL_NAME.to_string(),
|
||||
instructions,
|
||||
self.response.json_schema.as_ref().unwrap().clone(),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Final Output".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: true,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
self.response
|
||||
.json_schema
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Final Output".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(true),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn system_prompt(&self) -> String {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use indoc::indoc;
|
||||
use mcp_core::tool::{Tool, ToolAnnotations};
|
||||
use serde_json::json;
|
||||
use rmcp::model::{Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
|
||||
pub const PLATFORM_READ_RESOURCE_TOOL_NAME: &str = "platform__read_resource";
|
||||
pub const PLATFORM_LIST_RESOURCES_TOOL_NAME: &str = "platform__list_resources";
|
||||
@@ -20,22 +20,21 @@ pub fn read_resource_tool() -> Tool {
|
||||
resource URI in the provided extension, and reads in the resource content. If no extension
|
||||
is provided, the tool will search all extensions for the resource.
|
||||
"#}.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["uri"],
|
||||
"properties": {
|
||||
"uri": {"type": "string", "description": "Resource URI"},
|
||||
"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,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Read a resource".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_resources_tool() -> Tool {
|
||||
@@ -50,20 +49,20 @@ pub fn list_resources_tool() -> Tool {
|
||||
is provided, the tool will search all extensions for the resource.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"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,
|
||||
}),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("List resources".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn search_available_extensions_tool() -> Tool {
|
||||
@@ -73,19 +72,18 @@ pub fn search_available_extensions_tool() -> Tool {
|
||||
Use this tool when you're unable to find a specific feature or functionality you need to complete your task, or when standard approaches aren't working.
|
||||
These extensions might provide the exact tools needed to solve your problem.
|
||||
If you find a relevant one, consider using your tools to enable it.".to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Discover extensions".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Discover extensions".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn manage_extensions_tool() -> Tool {
|
||||
@@ -96,7 +94,7 @@ pub fn manage_extensions_tool() -> Tool {
|
||||
Enable or disable an extension by providing the extension name.
|
||||
"
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["action", "extension_name"],
|
||||
"properties": {
|
||||
@@ -104,14 +102,13 @@ pub fn manage_extensions_tool() -> Tool {
|
||||
"extension_name": {"type": "string", "description": "The name of the extension to enable"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Enable or disable an extension".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Enable or disable an extension".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn manage_schedule_tool() -> Tool {
|
||||
@@ -133,7 +130,7 @@ pub fn manage_schedule_tool() -> Tool {
|
||||
- "session_content": Get the full content (messages) of a specific session
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["action"],
|
||||
"properties": {
|
||||
@@ -149,12 +146,11 @@ pub fn manage_schedule_tool() -> Tool {
|
||||
"session_id": {"type": "string", "description": "Session identifier for session_content action"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Manage scheduled recipes".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true, // Can kill jobs
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Manage scheduled recipes".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true), // Can kill jobs
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
use crate::agents::subagent_execution_tool::tasks_manager::TasksManager;
|
||||
use crate::agents::subagent_execution_tool::{lib::ExecutionMode, task_types::Task};
|
||||
use crate::agents::tool_execution::ToolCallResult;
|
||||
use mcp_core::{tool::ToolAnnotations, Tool, ToolError};
|
||||
use rmcp::model::Content;
|
||||
use mcp_core::ToolError;
|
||||
use rmcp::model::{Content, Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
pub const DYNAMIC_TASK_TOOL_NAME_PREFIX: &str = "dynamic_task__create_task";
|
||||
@@ -35,7 +36,7 @@ pub fn create_dynamic_task_tool() -> Tool {
|
||||
text_instruction: Get weather for Los Angeles.
|
||||
text_instruction: Get weather for San Francisco.
|
||||
".to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task_parameters": {
|
||||
@@ -56,15 +57,14 @@ pub fn create_dynamic_task_tool() -> Tool {
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Dynamic Task Creation".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Dynamic Task Creation".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(true),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_task_parameters(params: &Value) -> Vec<Value> {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use mcp_core::tool::{Tool, ToolAnnotations};
|
||||
use rmcp::model::{Tool, ToolAnnotations};
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::agents::subagent_execution_tool::lib::{ExecutionMode, Task};
|
||||
@@ -15,6 +16,7 @@ pub const SUB_RECIPE_TASK_TOOL_NAME_PREFIX: &str = "subrecipe__create_task";
|
||||
|
||||
pub fn create_sub_recipe_task_tool(sub_recipe: &SubRecipe) -> Tool {
|
||||
let input_schema = get_input_schema(sub_recipe).unwrap();
|
||||
|
||||
Tool::new(
|
||||
format!("{}_{}", SUB_RECIPE_TASK_TOOL_NAME_PREFIX, sub_recipe.name),
|
||||
format!(
|
||||
@@ -27,15 +29,17 @@ pub fn create_sub_recipe_task_tool(sub_recipe: &SubRecipe) -> Tool {
|
||||
After creating the tasks and execution_mode is provided, pass them to the task executor to run these tasks",
|
||||
sub_recipe.name
|
||||
),
|
||||
input_schema,
|
||||
Some(ToolAnnotations {
|
||||
title: Some(format!("create multiple sub recipe tasks for {}", sub_recipe.name)),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}),
|
||||
)
|
||||
Arc::new(input_schema.as_object().unwrap().clone())
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some(format!(
|
||||
"create multiple sub recipe tasks for {}",
|
||||
sub_recipe.name
|
||||
)),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(true),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_task_parameters(params: &Value) -> Vec<Value> {
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::providers::toolshim::{
|
||||
modify_system_prompt_for_tool_json, OllamaInterpreter,
|
||||
};
|
||||
use crate::session;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
use super::super::agents::Agent;
|
||||
|
||||
@@ -110,11 +110,11 @@ impl Agent {
|
||||
.iter()
|
||||
.fold((HashSet::new(), HashSet::new()), |mut acc, tool| {
|
||||
match &tool.annotations {
|
||||
Some(annotations) if annotations.read_only_hint => {
|
||||
acc.0.insert(tool.name.clone());
|
||||
Some(annotations) if annotations.read_only_hint.unwrap_or(false) => {
|
||||
acc.0.insert(tool.name.to_string());
|
||||
}
|
||||
_ => {
|
||||
acc.1.insert(tool.name.clone());
|
||||
acc.1.insert(tool.name.to_string());
|
||||
}
|
||||
}
|
||||
acc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::ToolError;
|
||||
use rmcp::model::Content;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
@@ -128,7 +128,15 @@ impl RouterToolSelector for VectorToolSelector {
|
||||
.map(|tool| {
|
||||
let schema_str = serde_json::to_string_pretty(&tool.input_schema)
|
||||
.unwrap_or_else(|_| "{}".to_string());
|
||||
format!("{} {} {}", tool.name, tool.description, schema_str)
|
||||
format!(
|
||||
"{} {} {}",
|
||||
tool.name,
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default(),
|
||||
schema_str
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -154,8 +162,12 @@ impl RouterToolSelector for VectorToolSelector {
|
||||
let schema_str = serde_json::to_string_pretty(&tool.input_schema)
|
||||
.unwrap_or_else(|_| "{}".to_string());
|
||||
crate::agents::tool_vectordb::ToolRecord {
|
||||
tool_name: tool.name.clone(),
|
||||
description: tool.description.clone(),
|
||||
tool_name: tool.name.to_string(),
|
||||
description: tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map(|d| d.to_string())
|
||||
.unwrap_or_default(),
|
||||
schema: schema_str,
|
||||
vector,
|
||||
extension_name: extension_name.to_string(),
|
||||
@@ -305,7 +317,10 @@ impl RouterToolSelector for LLMToolSelector {
|
||||
let tool_string = format!(
|
||||
"Tool: {}\nDescription: {}\nSchema: {}",
|
||||
tool.name,
|
||||
tool.description,
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default(),
|
||||
serde_json::to_string_pretty(&tool.input_schema)
|
||||
.unwrap_or_else(|_| "{}".to_string())
|
||||
);
|
||||
|
||||
@@ -3,8 +3,8 @@ use super::platform_tools::{
|
||||
PLATFORM_READ_RESOURCE_TOOL_NAME, PLATFORM_SEARCH_AVAILABLE_EXTENSIONS_TOOL_NAME,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use mcp_core::tool::{Tool, ToolAnnotations};
|
||||
use serde_json::json;
|
||||
use rmcp::model::{Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
|
||||
pub const ROUTER_VECTOR_SEARCH_TOOL_NAME: &str = "router__vector_search";
|
||||
pub const ROUTER_LLM_SEARCH_TOOL_NAME: &str = "router__llm_search";
|
||||
@@ -24,7 +24,7 @@ pub fn vector_search_tool() -> Tool {
|
||||
Extension name is not optional, it is required.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["query", "extension_name"],
|
||||
"properties": {
|
||||
@@ -32,15 +32,14 @@ pub fn vector_search_tool() -> Tool {
|
||||
"k": {"type": "integer", "description": "The number of tools to retrieve (defaults to 5)", "default": 5},
|
||||
"extension_name": {"type": "string", "description": "Name of the extension to filter tools by"}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Vector search for relevant tools".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Vector search for relevant tools".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn vector_search_tool_prompt() -> String {
|
||||
@@ -81,7 +80,7 @@ pub fn llm_search_tool() -> Tool {
|
||||
The returned result will be a list of tool names, descriptions, and schemas from which you, the agent can select the most relevant tool to invoke.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["query", "extension_name"],
|
||||
"properties": {
|
||||
@@ -89,15 +88,14 @@ pub fn llm_search_tool() -> Tool {
|
||||
"query": {"type": "string", "description": "The query to search for the most relevant tools based on the user's messages"},
|
||||
"k": {"type": "integer", "description": "The number of tools to retrieve (defaults to 5)", "default": 5}
|
||||
}
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("LLM search for relevant tools".to_string()),
|
||||
read_only_hint: true,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("LLM search for relevant tools".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn llm_search_tool_prompt() -> String {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use mcp_core::{Tool, ToolError};
|
||||
use mcp_core::ToolError;
|
||||
use rmcp::model::Content;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ use crate::{
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use mcp_core::{handler::ToolError, tool::Tool};
|
||||
use mcp_core::handler::ToolError;
|
||||
use rmcp::model::Tool;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use serde_json::{self};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
@@ -336,10 +337,10 @@ impl SubAgent {
|
||||
let tools_with_descriptions: Vec<String> = available_tools
|
||||
.iter()
|
||||
.map(|t| {
|
||||
if t.description.is_empty() {
|
||||
t.name.clone()
|
||||
if let Some(description) = &t.description {
|
||||
format!("{}: {}", t.name, description)
|
||||
} else {
|
||||
format!("{}: {}", t.name, t.description)
|
||||
t.name.to_string()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use mcp_core::{tool::ToolAnnotations, Tool, ToolError};
|
||||
use rmcp::model::Content;
|
||||
use mcp_core::ToolError;
|
||||
use rmcp::model::{Content, Tool, ToolAnnotations};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::agents::subagent_task_config::TaskConfig;
|
||||
@@ -9,6 +9,7 @@ use crate::agents::{
|
||||
subagent_execution_tool::tasks_manager::TasksManager, tool_execution::ToolCallResult,
|
||||
};
|
||||
use rmcp::model::JsonRpcMessage;
|
||||
use rmcp::object;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_stream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -18,21 +19,21 @@ pub fn create_subagent_execute_task_tool() -> Tool {
|
||||
Tool::new(
|
||||
SUBAGENT_EXECUTE_TASK_TOOL_NAME,
|
||||
"Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task.
|
||||
EXECUTION STRATEGY DECISION:
|
||||
1. If the tasks are created with execution_mode, use the execution_mode.
|
||||
2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'
|
||||
EXECUTION STRATEGY DECISION:
|
||||
1. If the tasks are created with execution_mode, use the execution_mode.
|
||||
2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'
|
||||
|
||||
IMPLEMENTATION:
|
||||
- Sequential execution: Call this tool multiple times, passing exactly ONE task per call
|
||||
- Parallel execution: Call this tool once, passing an ARRAY of all tasks
|
||||
IMPLEMENTATION:
|
||||
- Sequential execution: Call this tool multiple times, passing exactly ONE task per call
|
||||
- Parallel execution: Call this tool once, passing an ARRAY of all tasks
|
||||
|
||||
EXAMPLES:
|
||||
User Intent Based:
|
||||
- User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)
|
||||
- User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)
|
||||
- User: 'run these simultaneously' → Parallel (1 tool call with task array)
|
||||
- User: 'do task A then task B' → Sequential (2 separate tool calls)",
|
||||
serde_json::json!({
|
||||
EXAMPLES:
|
||||
User Intent Based:
|
||||
- User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)
|
||||
- User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)
|
||||
- User: 'run these simultaneously' → Parallel (1 tool call with task array)
|
||||
- User: 'do task A then task B' → Sequential (2 separate tool calls)",
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"execution_mode": {
|
||||
@@ -50,15 +51,14 @@ User Intent Based:
|
||||
}
|
||||
},
|
||||
"required": ["task_ids"]
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Run tasks in parallel".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: true,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: true,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Run tasks in parallel".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(true),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(true),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn run_tasks(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::session;
|
||||
use mcp_core::{Tool, ToolResult};
|
||||
use rmcp::model::Content;
|
||||
use mcp_core::ToolResult;
|
||||
use rmcp::model::{Content, Tool};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use mcp_core::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
use crate::{
|
||||
message::Message,
|
||||
|
||||
@@ -220,9 +220,9 @@ mod tests {
|
||||
use crate::providers::base::{Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||
use crate::providers::errors::ProviderError;
|
||||
use chrono::Utc;
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::ToolCall;
|
||||
use rmcp::model::Role;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{AnnotateAble, Content, RawTextContent};
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -5,10 +5,10 @@ use crate::message::{Message, MessageContent, ToolRequest};
|
||||
use crate::providers::base::Provider;
|
||||
use chrono::Utc;
|
||||
use indoc::indoc;
|
||||
use mcp_core::tool::Tool;
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use rmcp::model::{Tool, ToolAnnotations};
|
||||
use rmcp::object;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -45,7 +45,7 @@ fn create_read_only_tool() -> Tool {
|
||||
Use this analysis to generate the list of tools performing read-only operations from the provided tool requests.
|
||||
"#}
|
||||
.to_string(),
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"read_only_tools": {
|
||||
@@ -57,15 +57,14 @@ 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,
|
||||
}),
|
||||
)
|
||||
})
|
||||
).annotate(ToolAnnotations {
|
||||
title: Some("Check tool operation".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds the message to be sent to the LLM for detecting read-only operations.
|
||||
@@ -266,9 +265,8 @@ mod tests {
|
||||
use crate::providers::base::{Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||
use crate::providers::errors::ProviderError;
|
||||
use chrono::Utc;
|
||||
use mcp_core::ToolCall;
|
||||
use mcp_core::{tool::Tool, ToolResult};
|
||||
use rmcp::model::Role;
|
||||
use mcp_core::{ToolCall, ToolResult};
|
||||
use rmcp::model::{Role, Tool};
|
||||
use serde_json::json;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
@@ -324,7 +322,10 @@ mod tests {
|
||||
async fn test_create_read_only_tool() {
|
||||
let tool = create_read_only_tool();
|
||||
assert_eq!(tool.name, "platform__tool_by_tool_permission");
|
||||
assert!(tool.description.contains("read-only operation"));
|
||||
assert!(tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map_or(false, |desc| desc.contains("read-only operation")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -19,7 +19,7 @@ use super::formats::anthropic::{
|
||||
use super::utils::{emit_debug_trace, get_model};
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const ANTHROPIC_DEFAULT_MODEL: &str = "claude-3-5-sonnet-latest";
|
||||
pub const ANTHROPIC_KNOWN_MODELS: &[&str] = &[
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::formats::openai::{create_request, get_usage, response_to_message};
|
||||
use super::utils::{emit_debug_trace, get_model, handle_response_openai_compat, ImageFormat};
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const AZURE_DEFAULT_MODEL: &str = "gpt-4o";
|
||||
pub const AZURE_DOC_URL: &str =
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use super::errors::ProviderError;
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
@@ -6,7 +6,7 @@ use async_trait::async_trait;
|
||||
use aws_sdk_bedrockruntime::config::ProvideCredentials;
|
||||
use aws_sdk_bedrockruntime::operation::converse::ConverseError;
|
||||
use aws_sdk_bedrockruntime::{types as bedrock, Client};
|
||||
use mcp_core::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use tokio::time::sleep;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::utils::emit_debug_trace;
|
||||
use crate::config::Config;
|
||||
use crate::message::{Message, MessageContent};
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const CLAUDE_CODE_DEFAULT_MODEL: &str = "claude-3-5-sonnet-latest";
|
||||
pub const CLAUDE_CODE_KNOWN_MODELS: &[&str] = &["sonnet", "opus", "claude-3-5-sonnet-latest"];
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::config::ConfigError;
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::formats::openai::{get_usage, response_to_streaming_message};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::json;
|
||||
use tokio::time::sleep;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
@@ -27,7 +27,7 @@ use anyhow::Result;
|
||||
#[cfg(test)]
|
||||
use super::errors::ProviderError;
|
||||
#[cfg(test)]
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
fn default_lead_turns() -> usize {
|
||||
3
|
||||
|
||||
@@ -3,8 +3,8 @@ use crate::model::ModelConfig;
|
||||
use crate::providers::base::Usage;
|
||||
use crate::providers::errors::ProviderError;
|
||||
use anyhow::{anyhow, Result};
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use rmcp::model::Role;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::{Role, Tool};
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -676,6 +676,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
@@ -858,7 +859,7 @@ mod tests {
|
||||
Tool::new(
|
||||
"calculator",
|
||||
"Calculate mathematical expressions",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
@@ -867,12 +868,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
Tool::new(
|
||||
"weather",
|
||||
"Get weather information",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
@@ -881,7 +881,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ use aws_sdk_bedrockruntime::types as bedrock;
|
||||
use aws_smithy_types::{Document, Number};
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use mcp_core::{Tool, ToolCall, ToolError, ToolResult};
|
||||
use rmcp::model::{Content, RawContent, ResourceContents, Role};
|
||||
use mcp_core::{ToolCall, ToolError, ToolResult};
|
||||
use rmcp::model::{Content, RawContent, ResourceContents, Role, Tool};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::super::base::Usage;
|
||||
@@ -184,9 +184,14 @@ pub fn to_bedrock_tool(tool: &Tool) -> Result<bedrock::Tool> {
|
||||
Ok(bedrock::Tool::ToolSpec(
|
||||
bedrock::ToolSpecification::builder()
|
||||
.name(tool.name.to_string())
|
||||
.description(tool.description.to_string())
|
||||
.description(
|
||||
tool.description
|
||||
.as_ref()
|
||||
.map(|d| d.to_string())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.input_schema(bedrock::ToolInputSchema::Json(to_bedrock_json(
|
||||
&tool.input_schema,
|
||||
&Value::Object(tool.input_schema.as_ref().clone()),
|
||||
)))
|
||||
.build()?,
|
||||
))
|
||||
|
||||
@@ -5,10 +5,8 @@ use crate::providers::utils::{
|
||||
sanitize_function_name, ImageFormat,
|
||||
};
|
||||
use anyhow::{anyhow, Error};
|
||||
use mcp_core::ToolError;
|
||||
use mcp_core::{Tool, ToolCall};
|
||||
use rmcp::model::Role;
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents};
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents, Role, Tool};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
@@ -589,6 +587,7 @@ pub fn create_request(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
@@ -703,7 +702,7 @@ mod tests {
|
||||
let tool = Tool::new(
|
||||
"test_tool",
|
||||
"A test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -713,7 +712,6 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = format_tools(&[tool])?;
|
||||
@@ -795,7 +793,7 @@ mod tests {
|
||||
let tool1 = Tool::new(
|
||||
"test_tool",
|
||||
"Test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -805,13 +803,12 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let tool2 = Tool::new(
|
||||
"test_tool",
|
||||
"Test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -821,7 +818,6 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let result = format_tools(&[tool1, tool2]);
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::base::Usage;
|
||||
use anyhow::{Context, Result};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
@@ -4,9 +4,10 @@ use crate::providers::base::Usage;
|
||||
use crate::providers::errors::ProviderError;
|
||||
use crate::providers::utils::{is_valid_function_name, sanitize_function_name};
|
||||
use anyhow::Result;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use rmcp::model::{AnnotateAble, RawContent, Role};
|
||||
use rmcp::model::{AnnotateAble, RawContent, Role, Tool};
|
||||
|
||||
use serde_json::{json, Map, Value};
|
||||
use std::ops::Deref;
|
||||
|
||||
@@ -132,18 +133,17 @@ pub fn format_tools(tools: &[Tool]) -> Vec<Value> {
|
||||
let mut parameters = Map::new();
|
||||
parameters.insert("name".to_string(), json!(tool.name));
|
||||
parameters.insert("description".to_string(), json!(tool.description));
|
||||
if let Some(tool_input_schema) = tool.input_schema.as_object() {
|
||||
// Only add the parameters key if the tool schema has non-empty properties.
|
||||
if tool_input_schema
|
||||
.get("properties")
|
||||
.and_then(|v| v.as_object())
|
||||
.is_some_and(|p| !p.is_empty())
|
||||
{
|
||||
parameters.insert(
|
||||
"parameters".to_string(),
|
||||
process_map(tool_input_schema, None),
|
||||
);
|
||||
}
|
||||
let tool_input_schema = &tool.input_schema;
|
||||
// Only add the parameters key if the tool schema has non-empty properties.
|
||||
if tool_input_schema
|
||||
.get("properties")
|
||||
.and_then(|v| v.as_object())
|
||||
.is_some_and(|p| !p.is_empty())
|
||||
{
|
||||
parameters.insert(
|
||||
"parameters".to_string(),
|
||||
process_map(tool_input_schema, None),
|
||||
);
|
||||
}
|
||||
json!(parameters)
|
||||
})
|
||||
@@ -320,7 +320,7 @@ pub fn create_request(
|
||||
}
|
||||
let mut generation_config = Map::new();
|
||||
if let Some(temp) = model_config.temperature {
|
||||
generation_config.insert("temperature".to_string(), json!(temp));
|
||||
generation_config.insert("temperature".to_string(), json!(temp as f64));
|
||||
}
|
||||
if let Some(tokens) = model_config.max_tokens {
|
||||
generation_config.insert("maxOutputTokens".to_string(), json!(tokens));
|
||||
@@ -329,13 +329,14 @@ pub fn create_request(
|
||||
payload.insert("generationConfig".to_string(), json!(generation_config));
|
||||
}
|
||||
|
||||
Ok(Value::Object(payload))
|
||||
Ok(json!(payload))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::model::Content;
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
fn set_up_text_message(text: &str, role: Role) -> Message {
|
||||
@@ -374,17 +375,6 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
fn set_up_tool(name: &str, description: &str, params: Value) -> Tool {
|
||||
Tool {
|
||||
name: name.to_string(),
|
||||
description: description.to_string(),
|
||||
input_schema: json!({
|
||||
"properties": params
|
||||
}),
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_usage() {
|
||||
let data = json!({
|
||||
@@ -420,8 +410,11 @@ mod tests {
|
||||
"param1": "value1"
|
||||
});
|
||||
let messages = vec![
|
||||
set_up_tool_request_message("id", ToolCall::new("tool_name", json!(arguments))),
|
||||
set_up_tool_confirmation_message("id2", ToolCall::new("tool_name_2", json!(arguments))),
|
||||
set_up_tool_request_message("id", ToolCall::new("tool_name", arguments.clone())),
|
||||
set_up_tool_confirmation_message(
|
||||
"id2",
|
||||
ToolCall::new("tool_name_2", arguments.clone()),
|
||||
),
|
||||
];
|
||||
let payload = format_messages(&messages);
|
||||
assert_eq!(payload.len(), 1);
|
||||
@@ -478,100 +471,106 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tools_to_google_spec_with_valid_tools() {
|
||||
let params1 = json!({
|
||||
"param1": {
|
||||
"type": "string",
|
||||
"description": "A parameter",
|
||||
"field_does_not_accept": ["value1", "value2"]
|
||||
}
|
||||
});
|
||||
let params2 = json!({
|
||||
"param2": {
|
||||
"type": "string",
|
||||
"description": "B parameter",
|
||||
}
|
||||
});
|
||||
let params3 = json!({
|
||||
"body": {
|
||||
"description": "Review comment text",
|
||||
"type": "string"
|
||||
},
|
||||
"comments": {
|
||||
"description": "Line-specific comments array of objects to place comments on pull request changes. Requires path and body. For line comments use line or position. For multi-line comments use start_line and line with optional side parameters.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"body": {
|
||||
"description": "comment body",
|
||||
"type": "string"
|
||||
},
|
||||
"line": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "line number in the file to comment on. For multi-line comments, the end of the line range"
|
||||
},
|
||||
"path": {
|
||||
"description": "path to the file",
|
||||
"type": "string"
|
||||
},
|
||||
"position": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "position of the comment in the diff"
|
||||
},
|
||||
"side": {
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range. (LEFT or RIGHT)"
|
||||
},
|
||||
"start_line": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The first line of the range to which the comment refers. Required for multi-line comments."
|
||||
},
|
||||
"start_side": {
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The side of the diff on which the start line resides for multi-line comments. (LEFT or RIGHT)"
|
||||
}
|
||||
},
|
||||
"required": ["path", "body", "position", "line", "side", "start_line", "start_side"],
|
||||
"type": "object"
|
||||
let params1 = object!({
|
||||
"properties": {
|
||||
"param1": {
|
||||
"type": "string",
|
||||
"description": "A parameter",
|
||||
"field_does_not_accept": ["value1", "value2"]
|
||||
}
|
||||
}
|
||||
});
|
||||
let params2 = object!({
|
||||
"properties": {
|
||||
"param2": {
|
||||
"type": "string",
|
||||
"description": "B parameter",
|
||||
}
|
||||
}
|
||||
});
|
||||
let params3 = object!({
|
||||
"properties": {
|
||||
"body": {
|
||||
"description": "Review comment text",
|
||||
"type": "string"
|
||||
},
|
||||
"comments": {
|
||||
"description": "Line-specific comments array of objects to place comments on pull request changes. Requires path and body. For line comments use line or position. For multi-line comments use start_line and line with optional side parameters.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"body": {
|
||||
"description": "comment body",
|
||||
"type": "string"
|
||||
},
|
||||
"line": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "line number in the file to comment on. For multi-line comments, the end of the line range"
|
||||
},
|
||||
"path": {
|
||||
"description": "path to the file",
|
||||
"type": "string"
|
||||
},
|
||||
"position": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "position of the comment in the diff"
|
||||
},
|
||||
"side": {
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range. (LEFT or RIGHT)"
|
||||
},
|
||||
"start_line": {
|
||||
"anyOf": [
|
||||
{ "type": "number" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The first line of the range to which the comment refers. Required for multi-line comments."
|
||||
},
|
||||
"start_side": {
|
||||
"anyOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "null" }
|
||||
],
|
||||
"description": "The side of the diff on which the start line resides for multi-line comments. (LEFT or RIGHT)"
|
||||
}
|
||||
},
|
||||
"required": ["path", "body", "position", "line", "side", "start_line", "start_side"],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"commitId": {
|
||||
"description": "SHA of commit to review",
|
||||
"type": "string"
|
||||
},
|
||||
"event": {
|
||||
"description": "Review action to perform",
|
||||
"enum": ["APPROVE", "REQUEST_CHANGES", "COMMENT"],
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"description": "Repository owner",
|
||||
"type": "string"
|
||||
},
|
||||
"pullNumber": {
|
||||
"description": "Pull request number",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"commitId": {
|
||||
"description": "SHA of commit to review",
|
||||
"type": "string"
|
||||
},
|
||||
"event": {
|
||||
"description": "Review action to perform",
|
||||
"enum": ["APPROVE", "REQUEST_CHANGES", "COMMENT"],
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"description": "Repository owner",
|
||||
"type": "string"
|
||||
},
|
||||
"pullNumber": {
|
||||
"description": "Pull request number",
|
||||
"type": "number"
|
||||
}
|
||||
});
|
||||
let tools = vec![
|
||||
set_up_tool("tool1", "description1", params1),
|
||||
set_up_tool("tool2", "description2", params2),
|
||||
set_up_tool("tool3", "description3", params3),
|
||||
Tool::new("tool1", "description1", params1),
|
||||
Tool::new("tool2", "description2", params2),
|
||||
Tool::new("tool3", "description3", params3),
|
||||
];
|
||||
let result = format_tools(&tools);
|
||||
assert_eq!(result.len(), 3);
|
||||
@@ -681,14 +680,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_tools_to_google_spec_with_empty_properties() {
|
||||
let tools = vec![Tool {
|
||||
name: "tool1".to_string(),
|
||||
description: "description1".to_string(),
|
||||
input_schema: json!({
|
||||
"properties": {}
|
||||
}),
|
||||
annotations: None,
|
||||
}];
|
||||
use rmcp::model::object;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
let schema = json!({
|
||||
"properties": {}
|
||||
});
|
||||
|
||||
let tools = vec![Tool::new(
|
||||
Cow::Borrowed("tool1"),
|
||||
Cow::Borrowed("description1"),
|
||||
Arc::new(object(schema)),
|
||||
)];
|
||||
let result = format_tools(&tools);
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0]["name"], "tool1");
|
||||
|
||||
@@ -8,10 +8,8 @@ use crate::providers::utils::{
|
||||
use anyhow::{anyhow, Error};
|
||||
use async_stream::try_stream;
|
||||
use futures::Stream;
|
||||
use mcp_core::ToolError;
|
||||
use mcp_core::{Tool, ToolCall};
|
||||
use rmcp::model::Role;
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents};
|
||||
use mcp_core::{ToolCall, ToolError};
|
||||
use rmcp::model::{AnnotateAble, Content, RawContent, ResourceContents, Role, Tool};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use std::ops::Deref;
|
||||
@@ -635,6 +633,7 @@ pub fn create_request(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
use tokio::pin;
|
||||
use tokio_stream::{self, StreamExt};
|
||||
@@ -751,7 +750,7 @@ mod tests {
|
||||
let tool = Tool::new(
|
||||
"test_tool",
|
||||
"A test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -761,7 +760,6 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = format_tools(&[tool])?;
|
||||
@@ -843,7 +841,7 @@ mod tests {
|
||||
let tool1 = Tool::new(
|
||||
"test_tool",
|
||||
"Test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -853,13 +851,12 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let tool2 = Tool::new(
|
||||
"test_tool",
|
||||
"Test tool",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
@@ -869,7 +866,6 @@ mod tests {
|
||||
},
|
||||
"required": ["input"]
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let result = format_tools(&[tool1, tool2]);
|
||||
|
||||
@@ -3,8 +3,8 @@ use crate::model::ModelConfig;
|
||||
use crate::providers::base::Usage;
|
||||
use crate::providers::errors::ProviderError;
|
||||
use anyhow::{anyhow, Result};
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use rmcp::model::Role;
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::{Role, Tool};
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -359,6 +359,7 @@ pub fn create_request(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rmcp::object;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
@@ -460,7 +461,7 @@ mod tests {
|
||||
Tool::new(
|
||||
"calculator",
|
||||
"Calculate mathematical expressions",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"expression": {
|
||||
@@ -469,12 +470,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
Tool::new(
|
||||
"weather",
|
||||
"Get weather information",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
@@ -483,7 +483,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -557,7 +556,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
|
||||
let tools = vec![Tool::new(
|
||||
"get_stock_price",
|
||||
"Get stock price information",
|
||||
json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"symbol": {
|
||||
@@ -567,7 +566,6 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
|
||||
},
|
||||
"required": ["symbol"]
|
||||
}),
|
||||
None,
|
||||
)];
|
||||
|
||||
let request = create_request(&model_config, system, &messages, &tools)?;
|
||||
@@ -664,8 +662,7 @@ data: {"id":"a9537c2c-2017-4906-9817-2456168d89fa","model":"claude-3-5-sonnet","
|
||||
let tools = vec![Tool::new(
|
||||
"test_tool",
|
||||
"Test tool",
|
||||
json!({"type": "object", "properties": {}}),
|
||||
None,
|
||||
object!({"type": "object", "properties": {}}),
|
||||
)];
|
||||
|
||||
let request = create_request(&model_config, system, &messages, &tools)?;
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::providers::formats::gcpvertexai::{
|
||||
use crate::providers::formats::gcpvertexai::GcpLocation::Iowa;
|
||||
use crate::providers::gcpauth::GcpAuth;
|
||||
use crate::providers::utils::emit_debug_trace;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
/// Base URL for GCP Vertex AI documentation
|
||||
const GCP_VERTEX_AI_DOC_URL: &str = "https://cloud.google.com/vertex-ai";
|
||||
|
||||
@@ -11,8 +11,8 @@ use super::errors::ProviderError;
|
||||
use super::utils::emit_debug_trace;
|
||||
use crate::message::{Message, MessageContent};
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Role;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const GEMINI_CLI_DEFAULT_MODEL: &str = "gemini-2.5-pro";
|
||||
pub const GEMINI_CLI_KNOWN_MODELS: &[&str] = &["gemini-2.5-pro"];
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::config::{Config, ConfigError};
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::base::ConfigKey;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const GITHUB_COPILOT_DEFAULT_MODEL: &str = "gpt-4o";
|
||||
pub const GITHUB_COPILOT_KNOWN_MODELS: &[&str] = &[
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::providers::utils::{
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use axum::http::HeaderMap;
|
||||
use mcp_core::tool::Tool;
|
||||
use reqwest::Client;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::providers::formats::openai::{create_request, get_usage, response_to_m
|
||||
use crate::providers::utils::get_model;
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use mcp_core::Tool;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
@@ -8,7 +8,7 @@ use super::base::{LeadWorkerProviderTrait, Provider, ProviderMetadata, ProviderU
|
||||
use super::errors::ProviderError;
|
||||
use crate::message::{Message, MessageContent};
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{Content, RawContent};
|
||||
|
||||
/// A provider that switches between a lead model and a worker model based on turn count
|
||||
|
||||
@@ -12,7 +12,7 @@ use super::errors::ProviderError;
|
||||
use super::utils::{emit_debug_trace, get_model, handle_response_openai_compat, ImageFormat};
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const LITELLM_DEFAULT_MODEL: &str = "gpt-4o-mini";
|
||||
pub const LITELLM_DOC_URL: &str = "https://docs.litellm.ai/docs/";
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::model::ModelConfig;
|
||||
use crate::providers::formats::openai::{create_request, get_usage, response_to_message};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use mcp_core::tool::Tool;
|
||||
use reqwest::Client;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::model::ModelConfig;
|
||||
use crate::providers::base::MessageStream;
|
||||
use crate::providers::formats::openai::response_to_streaming_message;
|
||||
use crate::providers::utils::handle_status_openai_compat;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
pub const OPEN_AI_DEFAULT_MODEL: &str = "gpt-4o";
|
||||
pub const OPEN_AI_KNOWN_MODELS: &[&str] = &[
|
||||
|
||||
@@ -13,7 +13,7 @@ use super::utils::{
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::formats::openai::{create_request, get_usage, response_to_message};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use url::Url;
|
||||
|
||||
pub const OPENROUTER_DEFAULT_MODEL: &str = "anthropic/claude-3.5-sonnet";
|
||||
|
||||
@@ -6,7 +6,7 @@ use async_trait::async_trait;
|
||||
use aws_config;
|
||||
use aws_sdk_bedrockruntime::config::ProvideCredentials;
|
||||
use aws_sdk_sagemakerruntime::Client as SageMakerClient;
|
||||
use mcp_core::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::{json, Value};
|
||||
use tokio::time::sleep;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use super::utils::{get_model, ImageFormat};
|
||||
use crate::config::ConfigError;
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use url::Url;
|
||||
|
||||
pub const SNOWFLAKE_DEFAULT_MODEL: &str = "claude-3-7-sonnet";
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::base::{Provider, ProviderMetadata, ProviderUsage};
|
||||
use super::errors::ProviderError;
|
||||
use crate::message::Message;
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct TestInput {
|
||||
|
||||
@@ -37,9 +37,9 @@ use crate::message::{Message, MessageContent};
|
||||
use crate::model::ModelConfig;
|
||||
use crate::providers::formats::openai::create_request;
|
||||
use anyhow::Result;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use reqwest::Client;
|
||||
use rmcp::model::RawContent;
|
||||
use rmcp::model::{RawContent, Tool};
|
||||
use serde_json::{json, Value};
|
||||
use std::ops::Deref;
|
||||
use std::time::Duration;
|
||||
@@ -297,7 +297,7 @@ pub fn format_tool_info(tools: &[Tool]) -> String {
|
||||
let mut tool_info = String::new();
|
||||
for tool in tools {
|
||||
tool_info.push_str(&format!(
|
||||
"Tool Name: {}\nSchema: {}\nDescription: {}\n\n",
|
||||
"Tool Name: {}\nSchema: {}\nDescription: {:?}\n\n",
|
||||
tool.name,
|
||||
serde_json::to_string_pretty(&tool.input_schema).unwrap_or_default(),
|
||||
tool.description
|
||||
|
||||
@@ -10,8 +10,8 @@ use super::base::{ConfigKey, Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||
use super::errors::ProviderError;
|
||||
use crate::message::{Message, MessageContent};
|
||||
use crate::model::ModelConfig;
|
||||
use mcp_core::{tool::Tool, ToolCall, ToolResult};
|
||||
use rmcp::model::Role;
|
||||
use mcp_core::{ToolCall, ToolResult};
|
||||
use rmcp::model::{Role, Tool};
|
||||
|
||||
// ---------- Capability Flags ----------
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::providers::formats::openai::{create_request, get_usage, response_to_m
|
||||
use crate::providers::utils::get_model;
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use mcp_core::Tool;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use rmcp::model::Tool;
|
||||
use serde_json::Value;
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
@@ -1332,7 +1332,7 @@ mod tests {
|
||||
providers::base::{ProviderMetadata, ProviderUsage, Usage},
|
||||
providers::errors::ProviderError,
|
||||
};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{AnnotateAble, RawTextContent, Role};
|
||||
// Removed: use crate::session::storage::{get_most_recent_session, read_metadata};
|
||||
// `read_metadata` is still used by the test itself, so keep it or its module.
|
||||
|
||||
@@ -100,7 +100,6 @@ pub fn get_valid_sorted_sessions(sort_order: SortOrder) -> Result<Vec<SessionInf
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::session::SessionMetadata;
|
||||
use std::fs;
|
||||
use tempfile::tempdir;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ahash::AHasher;
|
||||
use dashmap::DashMap;
|
||||
use mcp_core::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
use tiktoken_rs::CoreBPE;
|
||||
@@ -79,7 +79,12 @@ impl AsyncTokenCounter {
|
||||
for tool in tools {
|
||||
func_token_count += func_init;
|
||||
let name = &tool.name;
|
||||
let description = &tool.description.trim_end_matches('.');
|
||||
let description = &tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default()
|
||||
.trim_end_matches('.');
|
||||
|
||||
// Note: the separator (:) is likely tokenized with adjacent tokens, so we use original approach for accuracy
|
||||
let line = format!("{}:{}", name, description);
|
||||
@@ -225,7 +230,12 @@ impl TokenCounter {
|
||||
for tool in tools {
|
||||
func_token_count += func_init; // Add tokens for start of each function
|
||||
let name = &tool.name;
|
||||
let description = &tool.description.trim_end_matches('.');
|
||||
let description = &tool
|
||||
.description
|
||||
.as_ref()
|
||||
.map(|d| d.as_ref())
|
||||
.unwrap_or_default()
|
||||
.trim_end_matches('.');
|
||||
let line = format!("{}:{}", name, description);
|
||||
func_token_count += self.count_tokens(&line); // Add tokens for name and description
|
||||
|
||||
@@ -371,9 +381,8 @@ pub async fn create_async_token_counter() -> Result<AsyncTokenCounter, String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::message::{Message, MessageContent};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Role;
|
||||
use serde_json::json;
|
||||
use rmcp::model::{Role, Tool};
|
||||
use rmcp::object;
|
||||
|
||||
#[test]
|
||||
fn test_token_counter_basic() {
|
||||
@@ -428,10 +437,10 @@ mod tests {
|
||||
),
|
||||
];
|
||||
|
||||
let tools = vec![Tool {
|
||||
name: "get_current_weather".to_string(),
|
||||
description: "Get the current weather in a given location".to_string(),
|
||||
input_schema: json!({
|
||||
let tools = vec![Tool::new(
|
||||
"get_current_weather",
|
||||
"Get the current weather in a given location",
|
||||
object!({
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
@@ -445,8 +454,7 @@ mod tests {
|
||||
},
|
||||
"required": ["location"]
|
||||
}),
|
||||
annotations: None,
|
||||
}];
|
||||
)];
|
||||
|
||||
let token_count_without_tools = counter.count_chat_tokens(system_prompt, &messages, &[]);
|
||||
println!("Total tokens without tools: {}", token_count_without_tools);
|
||||
@@ -526,10 +534,10 @@ mod tests {
|
||||
),
|
||||
];
|
||||
|
||||
let tools = vec![Tool {
|
||||
name: "get_current_weather".to_string(),
|
||||
description: "Get the current weather in a given location".to_string(),
|
||||
input_schema: json!({
|
||||
let tools = vec![Tool::new(
|
||||
"get_current_weather",
|
||||
"Get the current weather in a given location",
|
||||
object!({
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
@@ -543,8 +551,7 @@ mod tests {
|
||||
},
|
||||
"required": ["location"]
|
||||
}),
|
||||
annotations: None,
|
||||
}];
|
||||
)];
|
||||
|
||||
let token_count_without_tools = counter.count_chat_tokens(system_prompt, &messages, &[]);
|
||||
println!(
|
||||
|
||||
@@ -448,6 +448,8 @@ mod schedule_tool_tests {
|
||||
let tool = schedule_tool.unwrap();
|
||||
assert!(tool
|
||||
.description
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.contains("Manage scheduled recipe execution"));
|
||||
}
|
||||
|
||||
@@ -478,6 +480,8 @@ mod schedule_tool_tests {
|
||||
let tool = schedule_tool.unwrap();
|
||||
assert!(tool
|
||||
.description
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.contains("Manage scheduled recipe execution"));
|
||||
|
||||
// Verify the tool has the expected actions in its schema
|
||||
@@ -548,7 +552,7 @@ mod final_output_tool_tests {
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::base::{Provider, ProviderUsage, Usage};
|
||||
use goose::providers::errors::ProviderError;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MockProvider {
|
||||
@@ -648,7 +652,7 @@ mod final_output_tool_tests {
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::base::{Provider, ProviderUsage};
|
||||
use goose::providers::errors::ProviderError;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MockProvider {
|
||||
@@ -769,7 +773,7 @@ mod retry_tests {
|
||||
use goose::model::ModelConfig;
|
||||
use goose::providers::base::{Provider, ProviderUsage, Usage};
|
||||
use goose::providers::errors::ProviderError;
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -950,7 +954,8 @@ mod max_turns_tests {
|
||||
use goose::providers::base::{Provider, ProviderMetadata, ProviderUsage, Usage};
|
||||
use goose::providers::errors::ProviderError;
|
||||
use goose::session::storage::Identifier;
|
||||
use mcp_core::tool::{Tool, ToolCall};
|
||||
use mcp_core::tool::ToolCall;
|
||||
use rmcp::model::Tool;
|
||||
use std::path::PathBuf;
|
||||
|
||||
struct MockToolProvider {}
|
||||
|
||||
@@ -7,8 +7,9 @@ use goose::providers::{
|
||||
anthropic, azure, bedrock, databricks, google, groq, litellm, ollama, openai, openrouter,
|
||||
snowflake, xai,
|
||||
};
|
||||
use mcp_core::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{AnnotateAble, Content, RawImageContent};
|
||||
use rmcp::object;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
@@ -118,7 +119,7 @@ impl ProviderTester {
|
||||
let weather_tool = Tool::new(
|
||||
"get_weather",
|
||||
"Get the weather for a location",
|
||||
serde_json::json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"required": ["location"],
|
||||
"properties": {
|
||||
@@ -128,7 +129,6 @@ impl ProviderTester {
|
||||
}
|
||||
}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let message = Message::user().with_text("What's the weather like in San Francisco?");
|
||||
@@ -309,11 +309,10 @@ impl ProviderTester {
|
||||
let screenshot_tool = Tool::new(
|
||||
"get_screenshot",
|
||||
"Get a screenshot of the current screen",
|
||||
serde_json::json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let user_message = Message::user().with_text("Take a screenshot please");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// The protocol messages exchanged between client and server
|
||||
use crate::tool::Tool;
|
||||
use rmcp::model::Tool;
|
||||
use rmcp::model::{Content, ErrorData, Prompt, PromptMessage, Resource, ResourceContents};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use anyhow::Result;
|
||||
use mcp_core::handler::{PromptError, ResourceError};
|
||||
use mcp_core::tool::ToolAnnotations;
|
||||
use mcp_core::{handler::ToolError, protocol::ServerCapabilities, tool::Tool};
|
||||
use mcp_core::{handler::ToolError, protocol::ServerCapabilities};
|
||||
use mcp_server::router::{CapabilitiesBuilder, RouterService};
|
||||
use mcp_server::{ByteTransport, Router, Server};
|
||||
use rmcp::model::{Content, JsonRpcMessage, Prompt, PromptArgument, RawResource, Resource};
|
||||
use rmcp::model::{
|
||||
Content, JsonRpcMessage, Prompt, PromptArgument, RawResource, Resource, Tool, ToolAnnotations,
|
||||
};
|
||||
use rmcp::object;
|
||||
use serde_json::Value;
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
use tokio::sync::mpsc;
|
||||
@@ -72,51 +74,51 @@ impl Router for CounterRouter {
|
||||
Tool::new(
|
||||
"increment".to_string(),
|
||||
"Increment the counter by 1".to_string(),
|
||||
serde_json::json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Increment Tool".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Increment Tool".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
}),
|
||||
Tool::new(
|
||||
"decrement".to_string(),
|
||||
"Decrement the counter by 1".to_string(),
|
||||
serde_json::json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}),
|
||||
Some(ToolAnnotations {
|
||||
title: Some("Decrement Tool".to_string()),
|
||||
read_only_hint: false,
|
||||
destructive_hint: false,
|
||||
idempotent_hint: false,
|
||||
open_world_hint: false,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Decrement Tool".to_string()),
|
||||
read_only_hint: Some(false),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
}),
|
||||
Tool::new(
|
||||
"get_value".to_string(),
|
||||
"Get the current counter value".to_string(),
|
||||
serde_json::json!({
|
||||
object!({
|
||||
"type": "object",
|
||||
"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,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotate(ToolAnnotations {
|
||||
title: Some("Get Value Tool".to_string()),
|
||||
read_only_hint: Some(true),
|
||||
destructive_hint: Some(false),
|
||||
idempotent_hint: Some(false),
|
||||
open_world_hint: Some(false),
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ pub trait Router: Send + Sync + 'static {
|
||||
// in the protocol, instructions are optional but we make it required
|
||||
fn instructions(&self) -> String;
|
||||
fn capabilities(&self) -> ServerCapabilities;
|
||||
fn list_tools(&self) -> Vec<mcp_core::tool::Tool>;
|
||||
fn list_tools(&self) -> Vec<rmcp::model::Tool>;
|
||||
fn call_tool(
|
||||
&self,
|
||||
tool_name: &str,
|
||||
|
||||
Reference in New Issue
Block a user