rmcp upgrade (#4792)
This commit is contained in:
@@ -458,11 +458,13 @@ mod tests {
|
||||
name: "required_arg".to_string(),
|
||||
description: Some("A required argument".to_string()),
|
||||
required: Some(true),
|
||||
title: None,
|
||||
},
|
||||
PromptArgument {
|
||||
name: "optional_arg".to_string(),
|
||||
description: Some("An optional argument".to_string()),
|
||||
required: Some(false),
|
||||
title: None,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
goose = { path = "../goose" }
|
||||
rmcp = { version = "0.6.0", features = ["server", "client", "transport-io", "macros"] }
|
||||
rmcp = { version = "0.7.0", features = ["server", "client", "transport-io", "macros"] }
|
||||
anyhow = "1.0.94"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["io-util"] }
|
||||
|
||||
@@ -409,6 +409,9 @@ impl ServerHandler for AutoVisualiserRouter {
|
||||
server_info: Implementation {
|
||||
name: "goose-autovisualiser".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
title: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
instructions: Some(self.instructions.clone()),
|
||||
@@ -458,7 +461,7 @@ impl AutoVisualiserRouter {
|
||||
/// show a Sankey diagram from flow data
|
||||
#[tool(
|
||||
name = "render_sankey",
|
||||
description = r#"show a Sankey diagram from flow data
|
||||
description = r#"show a Sankey diagram from flow data
|
||||
The data must contain:
|
||||
- nodes: Array of objects with 'name' and optional 'category' properties
|
||||
- links: Array of objects with 'source', 'target', and 'value' properties
|
||||
@@ -537,7 +540,7 @@ Example:
|
||||
/// show a radar chart (spider chart) for multi-dimensional data comparison
|
||||
#[tool(
|
||||
name = "render_radar",
|
||||
description = r#"show a radar chart (spider chart) for multi-dimensional data comparison
|
||||
description = r#"show a radar chart (spider chart) for multi-dimensional data comparison
|
||||
|
||||
The data must contain:
|
||||
- labels: Array of strings representing the dimensions/axes
|
||||
@@ -552,7 +555,7 @@ Example:
|
||||
"data": [85, 70, 90, 75, 80]
|
||||
},
|
||||
{
|
||||
"label": "Player 2",
|
||||
"label": "Player 2",
|
||||
"data": [75, 85, 80, 90, 70]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,9 +4,9 @@ use reqwest::{Client, Url};
|
||||
use rmcp::{
|
||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||
model::{
|
||||
CallToolResult, Content, ErrorCode, ErrorData, Implementation, ListResourcesResult,
|
||||
PaginatedRequestParam, RawResource, ReadResourceRequestParam, ReadResourceResult, Resource,
|
||||
ResourceContents, ServerCapabilities, ServerInfo,
|
||||
AnnotateAble, CallToolResult, Content, ErrorCode, ErrorData, Implementation,
|
||||
ListResourcesResult, PaginatedRequestParam, RawResource, ReadResourceRequestParam,
|
||||
ReadResourceResult, Resource, ResourceContents, ServerCapabilities, ServerInfo,
|
||||
},
|
||||
schemars::JsonSchema,
|
||||
service::RequestContext,
|
||||
@@ -1293,6 +1293,9 @@ impl ServerHandler for ComputerControllerServer {
|
||||
server_info: Implementation {
|
||||
name: "goose-computercontroller".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
title: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder()
|
||||
.enable_tools()
|
||||
@@ -1311,15 +1314,12 @@ impl ServerHandler for ComputerControllerServer {
|
||||
let active_resources = self.active_resources.lock().unwrap();
|
||||
let resources: Vec<Resource> = active_resources
|
||||
.keys()
|
||||
.map(|uri| Resource {
|
||||
raw: RawResource {
|
||||
name: uri.split('/').next_back().unwrap_or("").to_string(),
|
||||
uri: uri.clone(),
|
||||
description: None,
|
||||
mime_type: None,
|
||||
size: None,
|
||||
},
|
||||
annotations: None,
|
||||
.map(|uri| {
|
||||
RawResource::new(
|
||||
uri.clone(),
|
||||
uri.split('/').next_back().unwrap_or("").to_string(),
|
||||
)
|
||||
.no_annotation()
|
||||
})
|
||||
.collect();
|
||||
Ok(ListResourcesResult {
|
||||
|
||||
@@ -150,6 +150,7 @@ fn load_prompt_files() -> HashMap<String, Prompt> {
|
||||
name: arg.name,
|
||||
description: arg.description,
|
||||
required: arg.required,
|
||||
title: None,
|
||||
})
|
||||
.collect::<Vec<PromptArgument>>();
|
||||
|
||||
@@ -383,6 +384,9 @@ impl ServerHandler for DeveloperServer {
|
||||
server_info: Implementation {
|
||||
name: "goose-developer".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
title: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder()
|
||||
.enable_tools()
|
||||
|
||||
@@ -521,6 +521,9 @@ impl ServerHandler for MemoryServer {
|
||||
server_info: Implementation {
|
||||
name: "goose-memory".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
title: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
instructions: Some(self.instructions.clone()),
|
||||
|
||||
@@ -113,6 +113,9 @@ impl ServerHandler for TutorialServer {
|
||||
server_info: Implementation {
|
||||
name: "goose-tutorial".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
title: None,
|
||||
icons: None,
|
||||
website_url: None,
|
||||
},
|
||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||
instructions: Some(self.instructions.clone()),
|
||||
|
||||
@@ -9,9 +9,9 @@ use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
|
||||
|
||||
use goose::session::{Session, SessionInsights};
|
||||
use rmcp::model::{
|
||||
Annotations, Content, EmbeddedResource, ImageContent, JsonObject, RawEmbeddedResource,
|
||||
RawImageContent, RawResource, RawTextContent, ResourceContents, Role, TextContent, Tool,
|
||||
ToolAnnotations,
|
||||
Annotations, Content, EmbeddedResource, Icon, ImageContent, JsonObject, RawAudioContent,
|
||||
RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ResourceContents, Role,
|
||||
TextContent, Tool, ToolAnnotations,
|
||||
};
|
||||
use utoipa::{OpenApi, ToSchema};
|
||||
|
||||
@@ -307,6 +307,7 @@ derive_utoipa!(ImageContent as ImageContentSchema);
|
||||
derive_utoipa!(TextContent as TextContentSchema);
|
||||
derive_utoipa!(RawTextContent as RawTextContentSchema);
|
||||
derive_utoipa!(RawImageContent as RawImageContentSchema);
|
||||
derive_utoipa!(RawAudioContent as RawAudioContentSchema);
|
||||
derive_utoipa!(RawEmbeddedResource as RawEmbeddedResourceSchema);
|
||||
derive_utoipa!(RawResource as RawResourceSchema);
|
||||
derive_utoipa!(Tool as ToolSchema);
|
||||
@@ -314,30 +315,7 @@ derive_utoipa!(ToolAnnotations as ToolAnnotationsSchema);
|
||||
derive_utoipa!(Annotations as AnnotationsSchema);
|
||||
derive_utoipa!(ResourceContents as ResourceContentsSchema);
|
||||
derive_utoipa!(JsonObject as JsonObjectSchema);
|
||||
|
||||
// Create a manual schema for the generic Annotated type
|
||||
// We manually define this to avoid circular references from RawContent::Audio(AudioContent)
|
||||
// where AudioContent = Annotated<RawAudioContent>
|
||||
struct AnnotatedSchema {}
|
||||
|
||||
impl<'__s> ToSchema<'__s> for AnnotatedSchema {
|
||||
fn schema() -> (&'__s str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
|
||||
let schema = Schema::OneOf(
|
||||
OneOfBuilder::new()
|
||||
.item(RefOr::Ref(Ref::new("#/components/schemas/RawTextContent")))
|
||||
.item(RefOr::Ref(Ref::new("#/components/schemas/RawImageContent")))
|
||||
.item(RefOr::Ref(Ref::new(
|
||||
"#/components/schemas/RawEmbeddedResource",
|
||||
)))
|
||||
.build(),
|
||||
);
|
||||
("Annotated", RefOr::T(schema))
|
||||
}
|
||||
|
||||
fn aliases() -> Vec<(&'__s str, utoipa::openapi::schema::Schema)> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
derive_utoipa!(Icon as IconSchema);
|
||||
|
||||
#[derive(OpenApi)]
|
||||
#[openapi(
|
||||
@@ -419,9 +397,9 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
|
||||
TextContentSchema,
|
||||
RawTextContentSchema,
|
||||
RawImageContentSchema,
|
||||
RawAudioContentSchema,
|
||||
RawEmbeddedResourceSchema,
|
||||
RawResourceSchema,
|
||||
AnnotatedSchema,
|
||||
ToolResponse,
|
||||
ToolRequest,
|
||||
ToolConfirmationRequest,
|
||||
@@ -447,6 +425,7 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
|
||||
Session,
|
||||
SessionInsights,
|
||||
Conversation,
|
||||
IconSchema,
|
||||
goose::session::extension_data::ExtensionData,
|
||||
super::routes::schedule::CreateScheduleRequest,
|
||||
super::routes::schedule::UpdateScheduleRequest,
|
||||
|
||||
@@ -562,6 +562,8 @@ impl ExtensionManager {
|
||||
input_schema: tool.input_schema,
|
||||
annotations: tool.annotations,
|
||||
output_schema: tool.output_schema,
|
||||
icons: None,
|
||||
title: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1134,27 +1136,21 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
Ok(ListToolsResult {
|
||||
tools: vec![
|
||||
Tool {
|
||||
name: "tool".into(),
|
||||
description: Some("A basic tool".into()),
|
||||
input_schema: Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
annotations: None,
|
||||
output_schema: None,
|
||||
},
|
||||
Tool {
|
||||
name: "available_tool".into(),
|
||||
description: Some("An available tool".into()),
|
||||
input_schema: Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
annotations: None,
|
||||
output_schema: None,
|
||||
},
|
||||
Tool {
|
||||
name: "hidden_tool".into(),
|
||||
description: Some("A hidden tool".into()),
|
||||
input_schema: Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
annotations: None,
|
||||
output_schema: None,
|
||||
},
|
||||
Tool::new(
|
||||
"tool".to_string(),
|
||||
"A basic tool".to_string(),
|
||||
Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
),
|
||||
Tool::new(
|
||||
"available_tool".to_string(),
|
||||
"An available tool".to_string(),
|
||||
Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
),
|
||||
Tool::new(
|
||||
"hidden_tool".to_string(),
|
||||
"hidden tool".to_string(),
|
||||
Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
),
|
||||
],
|
||||
next_cursor: None,
|
||||
})
|
||||
|
||||
@@ -134,6 +134,9 @@ impl ClientHandler for GooseClient {
|
||||
client_info: Implementation {
|
||||
name: "goose".to_string(),
|
||||
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
icons: None,
|
||||
title: None,
|
||||
website_url: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,13 @@ const CALLBACK_TEMPLATE: &str = include_str!("oauth_callback.html");
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
code_receiver: Arc<Mutex<Option<oneshot::Sender<String>>>>,
|
||||
code_receiver: Arc<Mutex<Option<oneshot::Sender<CallbackParams>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct CallbackParams {
|
||||
code: String,
|
||||
#[allow(dead_code)]
|
||||
state: Option<String>,
|
||||
state: String,
|
||||
}
|
||||
|
||||
pub async fn oauth_flow(
|
||||
@@ -45,7 +44,7 @@ pub async fn oauth_flow(
|
||||
}
|
||||
}
|
||||
|
||||
let (code_sender, code_receiver) = oneshot::channel::<String>();
|
||||
let (code_sender, code_receiver) = oneshot::channel::<CallbackParams>();
|
||||
let app_state = AppState {
|
||||
code_receiver: Arc::new(Mutex::new(Some(code_sender))),
|
||||
};
|
||||
@@ -55,7 +54,7 @@ pub async fn oauth_flow(
|
||||
let rendered = rendered.clone();
|
||||
async move {
|
||||
if let Some(sender) = state.code_receiver.lock().await.take() {
|
||||
let _ = sender.send(params.code);
|
||||
let _ = sender.send(params);
|
||||
}
|
||||
Html(rendered)
|
||||
}
|
||||
@@ -86,8 +85,11 @@ pub async fn oauth_flow(
|
||||
eprintln!(" {}", authorization_url);
|
||||
}
|
||||
|
||||
let auth_code = code_receiver.await?;
|
||||
oauth_state.handle_callback(&auth_code).await?;
|
||||
let CallbackParams {
|
||||
code: auth_code,
|
||||
state: csrf_token,
|
||||
} = code_receiver.await?;
|
||||
oauth_state.handle_callback(&auth_code, &csrf_token).await?;
|
||||
|
||||
if let Err(e) = save_credentials(name, &oauth_state).await {
|
||||
warn!("Failed to save credentials: {}", e);
|
||||
|
||||
Reference in New Issue
Block a user