diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index c49049942b..0b51988720 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -1304,76 +1304,19 @@ impl ExtensionManager { cancellation_token: CancellationToken, ) -> Result, ErrorData> { let uri = require_str_parameter(¶ms, "uri")?; + let extension_name = require_str_parameter(¶ms, "extension_name")?; - let extension_name = params.get("extension_name").and_then(|v| v.as_str()); + let read_result = self + .read_resource(session_id, uri, extension_name, cancellation_token) + .await?; - // If extension name is provided, we can just look it up - if let Some(ext_name) = extension_name { - let read_result = self - .read_resource(session_id, uri, ext_name, cancellation_token.clone()) - .await?; - - let mut result = Vec::new(); - for content in read_result.contents { - if let ResourceContents::TextResourceContents { text, .. } = content { - let content_str = format!("{}\n\n{}", uri, text); - result.push(Content::text(content_str)); - } - } - return Ok(result); - } - - // If extension name is not provided, we need to search for the resource across all extensions - // Loop through each extension and try to read the resource, don't raise an error if the resource is not found - // TODO: do we want to find if a provided uri is in multiple extensions? - // currently it will return the first match and skip any others - let extension_names: Vec = self - .extensions - .lock() - .await - .iter() - .filter(|(_name, ext)| ext.supports_resources()) - .map(|(name, _)| name.clone()) - .collect(); - - for extension_name in extension_names { - let read_result = self - .read_resource(session_id, uri, &extension_name, cancellation_token.clone()) - .await; - match read_result { - Ok(read_result) => { - let mut result = Vec::new(); - for content in read_result.contents { - if let ResourceContents::TextResourceContents { text, .. } = content { - let content_str = format!("{}\n\n{}", uri, text); - result.push(Content::text(content_str)); - } - } - return Ok(result); - } - Err(_) => continue, + let mut result = Vec::new(); + for content in read_result.contents { + if let ResourceContents::TextResourceContents { text, .. } = content { + result.push(Content::text(format!("{}\n\n{}", uri, text))); } } - - // None of the extensions had the resource so we raise an error - let available_extensions = self - .extensions - .lock() - .await - .keys() - .map(|s| s.as_str()) - .collect::>() - .join(", "); - let error_msg = format!( - "Resource with uri '{}' not found. Here are the available extensions: {}", - uri, available_extensions - ); - - Err(ErrorData::new( - ErrorCode::RESOURCE_NOT_FOUND, - error_msg, - None, - )) + Ok(result) } pub async fn read_resource( @@ -1493,7 +1436,7 @@ impl ExtensionManager { params: Value, cancellation_token: CancellationToken, ) -> Result, ErrorData> { - let extension = params.get("extension").and_then(|v| v.as_str()); + let extension = params.get("extension_name").and_then(|v| v.as_str()); match extension { Some(extension_name) => { diff --git a/crates/goose/src/agents/platform_extensions/ext_manager.rs b/crates/goose/src/agents/platform_extensions/ext_manager.rs index 58221b690e..b54aae58f6 100644 --- a/crates/goose/src/agents/platform_extensions/ext_manager.rs +++ b/crates/goose/src/agents/platform_extensions/ext_manager.rs @@ -53,8 +53,7 @@ pub struct ManageExtensionsParams { #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct ReadResourceParams { pub uri: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub extension_name: Option, + pub extension_name: String, } #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -325,15 +324,17 @@ impl ExtensionManagerClient { files, database schemas, or application-specific information. This tool lists resources in the provided extension, and returns a list for the user to browse. If no extension is provided, the tool will search all extensions for the resource. - "#}.to_string(), + "#} + .to_string(), Arc::new( serde_json::to_value(schema_for!(ListResourcesParams)) .expect("Failed to serialize schema") .as_object() .expect("Schema must be an object") - .clone() + .clone(), ), - ).annotate(ToolAnnotations::from_raw( + ) + .annotate(ToolAnnotations::from_raw( Some("List resources".to_string()), Some(true), Some(false), @@ -343,21 +344,24 @@ impl ExtensionManagerClient { Tool::new( READ_RESOURCE_TOOL_NAME.to_string(), indoc! {r#" - Read a resource from an extension. + Read a resource from a specific extension. Resources allow extensions to share data that provide context to LLMs, such as - files, database schemas, or application-specific information. This tool searches for the - 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(), + files, database schemas, or application-specific information. You must pass the + owning extension as `extension_name`; if you don't know which extension owns a + URI, call `list_resources` first — its output labels each resource with its + extension. + "#} + .to_string(), Arc::new( serde_json::to_value(schema_for!(ReadResourceParams)) .expect("Failed to serialize schema") .as_object() .expect("Schema must be an object") - .clone() + .clone(), ), - ).annotate(ToolAnnotations::from_raw( + ) + .annotate(ToolAnnotations::from_raw( Some("Read a resource".to_string()), Some(true), Some(false),