rmcp upgrade (#4792)

This commit is contained in:
Jack Amadeo
2025-09-30 21:03:50 -04:00
committed by GitHub
parent dee7f79f32
commit a77d13aa88
15 changed files with 139 additions and 94 deletions
Generated
+4 -4
View File
@@ -5442,9 +5442,9 @@ dependencies = [
[[package]] [[package]]
name = "rmcp" name = "rmcp"
version = "0.6.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "817ef98583b16628962dd8214ee89e9a9df6a79de9b487eb29ceddb1f610a7f6" checksum = "534fd1cd0601e798ac30545ff2b7f4a62c6f14edd4aaed1cc5eb1e85f69f09af"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"chrono", "chrono",
@@ -5470,9 +5470,9 @@ dependencies = [
[[package]] [[package]]
name = "rmcp-macros" name = "rmcp-macros"
version = "0.6.2" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9712e19c12a2812bffa85ab71714e2a342a9d7e79656e6f74f650475b8094090" checksum = "9ba777eb0e5f53a757e36f0e287441da0ab766564ba7201600eeb92a4753022e"
dependencies = [ dependencies = [
"darling 0.21.0", "darling 0.21.0",
"proc-macro2", "proc-macro2",
+1 -1
View File
@@ -14,7 +14,7 @@ description = "An AI agent"
uninlined_format_args = "allow" uninlined_format_args = "allow"
[workspace.dependencies] [workspace.dependencies]
rmcp = { version = "0.6.2", features = ["schemars", "auth"] } rmcp = { version = "0.7.0", features = ["schemars", "auth"] }
# Patch for Windows cross-compilation issue with crunchy # Patch for Windows cross-compilation issue with crunchy
[patch.crates-io] [patch.crates-io]
@@ -458,11 +458,13 @@ mod tests {
name: "required_arg".to_string(), name: "required_arg".to_string(),
description: Some("A required argument".to_string()), description: Some("A required argument".to_string()),
required: Some(true), required: Some(true),
title: None,
}, },
PromptArgument { PromptArgument {
name: "optional_arg".to_string(), name: "optional_arg".to_string(),
description: Some("An optional argument".to_string()), description: Some("An optional argument".to_string()),
required: Some(false), required: Some(false),
title: None,
}, },
]; ];
+1 -1
View File
@@ -12,7 +12,7 @@ workspace = true
[dependencies] [dependencies]
goose = { path = "../goose" } 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" anyhow = "1.0.94"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
tokio-stream = { version = "0.1", features = ["io-util"] } tokio-stream = { version = "0.1", features = ["io-util"] }
@@ -409,6 +409,9 @@ impl ServerHandler for AutoVisualiserRouter {
server_info: Implementation { server_info: Implementation {
name: "goose-autovisualiser".to_string(), name: "goose-autovisualiser".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
title: None,
icons: None,
website_url: None,
}, },
capabilities: ServerCapabilities::builder().enable_tools().build(), capabilities: ServerCapabilities::builder().enable_tools().build(),
instructions: Some(self.instructions.clone()), instructions: Some(self.instructions.clone()),
+12 -12
View File
@@ -4,9 +4,9 @@ use reqwest::{Client, Url};
use rmcp::{ use rmcp::{
handler::server::{router::tool::ToolRouter, wrapper::Parameters}, handler::server::{router::tool::ToolRouter, wrapper::Parameters},
model::{ model::{
CallToolResult, Content, ErrorCode, ErrorData, Implementation, ListResourcesResult, AnnotateAble, CallToolResult, Content, ErrorCode, ErrorData, Implementation,
PaginatedRequestParam, RawResource, ReadResourceRequestParam, ReadResourceResult, Resource, ListResourcesResult, PaginatedRequestParam, RawResource, ReadResourceRequestParam,
ResourceContents, ServerCapabilities, ServerInfo, ReadResourceResult, Resource, ResourceContents, ServerCapabilities, ServerInfo,
}, },
schemars::JsonSchema, schemars::JsonSchema,
service::RequestContext, service::RequestContext,
@@ -1293,6 +1293,9 @@ impl ServerHandler for ComputerControllerServer {
server_info: Implementation { server_info: Implementation {
name: "goose-computercontroller".to_string(), name: "goose-computercontroller".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
title: None,
icons: None,
website_url: None,
}, },
capabilities: ServerCapabilities::builder() capabilities: ServerCapabilities::builder()
.enable_tools() .enable_tools()
@@ -1311,15 +1314,12 @@ impl ServerHandler for ComputerControllerServer {
let active_resources = self.active_resources.lock().unwrap(); let active_resources = self.active_resources.lock().unwrap();
let resources: Vec<Resource> = active_resources let resources: Vec<Resource> = active_resources
.keys() .keys()
.map(|uri| Resource { .map(|uri| {
raw: RawResource { RawResource::new(
name: uri.split('/').next_back().unwrap_or("").to_string(), uri.clone(),
uri: uri.clone(), uri.split('/').next_back().unwrap_or("").to_string(),
description: None, )
mime_type: None, .no_annotation()
size: None,
},
annotations: None,
}) })
.collect(); .collect();
Ok(ListResourcesResult { Ok(ListResourcesResult {
@@ -150,6 +150,7 @@ fn load_prompt_files() -> HashMap<String, Prompt> {
name: arg.name, name: arg.name,
description: arg.description, description: arg.description,
required: arg.required, required: arg.required,
title: None,
}) })
.collect::<Vec<PromptArgument>>(); .collect::<Vec<PromptArgument>>();
@@ -383,6 +384,9 @@ impl ServerHandler for DeveloperServer {
server_info: Implementation { server_info: Implementation {
name: "goose-developer".to_string(), name: "goose-developer".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
title: None,
icons: None,
website_url: None,
}, },
capabilities: ServerCapabilities::builder() capabilities: ServerCapabilities::builder()
.enable_tools() .enable_tools()
+3
View File
@@ -521,6 +521,9 @@ impl ServerHandler for MemoryServer {
server_info: Implementation { server_info: Implementation {
name: "goose-memory".to_string(), name: "goose-memory".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
title: None,
icons: None,
website_url: None,
}, },
capabilities: ServerCapabilities::builder().enable_tools().build(), capabilities: ServerCapabilities::builder().enable_tools().build(),
instructions: Some(self.instructions.clone()), instructions: Some(self.instructions.clone()),
+3
View File
@@ -113,6 +113,9 @@ impl ServerHandler for TutorialServer {
server_info: Implementation { server_info: Implementation {
name: "goose-tutorial".to_string(), name: "goose-tutorial".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
title: None,
icons: None,
website_url: None,
}, },
capabilities: ServerCapabilities::builder().enable_tools().build(), capabilities: ServerCapabilities::builder().enable_tools().build(),
instructions: Some(self.instructions.clone()), instructions: Some(self.instructions.clone()),
+7 -28
View File
@@ -9,9 +9,9 @@ use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
use goose::session::{Session, SessionInsights}; use goose::session::{Session, SessionInsights};
use rmcp::model::{ use rmcp::model::{
Annotations, Content, EmbeddedResource, ImageContent, JsonObject, RawEmbeddedResource, Annotations, Content, EmbeddedResource, Icon, ImageContent, JsonObject, RawAudioContent,
RawImageContent, RawResource, RawTextContent, ResourceContents, Role, TextContent, Tool, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ResourceContents, Role,
ToolAnnotations, TextContent, Tool, ToolAnnotations,
}; };
use utoipa::{OpenApi, ToSchema}; use utoipa::{OpenApi, ToSchema};
@@ -307,6 +307,7 @@ derive_utoipa!(ImageContent as ImageContentSchema);
derive_utoipa!(TextContent as TextContentSchema); derive_utoipa!(TextContent as TextContentSchema);
derive_utoipa!(RawTextContent as RawTextContentSchema); derive_utoipa!(RawTextContent as RawTextContentSchema);
derive_utoipa!(RawImageContent as RawImageContentSchema); derive_utoipa!(RawImageContent as RawImageContentSchema);
derive_utoipa!(RawAudioContent as RawAudioContentSchema);
derive_utoipa!(RawEmbeddedResource as RawEmbeddedResourceSchema); derive_utoipa!(RawEmbeddedResource as RawEmbeddedResourceSchema);
derive_utoipa!(RawResource as RawResourceSchema); derive_utoipa!(RawResource as RawResourceSchema);
derive_utoipa!(Tool as ToolSchema); derive_utoipa!(Tool as ToolSchema);
@@ -314,30 +315,7 @@ derive_utoipa!(ToolAnnotations as ToolAnnotationsSchema);
derive_utoipa!(Annotations as AnnotationsSchema); derive_utoipa!(Annotations as AnnotationsSchema);
derive_utoipa!(ResourceContents as ResourceContentsSchema); derive_utoipa!(ResourceContents as ResourceContentsSchema);
derive_utoipa!(JsonObject as JsonObjectSchema); derive_utoipa!(JsonObject as JsonObjectSchema);
derive_utoipa!(Icon as IconSchema);
// 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(OpenApi)] #[derive(OpenApi)]
#[openapi( #[openapi(
@@ -419,9 +397,9 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
TextContentSchema, TextContentSchema,
RawTextContentSchema, RawTextContentSchema,
RawImageContentSchema, RawImageContentSchema,
RawAudioContentSchema,
RawEmbeddedResourceSchema, RawEmbeddedResourceSchema,
RawResourceSchema, RawResourceSchema,
AnnotatedSchema,
ToolResponse, ToolResponse,
ToolRequest, ToolRequest,
ToolConfirmationRequest, ToolConfirmationRequest,
@@ -447,6 +425,7 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
Session, Session,
SessionInsights, SessionInsights,
Conversation, Conversation,
IconSchema,
goose::session::extension_data::ExtensionData, goose::session::extension_data::ExtensionData,
super::routes::schedule::CreateScheduleRequest, super::routes::schedule::CreateScheduleRequest,
super::routes::schedule::UpdateScheduleRequest, super::routes::schedule::UpdateScheduleRequest,
+17 -21
View File
@@ -562,6 +562,8 @@ impl ExtensionManager {
input_schema: tool.input_schema, input_schema: tool.input_schema,
annotations: tool.annotations, annotations: tool.annotations,
output_schema: tool.output_schema, output_schema: tool.output_schema,
icons: None,
title: None,
}); });
} }
} }
@@ -1134,27 +1136,21 @@ mod tests {
use std::sync::Arc; use std::sync::Arc;
Ok(ListToolsResult { Ok(ListToolsResult {
tools: vec![ tools: vec![
Tool { Tool::new(
name: "tool".into(), "tool".to_string(),
description: Some("A basic tool".into()), "A basic tool".to_string(),
input_schema: Arc::new(json!({}).as_object().unwrap().clone()), Arc::new(json!({}).as_object().unwrap().clone()),
annotations: None, ),
output_schema: None, Tool::new(
}, "available_tool".to_string(),
Tool { "An available tool".to_string(),
name: "available_tool".into(), Arc::new(json!({}).as_object().unwrap().clone()),
description: Some("An available tool".into()), ),
input_schema: Arc::new(json!({}).as_object().unwrap().clone()), Tool::new(
annotations: None, "hidden_tool".to_string(),
output_schema: None, "hidden tool".to_string(),
}, Arc::new(json!({}).as_object().unwrap().clone()),
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,
},
], ],
next_cursor: None, next_cursor: None,
}) })
+3
View File
@@ -134,6 +134,9 @@ impl ClientHandler for GooseClient {
client_info: Implementation { client_info: Implementation {
name: "goose".to_string(), name: "goose".to_string(),
version: env!("CARGO_PKG_VERSION").to_owned(), version: env!("CARGO_PKG_VERSION").to_owned(),
icons: None,
title: None,
website_url: None,
}, },
} }
} }
+9 -7
View File
@@ -19,14 +19,13 @@ const CALLBACK_TEMPLATE: &str = include_str!("oauth_callback.html");
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
code_receiver: Arc<Mutex<Option<oneshot::Sender<String>>>>, code_receiver: Arc<Mutex<Option<oneshot::Sender<CallbackParams>>>>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct CallbackParams { struct CallbackParams {
code: String, code: String,
#[allow(dead_code)] state: String,
state: Option<String>,
} }
pub async fn oauth_flow( 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 { let app_state = AppState {
code_receiver: Arc::new(Mutex::new(Some(code_sender))), code_receiver: Arc::new(Mutex::new(Some(code_sender))),
}; };
@@ -55,7 +54,7 @@ pub async fn oauth_flow(
let rendered = rendered.clone(); let rendered = rendered.clone();
async move { async move {
if let Some(sender) = state.code_receiver.lock().await.take() { if let Some(sender) = state.code_receiver.lock().await.take() {
let _ = sender.send(params.code); let _ = sender.send(params);
} }
Html(rendered) Html(rendered)
} }
@@ -86,8 +85,11 @@ pub async fn oauth_flow(
eprintln!(" {}", authorization_url); eprintln!(" {}", authorization_url);
} }
let auth_code = code_receiver.await?; let CallbackParams {
oauth_state.handle_callback(&auth_code).await?; 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 { if let Err(e) = save_credentials(name, &oauth_state).await {
warn!("Failed to save credentials: {}", e); warn!("Failed to save credentials: {}", e);
+51 -14
View File
@@ -1715,19 +1715,6 @@
} }
} }
}, },
"Annotated": {
"oneOf": [
{
"$ref": "#/components/schemas/RawTextContent"
},
{
"$ref": "#/components/schemas/RawImageContent"
},
{
"$ref": "#/components/schemas/RawEmbeddedResource"
}
]
},
"Annotations": { "Annotations": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1858,7 +1845,7 @@
{ {
"allOf": [ "allOf": [
{ {
"$ref": "#/components/schemas/Annotated" "$ref": "#/components/schemas/RawAudioContent"
} }
] ]
}, },
@@ -2587,6 +2574,23 @@
} }
} }
}, },
"Icon": {
"type": "object",
"required": [
"src"
],
"properties": {
"mimeType": {
"type": "string"
},
"sizes": {
"type": "string"
},
"src": {
"type": "string"
}
}
},
"ImageContent": { "ImageContent": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -3100,6 +3104,21 @@
} }
} }
}, },
"RawAudioContent": {
"type": "object",
"required": [
"data",
"mimeType"
],
"properties": {
"data": {
"type": "string"
},
"mimeType": {
"type": "string"
}
}
},
"RawEmbeddedResource": { "RawEmbeddedResource": {
"type": "object", "type": "object",
"required": [ "required": [
@@ -3144,6 +3163,12 @@
"description": { "description": {
"type": "string" "type": "string"
}, },
"icons": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Icon"
}
},
"mimeType": { "mimeType": {
"type": "string" "type": "string"
}, },
@@ -3154,6 +3179,9 @@
"type": "integer", "type": "integer",
"minimum": 0 "minimum": 0
}, },
"title": {
"type": "string"
},
"uri": { "uri": {
"type": "string" "type": "string"
} }
@@ -3945,6 +3973,12 @@
"description": { "description": {
"type": "string" "type": "string"
}, },
"icons": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Icon"
}
},
"inputSchema": { "inputSchema": {
"type": "object", "type": "object",
"additionalProperties": true "additionalProperties": true
@@ -3955,6 +3989,9 @@
"outputSchema": { "outputSchema": {
"type": "object", "type": "object",
"additionalProperties": true "additionalProperties": true
},
"title": {
"type": "string"
} }
} }
}, },
+16 -3
View File
@@ -9,8 +9,6 @@ export type AddSubRecipesResponse = {
success: boolean; success: boolean;
}; };
export type Annotated = RawTextContent | RawImageContent | RawEmbeddedResource;
export type Annotations = { export type Annotations = {
audience?: Array<Role>; audience?: Array<Role>;
lastModified?: string; lastModified?: string;
@@ -65,7 +63,7 @@ export type ConfigResponse = {
}; };
}; };
export type Content = RawTextContent | RawImageContent | RawEmbeddedResource | Annotated | RawResource; export type Content = RawTextContent | RawImageContent | RawEmbeddedResource | RawAudioContent | RawResource;
export type ContextLengthExceeded = { export type ContextLengthExceeded = {
msg: string; msg: string;
@@ -331,6 +329,12 @@ export type GetToolsQuery = {
session_id: string; session_id: string;
}; };
export type Icon = {
mimeType?: string;
sizes?: string;
src: string;
};
export type ImageContent = { export type ImageContent = {
_meta?: { _meta?: {
[key: string]: unknown; [key: string]: unknown;
@@ -503,6 +507,11 @@ export type ProvidersResponse = {
providers: Array<ProviderDetails>; providers: Array<ProviderDetails>;
}; };
export type RawAudioContent = {
data: string;
mimeType: string;
};
export type RawEmbeddedResource = { export type RawEmbeddedResource = {
_meta?: { _meta?: {
[key: string]: unknown; [key: string]: unknown;
@@ -520,9 +529,11 @@ export type RawImageContent = {
export type RawResource = { export type RawResource = {
description?: string; description?: string;
icons?: Array<Icon>;
mimeType?: string; mimeType?: string;
name: string; name: string;
size?: number; size?: number;
title?: string;
uri: string; uri: string;
}; };
@@ -826,6 +837,7 @@ export type Tool = {
[key: string]: unknown; [key: string]: unknown;
}; };
description?: string; description?: string;
icons?: Array<Icon>;
inputSchema: { inputSchema: {
[key: string]: unknown; [key: string]: unknown;
}; };
@@ -833,6 +845,7 @@ export type Tool = {
outputSchema?: { outputSchema?: {
[key: string]: unknown; [key: string]: unknown;
}; };
title?: string;
}; };
export type ToolAnnotations = { export type ToolAnnotations = {