From e7088ee791bddea4a362b8a2ff3b1a54880d5dc5 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Tue, 4 Aug 2026 21:20:57 +0200 Subject: [PATCH] Handle MCP tool list change notifications Co-authored-by: Douwe M Osinga Co-authored-by: Alexis Rohou Co-authored-by: Alexis Rohou Co-authored-by: Claude Opus 5 (1M context) --- crates/goose/src/agents/extension_manager.rs | 25 ++- crates/goose/src/agents/mcp_client.rs | 147 +++++++++++++++++- ...ontextprotocol_server-everything@2026.1.14 | 28 ++-- 3 files changed, 184 insertions(+), 16 deletions(-) diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index ef3fe8281..79c47b111 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; use std::pin::Pin; use std::process::Stdio; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use std::task::{Context, Poll}; use std::time::Duration; use tempfile::{tempdir, TempDir}; @@ -404,6 +404,7 @@ struct ResolvedTool { resource_uri: Option, } +#[allow(clippy::too_many_arguments)] async fn child_process_client( mut command: Command, timeout: &Option, @@ -412,6 +413,7 @@ async fn child_process_client( docker_container: Option, client_name: String, capabilities: GooseMcpClientCapabilities, + extension_manager: Weak, ) -> ExtensionResult { configure_subprocess(&mut command); @@ -450,6 +452,7 @@ async fn child_process_client( client_name, capabilities, working_dir.clone(), + extension_manager, ) .await; @@ -620,6 +623,7 @@ async fn connect_with_auth( client_name: String, capabilities: GooseMcpClientCapabilities, roots_dir: &std::path::Path, + extension_manager: Weak, ) -> ExtensionResult> { let mut auth_headers = HeaderMap::new(); auth_headers.insert(reqwest::header::USER_AGENT, GOOSE_USER_AGENT); @@ -654,6 +658,7 @@ async fn connect_with_auth( client_name, capabilities, roots_dir.to_path_buf(), + extension_manager, ) .await?, )) @@ -671,6 +676,7 @@ async fn create_streamable_http_client( client_name: String, capabilities: GooseMcpClientCapabilities, roots_dir: &std::path::Path, + extension_manager: Weak, ) -> ExtensionResult> { #[cfg(unix)] if let Some(socket_path) = socket { @@ -684,6 +690,7 @@ async fn create_streamable_http_client( client_name, capabilities, roots_dir, + extension_manager, ) .await; } @@ -739,6 +746,7 @@ async fn create_streamable_http_client( client_name.clone(), capabilities.clone(), roots_dir, + extension_manager.clone(), ) .await; @@ -777,6 +785,7 @@ async fn create_streamable_http_client( client_name.clone(), capabilities.clone(), roots_dir.to_path_buf(), + extension_manager.clone(), ) .await; @@ -792,6 +801,7 @@ async fn create_streamable_http_client( client_name, capabilities, roots_dir, + extension_manager, ) .await } @@ -814,6 +824,7 @@ async fn create_unix_socket_http_client( client_name: String, capabilities: GooseMcpClientCapabilities, roots_dir: &std::path::Path, + extension_manager: Weak, ) -> ExtensionResult> { use rmcp::transport::UnixSocketHttpClient; @@ -851,6 +862,7 @@ async fn create_unix_socket_http_client( client_name.clone(), capabilities.clone(), roots_dir.to_path_buf(), + extension_manager, ) .await; @@ -995,6 +1007,7 @@ impl ExtensionManager { self.client_name.clone(), self.mcp_client_capabilities(), &effective_working_dir, + Arc::downgrade(self), ) .await? } @@ -1052,6 +1065,7 @@ impl ExtensionManager { Some(container_id.to_string()), self.client_name.clone(), self.mcp_client_capabilities(), + Arc::downgrade(self), ) .await?; Box::new(client) @@ -1068,6 +1082,7 @@ impl ExtensionManager { self.client_name.clone(), self.mcp_client_capabilities(), effective_working_dir.clone(), + Arc::downgrade(self), ) .await?, ) @@ -1129,6 +1144,7 @@ impl ExtensionManager { container.map(|c| c.id().to_string()), self.client_name.clone(), self.mcp_client_capabilities(), + Arc::downgrade(self), ) .await?; Box::new(client) @@ -1161,6 +1177,7 @@ impl ExtensionManager { container.map(|c| c.id().to_string()), self.client_name.clone(), self.mcp_client_capabilities(), + Arc::downgrade(self), ) .await?; @@ -1385,7 +1402,7 @@ impl ExtensionManager { Some(attachment) } - async fn invalidate_tools_cache_and_bump_version(&self) { + pub(crate) async fn invalidate_tools_cache_and_bump_version(&self) { self.tools_cache_version.fetch_add(1, Ordering::SeqCst); *self.tools_cache.lock().await = None; } @@ -3329,6 +3346,7 @@ mod tests { "goose-test".to_string(), capabilities, temp_dir.path(), + Weak::new(), ) .await; @@ -3364,6 +3382,7 @@ mod tests { "goose-test".to_string(), capabilities, temp_dir.path(), + Weak::new(), ) .await; @@ -3410,6 +3429,7 @@ mod tests { "goose-test".to_string(), capabilities, temp_dir.path(), + Weak::new(), ) .await; @@ -3491,6 +3511,7 @@ mod tests { "goose-test".to_string(), capabilities, temp_dir.path(), + Weak::new(), ) .await; diff --git a/crates/goose/src/agents/mcp_client.rs b/crates/goose/src/agents/mcp_client.rs index d7b0dddf3..efd309191 100644 --- a/crates/goose/src/agents/mcp_client.rs +++ b/crates/goose/src/agents/mcp_client.rs @@ -1,4 +1,5 @@ use crate::action_required_manager::{ActionRequiredManager, ElicitationOutcome}; +use crate::agents::extension_manager::ExtensionManager; use crate::agents::tool_execution::ToolCallContext; use crate::agents::types::SharedProvider; use crate::session_context::{SESSION_ID_HEADER, TOOL_CALL_REQUEST_ID_HEADER, WORKING_DIR_HEADER}; @@ -31,7 +32,10 @@ use rmcp::{ }; use serde_json::Value; use std::{ - collections::HashMap, path::PathBuf, sync::Arc, sync::Mutex as StdMutex, time::Duration, + collections::HashMap, + path::PathBuf, + sync::{Arc, Mutex as StdMutex, Weak}, + time::Duration, }; use tokio::sync::{ mpsc::{self, Sender}, @@ -185,6 +189,7 @@ pub struct GooseClient { client_name: String, capabilities: GooseMcpClientCapabilities, working_dir: Arc>, + extension_manager: Weak, } impl GooseClient { @@ -194,6 +199,7 @@ impl GooseClient { client_name: String, capabilities: GooseMcpClientCapabilities, working_dir: PathBuf, + extension_manager: Weak, ) -> Self { GooseClient { notification_handlers: handlers, @@ -203,6 +209,7 @@ impl GooseClient { client_name, capabilities, working_dir: Arc::new(tokio::sync::RwLock::new(working_dir)), + extension_manager, } } @@ -219,6 +226,14 @@ impl GooseClient { *slot = Some(session_id.to_string()); } + async fn handle_tool_list_changed(&self) { + if let Some(extension_manager) = self.extension_manager.upgrade() { + extension_manager + .invalidate_tools_cache_and_bump_version() + .await; + } + } + async fn current_session_id(&self) -> Option { self.session_id.lock().await.clone() } @@ -362,6 +377,10 @@ impl ClientHandler for GooseClient { }); } + async fn on_tool_list_changed(&self, _context: rmcp::service::NotificationContext) { + self.handle_tool_list_changed().await; + } + #[expect(deprecated)] async fn on_logging_message( &self, @@ -570,6 +589,7 @@ impl McpClient { client_name: String, capabilities: GooseMcpClientCapabilities, working_dir: PathBuf, + extension_manager: Weak, ) -> Result where T: IntoTransport, @@ -583,10 +603,12 @@ impl McpClient { client_name, capabilities, working_dir, + extension_manager, ) .await } + #[allow(clippy::too_many_arguments)] pub async fn connect_with_container( transport: T, timeout: std::time::Duration, @@ -595,6 +617,7 @@ impl McpClient { client_name: String, capabilities: GooseMcpClientCapabilities, working_dir: PathBuf, + extension_manager: Weak, ) -> Result where T: IntoTransport, @@ -609,6 +632,7 @@ impl McpClient { client_name.clone(), capabilities.clone(), working_dir, + extension_manager, ); let client: rmcp::service::RunningService = client.serve(transport).await?; @@ -1004,9 +1028,62 @@ fn inject_session_context_into_request( #[cfg(test)] mod tests { use super::*; + use crate::agents::extension::ExtensionConfig; use crate::agents::GoosePlatform; + use rmcp::model::Tool; use serde_json::json; + use std::sync::atomic::{AtomicUsize, Ordering}; use test_case::test_case; + use tokio::sync::Semaphore; + + struct BlockingToolsClient { + calls: AtomicUsize, + first_fetch_started: Semaphore, + release_first_fetch: Semaphore, + } + + #[async_trait::async_trait] + impl McpClientTrait for BlockingToolsClient { + async fn list_tools( + &self, + _session_id: &str, + _next_cursor: Option, + _cancel_token: CancellationToken, + ) -> Result { + let call = self.calls.fetch_add(1, Ordering::SeqCst); + let name = if call == 0 { "old" } else { "new" }; + + if call == 0 { + self.first_fetch_started.add_permits(1); + let _permit = self.release_first_fetch.acquire().await.unwrap(); + } + + Ok(ListToolsResult { + tools: vec![Tool::new( + name, + format!("{name} tool list"), + Arc::new(JsonObject::new()), + )], + next_cursor: None, + meta: None, + ..Default::default() + }) + } + + async fn call_tool( + &self, + _ctx: &ToolCallContext, + _name: &str, + _arguments: Option, + _cancel_token: CancellationToken, + ) -> Result { + Ok(CallToolResult::success(vec![])) + } + + fn get_info(&self) -> Option<&InitializeResult> { + None + } + } fn new_client(platform: GoosePlatform) -> GooseClient { let capabilities = match platform { @@ -1026,9 +1103,74 @@ mod tests { platform.to_string(), capabilities, std::env::current_dir().unwrap_or_default(), + Weak::new(), ) } + #[tokio::test] + async fn tool_list_changed_during_fetch_prevents_stale_cache() { + let temp_dir = tempfile::tempdir().unwrap(); + let extension_manager = Arc::new(ExtensionManager::new_without_provider( + temp_dir.path().to_path_buf(), + )); + let tools_client = Arc::new(BlockingToolsClient { + calls: AtomicUsize::new(0), + first_fetch_started: Semaphore::new(0), + release_first_fetch: Semaphore::new(0), + }); + let config = ExtensionConfig::Builtin { + name: "dynamic".to_string(), + display_name: Some("dynamic".to_string()), + description: "dynamic tools".to_string(), + timeout: None, + bundled: None, + available_tools: vec![], + }; + extension_manager + .add_client( + "dynamic".to_string(), + config, + tools_client.clone(), + None, + None, + ) + .await; + + let goose_client = GooseClient::new( + Arc::new(Mutex::new(Vec::new())), + Arc::new(Mutex::new(None)), + "goose-test".to_string(), + GooseMcpClientCapabilities { + mcpui: false, + host_info: None, + }, + temp_dir.path().to_path_buf(), + Arc::downgrade(&extension_manager), + ); + + let manager = extension_manager.clone(); + let first_fetch = tokio::spawn(async move { + manager + .get_prefixed_tools("test-session", None) + .await + .unwrap() + }); + + let _started = tools_client.first_fetch_started.acquire().await.unwrap(); + goose_client.handle_tool_list_changed().await; + tools_client.release_first_fetch.add_permits(1); + + let stale_result = first_fetch.await.unwrap(); + assert!(stale_result.iter().any(|tool| tool.name == "dynamic__old")); + + let refreshed = extension_manager + .get_prefixed_tools("test-session", None) + .await + .unwrap(); + assert!(refreshed.iter().any(|tool| tool.name == "dynamic__new")); + assert_eq!(tools_client.calls.load(Ordering::SeqCst), 2); + } + fn request_extensions(request: &ClientRequest) -> Option<&Extensions> { match request { ClientRequest::ListResourcesRequest(req) => Some(&req.extensions), @@ -1378,6 +1520,7 @@ mod tests { }), }, std::env::current_dir().unwrap_or_default(), + Weak::new(), ); let info = ClientHandler::get_info(&client); @@ -1409,6 +1552,7 @@ mod tests { }), }, std::env::current_dir().unwrap_or_default(), + Weak::new(), ); let info = ClientHandler::get_info(&client); @@ -1437,6 +1581,7 @@ mod tests { }), }, std::env::current_dir().unwrap_or_default(), + Weak::new(), ); let info = ClientHandler::get_info(&client); diff --git a/crates/goose/tests/mcp_replays/npx-y@modelcontextprotocol_server-everything@2026.1.14 b/crates/goose/tests/mcp_replays/npx-y@modelcontextprotocol_server-everything@2026.1.14 index dd9de3d55..44fb61425 100644 --- a/crates/goose/tests/mcp_replays/npx-y@modelcontextprotocol_server-everything@2026.1.14 +++ b/crates/goose/tests/mcp_replays/npx-y@modelcontextprotocol_server-everything@2026.1.14 @@ -9,21 +9,23 @@ STDOUT: {"method":"notifications/tools/list_changed","jsonrpc":"2.0"} STDOUT: {"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes back the input string","inputSchema":{"type":"object","properties":{"message":{"type":"string","description":"Message to echo"}},"required":["message"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-annotated-message","title":"Get Annotated Message Tool","description":"Demonstrates how annotations can be used to provide metadata about content.","inputSchema":{"type":"object","properties":{"messageType":{"type":"string","enum":["error","success","debug"],"description":"Type of message to demonstrate different annotation patterns"},"includeImage":{"type":"boolean","default":false,"description":"Whether to include an example image"}},"required":["messageType"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-env","title":"Print Environment Tool","description":"Returns all environment variables, helpful for debugging MCP server configuration","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"get-resource-links","title":"Get Resource Links Tool","description":"Returns up to ten resource links that reference different types of resources","inputSchema":{"type":"object","properties":{"count":{"type":"number","minimum":1,"maximum":10,"default":3,"description":"Number of resource links to return (1-10)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-resource-reference","title":"Get Resource Reference Tool","description":"Returns a resource reference that can be used by MCP clients","inputSchema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["Text","Blob"],"default":"Text"},"resourceId":{"type":"number","default":1,"description":"ID of the text resource to fetch"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-structured-content","title":"Get Structured Content Tool","description":"Returns structured content along with an output schema for client data validation","inputSchema":{"type":"object","properties":{"location":{"type":"string","enum":["New York","Chicago","Los Angeles"],"description":"Choose city"}},"required":["location"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"temperature":{"type":"number","description":"Temperature in celsius"},"conditions":{"type":"string","description":"Weather conditions description"},"humidity":{"type":"number","description":"Humidity percentage"}},"required":["temperature","conditions","humidity"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"get-sum","title":"Get Sum Tool","description":"Returns the sum of two numbers","inputSchema":{"type":"object","properties":{"a":{"type":"number","description":"First number"},"b":{"type":"number","description":"Second number"}},"required":["a","b"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-tiny-image","title":"Get Tiny Image Tool","description":"Returns a tiny MCP logo image.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"gzip-file-as-resource","title":"GZip File as Resource Tool","description":"Compresses a single file using gzip compression. Depending upon the selected output type, returns either the compressed data as a gzipped resource or a resource link, allowing it to be downloaded in a subsequent request during the current session.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Name of the output file","default":"README.md.gz"},"data":{"type":"string","format":"uri","description":"URL or data URI of the file content to compress","default":"https://raw.githubusercontent.com/modelcontextprotocol/servers/refs/heads/main/README.md"},"outputType":{"type":"string","enum":["resourceLink","resource"],"default":"resourceLink","description":"How the resulting gzipped file should be returned. 'resourceLink' returns a link to a resource that can be read later, 'resource' returns a full resource object."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"toggle-simulated-logging","title":"Toggle Simulated Logging","description":"Toggles simulated, random-leveled logging on or off.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"toggle-subscriber-updates","title":"Toggle Subscriber Updates","description":"Toggles simulated resource subscription updates on or off.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-long-running-operation","title":"Trigger Long Running Operation Tool","description":"Demonstrates a long running operation with progress updates.","inputSchema":{"type":"object","properties":{"duration":{"type":"number","default":10,"description":"Duration of the operation in seconds"},"steps":{"type":"number","default":5,"description":"Number of steps in the operation"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-roots-list","title":"Get Roots List Tool","description":"Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-elicitation-request","title":"Trigger Elicitation Request Tool","description":"Trigger a Request from the Server for User Elicitation","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-sampling-request","title":"Trigger Sampling Request Tool","description":"Trigger a Request from the Server for LLM Sampling","inputSchema":{"type":"object","properties":{"prompt":{"type":"string","description":"The prompt to send to the LLM"},"maxTokens":{"type":"number","default":100,"description":"Maximum number of tokens to generate"}},"required":["prompt"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}}]},"jsonrpc":"2.0","id":1} STDIN: {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":1},"name":"echo","arguments":{"message":"Hello, world!"}}} STDOUT: {"result":{"content":[{"type":"text","text":"Echo: Hello, world!"}]},"jsonrpc":"2.0","id":2} -STDIN: {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":2},"name":"get-sum","arguments":{"a":1,"b":2}}} -STDOUT: {"result":{"content":[{"type":"text","text":"The sum of 1 and 2 is 3."}]},"jsonrpc":"2.0","id":3} -STDIN: {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":3},"name":"trigger-long-running-operation","arguments":{"duration":1,"steps":5}}} -STDOUT: {"method":"notifications/progress","params":{"progress":1,"total":5,"progressToken":3},"jsonrpc":"2.0"} +STDIN: {"jsonrpc":"2.0","id":3,"method":"tools/list","params":{"_meta":{"agent-session-id":"test-session-id","progressToken":2}}} +STDOUT: {"result":{"tools":[{"name":"echo","title":"Echo Tool","description":"Echoes back the input string","inputSchema":{"type":"object","properties":{"message":{"type":"string","description":"Message to echo"}},"required":["message"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-annotated-message","title":"Get Annotated Message Tool","description":"Demonstrates how annotations can be used to provide metadata about content.","inputSchema":{"type":"object","properties":{"messageType":{"type":"string","enum":["error","success","debug"],"description":"Type of message to demonstrate different annotation patterns"},"includeImage":{"type":"boolean","default":false,"description":"Whether to include an example image"}},"required":["messageType"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-env","title":"Print Environment Tool","description":"Returns all environment variables, helpful for debugging MCP server configuration","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"get-resource-links","title":"Get Resource Links Tool","description":"Returns up to ten resource links that reference different types of resources","inputSchema":{"type":"object","properties":{"count":{"type":"number","minimum":1,"maximum":10,"default":3,"description":"Number of resource links to return (1-10)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-resource-reference","title":"Get Resource Reference Tool","description":"Returns a resource reference that can be used by MCP clients","inputSchema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["Text","Blob"],"default":"Text"},"resourceId":{"type":"number","default":1,"description":"ID of the text resource to fetch"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-structured-content","title":"Get Structured Content Tool","description":"Returns structured content along with an output schema for client data validation","inputSchema":{"type":"object","properties":{"location":{"type":"string","enum":["New York","Chicago","Los Angeles"],"description":"Choose city"}},"required":["location"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"temperature":{"type":"number","description":"Temperature in celsius"},"conditions":{"type":"string","description":"Weather conditions description"},"humidity":{"type":"number","description":"Humidity percentage"}},"required":["temperature","conditions","humidity"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"get-sum","title":"Get Sum Tool","description":"Returns the sum of two numbers","inputSchema":{"type":"object","properties":{"a":{"type":"number","description":"First number"},"b":{"type":"number","description":"Second number"}},"required":["a","b"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-tiny-image","title":"Get Tiny Image Tool","description":"Returns a tiny MCP logo image.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"gzip-file-as-resource","title":"GZip File as Resource Tool","description":"Compresses a single file using gzip compression. Depending upon the selected output type, returns either the compressed data as a gzipped resource or a resource link, allowing it to be downloaded in a subsequent request during the current session.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Name of the output file","default":"README.md.gz"},"data":{"type":"string","format":"uri","description":"URL or data URI of the file content to compress","default":"https://raw.githubusercontent.com/modelcontextprotocol/servers/refs/heads/main/README.md"},"outputType":{"type":"string","enum":["resourceLink","resource"],"default":"resourceLink","description":"How the resulting gzipped file should be returned. 'resourceLink' returns a link to a resource that can be read later, 'resource' returns a full resource object."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"toggle-simulated-logging","title":"Toggle Simulated Logging","description":"Toggles simulated, random-leveled logging on or off.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"toggle-subscriber-updates","title":"Toggle Subscriber Updates","description":"Toggles simulated resource subscription updates on or off.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-long-running-operation","title":"Trigger Long Running Operation Tool","description":"Demonstrates a long running operation with progress updates.","inputSchema":{"type":"object","properties":{"duration":{"type":"number","default":10,"description":"Duration of the operation in seconds"},"steps":{"type":"number","default":5,"description":"Number of steps in the operation"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"get-roots-list","title":"Get Roots List Tool","description":"Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-elicitation-request","title":"Trigger Elicitation Request Tool","description":"Trigger a Request from the Server for User Elicitation","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{}},"execution":{"taskSupport":"forbidden"}},{"name":"trigger-sampling-request","title":"Trigger Sampling Request Tool","description":"Trigger a Request from the Server for LLM Sampling","inputSchema":{"type":"object","properties":{"prompt":{"type":"string","description":"The prompt to send to the LLM"},"maxTokens":{"type":"number","default":100,"description":"Maximum number of tokens to generate"}},"required":["prompt"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}}]},"jsonrpc":"2.0","id":3} +STDIN: {"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":3},"name":"get-sum","arguments":{"a":1,"b":2}}} +STDOUT: {"result":{"content":[{"type":"text","text":"The sum of 1 and 2 is 3."}]},"jsonrpc":"2.0","id":4} +STDIN: {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":4},"name":"trigger-long-running-operation","arguments":{"duration":1,"steps":5}}} +STDOUT: {"method":"notifications/progress","params":{"progress":1,"total":5,"progressToken":4},"jsonrpc":"2.0"} STDOUT: {"method":"roots/list","jsonrpc":"2.0","id":0} STDIN: {"jsonrpc":"2.0","id":0,"result":{"roots":[{"uri":"file:///tmp/goose_test","name":"working_directory"}]}} STDOUT: {"method":"notifications/message","params":{"level":"info","logger":"everything-server","data":"Roots updated: 1 root(s) received from client"},"jsonrpc":"2.0"} -STDOUT: {"method":"notifications/progress","params":{"progress":2,"total":5,"progressToken":3},"jsonrpc":"2.0"} -STDOUT: {"method":"notifications/progress","params":{"progress":3,"total":5,"progressToken":3},"jsonrpc":"2.0"} -STDOUT: {"method":"notifications/progress","params":{"progress":4,"total":5,"progressToken":3},"jsonrpc":"2.0"} -STDOUT: {"method":"notifications/progress","params":{"progress":5,"total":5,"progressToken":3},"jsonrpc":"2.0"} -STDOUT: {"result":{"content":[{"type":"text","text":"Long running operation completed. Duration: 1 seconds, Steps: 5."}]},"jsonrpc":"2.0","id":4} -STDIN: {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":4},"name":"get-structured-content","arguments":{"location":"New York"}}} -STDOUT: {"result":{"content":[{"type":"text","text":"{\"temperature\":33,\"conditions\":\"Cloudy\",\"humidity\":82}"}],"structuredContent":{"temperature":33,"conditions":"Cloudy","humidity":82}},"jsonrpc":"2.0","id":5} -STDIN: {"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":5},"name":"trigger-sampling-request","arguments":{"prompt":"Please provide a quote from The Great Gatsby","maxTokens":100}}} +STDOUT: {"method":"notifications/progress","params":{"progress":2,"total":5,"progressToken":4},"jsonrpc":"2.0"} +STDOUT: {"method":"notifications/progress","params":{"progress":3,"total":5,"progressToken":4},"jsonrpc":"2.0"} +STDOUT: {"method":"notifications/progress","params":{"progress":4,"total":5,"progressToken":4},"jsonrpc":"2.0"} +STDOUT: {"method":"notifications/progress","params":{"progress":5,"total":5,"progressToken":4},"jsonrpc":"2.0"} +STDOUT: {"result":{"content":[{"type":"text","text":"Long running operation completed. Duration: 1 seconds, Steps: 5."}]},"jsonrpc":"2.0","id":5} +STDIN: {"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":5},"name":"get-structured-content","arguments":{"location":"New York"}}} +STDOUT: {"result":{"content":[{"type":"text","text":"{\"temperature\":33,\"conditions\":\"Cloudy\",\"humidity\":82}"}],"structuredContent":{"temperature":33,"conditions":"Cloudy","humidity":82}},"jsonrpc":"2.0","id":6} +STDIN: {"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":6},"name":"trigger-sampling-request","arguments":{"prompt":"Please provide a quote from The Great Gatsby","maxTokens":100}}} STDOUT: {"method":"sampling/createMessage","params":{"messages":[{"role":"user","content":{"type":"text","text":"Resource trigger-sampling-request context: Please provide a quote from The Great Gatsby"}}],"systemPrompt":"You are a helpful test server.","maxTokens":100,"temperature":0.7},"jsonrpc":"2.0","id":1} STDIN: {"jsonrpc":"2.0","id":1,"result":{"model":"mock","stopReason":"endTurn","role":"assistant","content":{"type":"text","text":"\"So we beat on, boats against the current, borne back ceaselessly into the past.\" — F. Scott Fitzgerald, The Great Gatsby (1925)"}}} -STDOUT: {"result":{"content":[{"type":"text","text":"LLM sampling result: \n{\n \"model\": \"mock\",\n \"stopReason\": \"endTurn\",\n \"role\": \"assistant\",\n \"content\": {\n \"type\": \"text\",\n \"text\": \"\\\"So we beat on, boats against the current, borne back ceaselessly into the past.\\\" — F. Scott Fitzgerald, The Great Gatsby (1925)\"\n }\n}"}]},"jsonrpc":"2.0","id":6} +STDOUT: {"result":{"content":[{"type":"text","text":"LLM sampling result: \n{\n \"model\": \"mock\",\n \"stopReason\": \"endTurn\",\n \"role\": \"assistant\",\n \"content\": {\n \"type\": \"text\",\n \"text\": \"\\\"So we beat on, boats against the current, borne back ceaselessly into the past.\\\" — F. Scott Fitzgerald, The Great Gatsby (1925)\"\n }\n}"}]},"jsonrpc":"2.0","id":7}