Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions crates/goose-mcp/src/computercontroller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ use std::os::unix::fs::PermissionsExt;
use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::{JsonRpcMessage, ServerCapabilities},
resource::Resource,
tool::{Tool, ToolAnnotations},
};
use mcp_server::router::CapabilitiesBuilder;
use mcp_server::Router;
use rmcp::model::{Content, Prompt};
use rmcp::model::{AnnotateAble, Content, Prompt, RawResource, Resource};

mod docx_tool;
mod pdf_tool;
Expand Down Expand Up @@ -585,14 +584,16 @@ impl ComputerControllerRouter {
.map_err(|_| ToolError::ExecutionError("Invalid cache path".into()))?
.to_string();

let resource = Resource::new(
uri.clone(),
Some(mime_type.to_string()),
Some(cache_path.to_string_lossy().into_owned()),
)
.map_err(|e| ToolError::ExecutionError(e.to_string()))?;

self.active_resources.lock().unwrap().insert(uri, resource);
let mut resource = RawResource::new(uri.clone(), cache_path.to_string_lossy().into_owned());
resource.mime_type = Some(if mime_type == "blob" {
"blob".to_string()
} else {
"text".to_string()
});
self.active_resources
.lock()
.unwrap()
.insert(uri, resource.no_annotation());
Ok(())
}

Expand Down Expand Up @@ -1175,17 +1176,17 @@ impl Router for ComputerControllerRouter {
.to_file_path()
.map_err(|_| ResourceError::NotFound("Invalid file path in URI".into()))?;

match resource.mime_type.as_str() {
"text" | "json" => fs::read_to_string(&path).map_err(|e| {
match resource.raw.mime_type.as_deref() {
Some("text") | Some("json") | None => fs::read_to_string(&path).map_err(|e| {
ResourceError::ExecutionError(format!("Failed to read file: {}", e))
}),
"binary" => {
Some("binary") => {
let bytes = fs::read(&path).map_err(|e| {
ResourceError::ExecutionError(format!("Failed to read file: {}", e))
})?;
Ok(base64::prelude::BASE64_STANDARD.encode(bytes))
}
mime_type => Err(ResourceError::NotFound(format!(
Some(mime_type) => Err(ResourceError::NotFound(format!(
"Unsupported mime type: {}",
mime_type
))),
Expand Down
3 changes: 1 addition & 2 deletions crates/goose-mcp/src/developer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ use mcp_core::tool::ToolAnnotations;
use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::{JsonRpcMessage, JsonRpcNotification, ServerCapabilities},
resource::Resource,
tool::Tool,
};
use mcp_server::router::CapabilitiesBuilder;
use mcp_server::Router;
use rmcp::model::{Content, Prompt, PromptArgument, PromptTemplate};
use rmcp::model::{Content, Prompt, PromptArgument, PromptTemplate, Resource};

use rmcp::model::Role;

Expand Down
18 changes: 10 additions & 8 deletions crates/goose-mcp/src/google_drive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use mcp_core::protocol::JsonRpcMessage;
use mcp_core::tool::ToolAnnotations;
use oauth_pkce::PkceOAuth2Client;
use regex::Regex;
use rmcp::model::{Content, Prompt};
use rmcp::model::{AnnotateAble, Content, Prompt, RawResource, Resource};
use serde_json::{json, Value};
use std::io::Cursor;
use std::{env, fs, future::Future, path::Path, pin::Pin, sync::Arc};
Expand All @@ -21,7 +21,6 @@ use tokio::sync::mpsc;
use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::ServerCapabilities,
resource::Resource,
tool::Tool,
};
use mcp_server::router::CapabilitiesBuilder;
Expand Down Expand Up @@ -1888,12 +1887,15 @@ impl GoogleDriveRouter {
Ok(r) => {
r.1.files
.map(|files| {
files.into_iter().map(|f| Resource {
uri: f.id.unwrap_or_default(),
mime_type: f.mime_type.unwrap_or_default(),
name: f.name.unwrap_or_default(),
description: None,
annotations: None,
files.into_iter().map(|f| {
RawResource {
uri: f.id.unwrap_or_default(),
mime_type: f.mime_type,
name: f.name.unwrap_or_default(),
description: None,
size: None,
}
.no_annotation()
})
})
.into_iter()
Expand Down
3 changes: 1 addition & 2 deletions crates/goose-mcp/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ use tokio::sync::mpsc;
use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::{JsonRpcMessage, ServerCapabilities},
resource::Resource,
tool::{Tool, ToolAnnotations, ToolCall},
};
use mcp_server::router::CapabilitiesBuilder;
use mcp_server::Router;
use rmcp::model::{Content, Prompt};
use rmcp::model::{Content, Prompt, Resource};

// MemoryRouter implementation
#[derive(Clone)]
Expand Down
3 changes: 1 addition & 2 deletions crates/goose-mcp/src/tutorial/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use anyhow::Result;
use include_dir::{include_dir, Dir};
use indoc::formatdoc;
use rmcp::model::{Content, Prompt, Role};
use rmcp::model::{Content, Prompt, Resource, Role};
use serde_json::{json, Value};
use std::{future::Future, pin::Pin};
use tokio::sync::mpsc;

use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::{JsonRpcMessage, ServerCapabilities},
resource::Resource,
tool::{Tool, ToolAnnotations},
};
use mcp_server::router::CapabilitiesBuilder;
Expand Down
5 changes: 3 additions & 2 deletions crates/goose-server/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata};
use goose::session::info::SessionInfo;
use goose::session::SessionMetadata;
use mcp_core::handler::ToolResultSchema;
use mcp_core::resource::ResourceContents;
use mcp_core::tool::{Tool, ToolAnnotations};
use rmcp::model::ResourceContents;
use rmcp::model::{Annotations, Content, EmbeddedResource, ImageContent, Role, TextContent};
use utoipa::{OpenApi, ToSchema};

Expand Down Expand Up @@ -286,6 +286,7 @@ derive_utoipa!(EmbeddedResource as EmbeddedResourceSchema);
derive_utoipa!(ImageContent as ImageContentSchema);
derive_utoipa!(TextContent as TextContentSchema);
derive_utoipa!(Annotations as AnnotationsSchema);
derive_utoipa!(ResourceContents as ResourceContentsSchema);

#[allow(dead_code)] // Used by utoipa for OpenAPI generation
#[derive(OpenApi)]
Expand Down Expand Up @@ -352,7 +353,7 @@ derive_utoipa!(Annotations as AnnotationsSchema);
ThinkingContent,
RedactedThinkingContent,
FrontendToolRequest,
ResourceContents,
ResourceContentsSchema,
ContextLengthExceeded,
SummarizationRequested,
RoleSchema,
Expand Down
23 changes: 9 additions & 14 deletions crates/goose/src/agents/extension_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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};
use rmcp::model::{Content, Prompt, Resource, ResourceContents};
use serde_json::Value;

// By default, we set it to Jan 1, 2020 if the resource does not have a timestamp
Expand Down Expand Up @@ -427,23 +427,15 @@ impl ExtensionManager {
for resource in resources.resources {
// Skip reading the resource if it's not marked active
// This avoids blowing up the context with inactive resources
if !resource.is_active() {
if !resource_is_active(&resource) {
continue;
}

if let Ok(contents) = client_guard.read_resource(&resource.uri).await {
for content in contents.contents {
let (uri, content_str) = match content {
mcp_core::resource::ResourceContents::TextResourceContents {
uri,
text,
..
} => (uri, text),
mcp_core::resource::ResourceContents::BlobResourceContents {
uri,
blob,
..
} => (uri, blob),
ResourceContents::TextResourceContents { uri, text, .. } => (uri, text),
ResourceContents::BlobResourceContents { uri, blob, .. } => (uri, blob),
};

result.push(ResourceItem::new(
Expand Down Expand Up @@ -550,8 +542,7 @@ impl ExtensionManager {
let mut result = Vec::new();
for content in read_result.contents {
// Only reading the text resource content; skipping the blob content cause it's too long
if let mcp_core::resource::ResourceContents::TextResourceContents { text, .. } = content
{
if let ResourceContents::TextResourceContents { text, .. } = content {
let content_str = format!("{}\n\n{}", uri, text);
result.push(Content::text(content_str));
}
Expand Down Expand Up @@ -825,6 +816,10 @@ impl ExtensionManager {
}
}

fn resource_is_active(resource: &Resource) -> bool {
resource.priority().is_some_and(|p| (p - 1.0).abs() < 1e-6)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 0 additions & 2 deletions crates/mcp-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
pub mod handler;
pub mod tool;
pub use tool::{Tool, ToolCall};
pub mod resource;
pub use resource::{Resource, ResourceContents};
pub mod protocol;
pub use handler::{ToolError, ToolResult};
4 changes: 2 additions & 2 deletions crates/mcp-core/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// The protocol messages exchanged between client and server
use crate::{resource::Resource, resource::ResourceContents, tool::Tool};
use rmcp::model::{Content, Prompt, PromptMessage};
use crate::tool::Tool;
use rmcp::model::{Content, Prompt, PromptMessage, Resource, ResourceContents};
use serde::{Deserialize, Serialize};
use serde_json::Value;

Expand Down
Loading
Loading