Update to rmcp 1.1.0 (#7619)

Co-authored-by: Alex Hancock <alexhancock@block.xyz>
This commit is contained in:
Jack Amadeo
2026-03-05 21:40:52 -05:00
committed by GitHub
parent abadb87892
commit 325bf396af
61 changed files with 729 additions and 1757 deletions
+5 -21
View File
@@ -33,7 +33,6 @@ use crate::conversation::message::{
ActionRequiredData, Message, MessageContent, ProviderMetadata, SystemNotificationType,
ToolRequest,
};
use crate::conversation::tool_result_serde::call_tool_result;
use crate::conversation::{debug_conversation_fix, fix_conversation, Conversation};
use crate::mcp_utils::ToolResult;
use crate::permission::permission_inspector::PermissionInspector;
@@ -431,12 +430,9 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(CallToolResult {
content: vec![rmcp::model::Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
Ok(CallToolResult::error(vec![rmcp::model::Content::text(
DECLINED_RESPONSE,
)])),
request.metadata.as_ref(),
);
}
@@ -514,12 +510,7 @@ impl Agent {
let result = self
.handle_schedule_management(arguments, request_id.clone())
.await;
let wrapped_result = result.map(|content| CallToolResult {
content,
structured_content: None,
is_error: Some(false),
meta: None,
});
let wrapped_result = result.map(CallToolResult::success);
return (request_id, Ok(ToolCallResult::from(wrapped_result)));
}
@@ -1261,12 +1252,7 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(CallToolResult {
content: vec![Content::text(CHAT_MODE_TOOL_SKIPPED_RESPONSE)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
Ok(CallToolResult::success(vec![Content::text(CHAT_MODE_TOOL_SKIPPED_RESPONSE)])),
request.metadata.as_ref(),
);
}
@@ -1360,8 +1346,6 @@ impl Agent {
Some((request_id, item)) => {
match item {
ToolStreamItem::Result(output) => {
let output = call_tool_result::validate(output);
if let Ok(ref call_result) = output {
if let Some(ref meta) = call_result.meta {
if let Some(notification_data) = meta.0.get("platform_notification") {
+25 -66
View File
@@ -971,7 +971,7 @@ impl ExtensionManager {
let expose_unprefixed = is_unprefixed_extension(&config);
loop {
for tool in client_tools.tools {
for mut tool in client_tools.tools {
if config.is_tool_available(&tool.name) {
let public_name = if expose_unprefixed {
tool.name.to_string()
@@ -989,17 +989,10 @@ impl ExtensionManager {
serde_json::Value::String(name.clone()),
);
tools.push(Tool {
name: public_name.into(),
description: tool.description,
input_schema: tool.input_schema,
annotations: tool.annotations,
output_schema: tool.output_schema,
execution: tool.execution,
icons: tool.icons,
title: tool.title,
meta: Some(rmcp::model::Meta(meta_map)),
});
tool.name = public_name.into();
tool.meta = Some(rmcp::model::Meta(meta_map));
tools.push(tool);
}
}
@@ -1790,12 +1783,9 @@ mod tests {
_cancellation_token: CancellationToken,
) -> Result<CallToolResult, Error> {
match name {
"tool" | "test__tool" | "available_tool" | "hidden_tool" => Ok(CallToolResult {
content: vec![],
is_error: None,
structured_content: None,
meta: None,
}),
"tool" | "test__tool" | "available_tool" | "hidden_tool" => {
Ok(CallToolResult::success(vec![]))
}
_ => Err(Error::TransportClosed),
}
}
@@ -1843,12 +1833,8 @@ mod tests {
.add_mock_extension("client 🚀".to_string(), Arc::new(MockClient {}))
.await;
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "test_client__tool".to_string().into(),
arguments: Some(object!({})),
};
let tool_call =
CallToolRequestParams::new("test_client__tool".to_string()).with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -1860,12 +1846,8 @@ mod tests {
.await;
assert!(result.is_ok());
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "test_client__available_tool".to_string().into(),
arguments: Some(object!({})),
};
let tool_call = CallToolRequestParams::new("test_client__available_tool".to_string())
.with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -1877,12 +1859,8 @@ mod tests {
.await;
assert!(result.is_ok());
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "__cli__ent____tool".to_string().into(),
arguments: Some(object!({})),
};
let tool_call = CallToolRequestParams::new("__cli__ent____tool".to_string())
.with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -1894,12 +1872,8 @@ mod tests {
.await;
assert!(result.is_ok());
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "client___tool".to_string().into(),
arguments: Some(object!({})),
};
let tool_call =
CallToolRequestParams::new("client___tool".to_string()).with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -1911,12 +1885,8 @@ mod tests {
.await;
assert!(result.is_ok());
let invalid_tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "client___tools".to_string().into(),
arguments: Some(object!({})),
};
let invalid_tool_call =
CallToolRequestParams::new("client___tools".to_string()).with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -1933,12 +1903,8 @@ mod tests {
panic!("Expected ErrorData with ErrorCode::RESOURCE_NOT_FOUND");
}
let invalid_tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "_client__tools".to_string().into(),
arguments: Some(object!({})),
};
let invalid_tool_call =
CallToolRequestParams::new("_client__tools".to_string()).with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -2035,12 +2001,8 @@ mod tests {
)
.await;
let unavailable_tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "test_extension__tool".to_string().into(),
arguments: Some(object!({})),
};
let unavailable_tool_call = CallToolRequestParams::new("test_extension__tool".to_string())
.with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
@@ -2059,12 +2021,9 @@ mod tests {
}
// Try to call an available tool - should succeed
let available_tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "test_extension__available_tool".to_string().into(),
arguments: Some(object!({})),
};
let available_tool_call =
CallToolRequestParams::new("test_extension__available_tool".to_string())
.with_arguments(object!({}));
let result = extension_manager
.dispatch_tool_call(
+16 -29
View File
@@ -69,13 +69,13 @@ impl FinalOutputTool {
.unwrap()
.clone(),
)
.annotate(ToolAnnotations {
title: Some("Final Output".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
})
.annotate(
ToolAnnotations::with_title("Final Output".to_string())
.read_only(false)
.destructive(false)
.idempotent(true)
.open_world(false),
)
}
pub fn system_prompt(&self) -> String {
@@ -123,14 +123,9 @@ impl FinalOutputTool {
match result {
Ok(parsed_value) => {
self.final_output = Some(Self::parsed_final_output_string(parsed_value));
ToolCallResult::from(Ok(rmcp::model::CallToolResult {
content: vec![Content::text(
"Final output successfully collected.".to_string(),
)],
structured_content: None,
is_error: Some(false),
meta: None,
}))
ToolCallResult::from(Ok(rmcp::model::CallToolResult::success(vec![
Content::text("Final output successfully collected.".to_string()),
])))
}
Err(error) => ToolCallResult::from(Err(ErrorData {
code: ErrorCode::INVALID_PARAMS,
@@ -232,14 +227,10 @@ mod tests {
};
let mut tool = FinalOutputTool::new(response);
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: FINAL_OUTPUT_TOOL_NAME.into(),
arguments: Some(object!({
let tool_call =
CallToolRequestParams::new(FINAL_OUTPUT_TOOL_NAME).with_arguments(object!({
"message": "Hello" // Missing required "count" field
})),
};
}));
let result = tool.execute_tool_call(tool_call).await;
let tool_result = result.result.await;
@@ -256,18 +247,14 @@ mod tests {
};
let mut tool = FinalOutputTool::new(response);
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: FINAL_OUTPUT_TOOL_NAME.into(),
arguments: Some(object!({
let tool_call =
CallToolRequestParams::new(FINAL_OUTPUT_TOOL_NAME).with_arguments(object!({
"user": {
"name": "John",
"age": 30
},
"tags": ["developer", "rust"]
})),
};
}));
let result = tool.execute_tool_call(tool_call).await;
let tool_result = result.result.await;
@@ -90,12 +90,7 @@ mod tests {
let small_text = "This is a small text response";
let content = Content::text(small_text.to_string());
let response = Ok(CallToolResult {
content: vec![content],
structured_content: None,
is_error: Some(false),
meta: None,
});
let response = Ok(CallToolResult::success(vec![content]));
// Process the response
let processed = process_tool_response(response).unwrap();
@@ -115,12 +110,7 @@ mod tests {
let large_text = "a".repeat(LARGE_TEXT_THRESHOLD + 1000);
let content = Content::text(large_text.clone());
let response = Ok(CallToolResult {
content: vec![content],
structured_content: None,
is_error: Some(false),
meta: None,
});
let response = Ok(CallToolResult::success(vec![content]));
// Process the response
let processed = process_tool_response(response).unwrap();
@@ -157,12 +147,7 @@ mod tests {
// Create an image content
let image_content = Content::image("base64data".to_string(), "image/png".to_string());
let response = Ok(CallToolResult {
content: vec![image_content],
structured_content: None,
is_error: Some(false),
meta: None,
});
let response = Ok(CallToolResult::success(vec![image_content]));
// Process the response
let processed = process_tool_response(response).unwrap();
@@ -184,12 +169,7 @@ mod tests {
let large_text = Content::text("a".repeat(LARGE_TEXT_THRESHOLD + 1000));
let image = Content::image("image_data".to_string(), "image/jpeg".to_string());
let response = Ok(CallToolResult {
content: vec![small_text, large_text, image],
structured_content: None,
is_error: Some(false),
meta: None,
});
let response = Ok(CallToolResult::success(vec![small_text, large_text, image]));
// Process the response
let processed = process_tool_response(response).unwrap();
+72 -142
View File
@@ -3,21 +3,19 @@ use crate::agents::types::SharedProvider;
use crate::session_context::{SESSION_ID_HEADER, WORKING_DIR_HEADER};
use rmcp::model::{
CreateElicitationRequestParams, CreateElicitationResult, ElicitationAction, ErrorCode,
ExtensionCapabilities, Extensions, JsonObject, Meta, SamplingMessageContent,
ExtensionCapabilities, Extensions, JsonObject, LoggingMessageNotification, Meta,
SamplingMessageContent,
};
/// MCP client implementation for Goose
use rmcp::{
model::{
CallToolRequest, CallToolRequestParams, CallToolResult, CancelledNotification,
CancelledNotificationMethod, CancelledNotificationParam, ClientCapabilities, ClientInfo,
ClientRequest, CreateMessageRequestParams, CreateMessageResult, GetPromptRequest,
GetPromptRequestParams, GetPromptResult, Implementation, InitializeResult,
ListPromptsRequest, ListPromptsResult, ListResourcesRequest, ListResourcesResult,
ListToolsRequest, ListToolsResult, LoggingMessageNotification,
LoggingMessageNotificationMethod, PaginatedRequestParams, ProgressNotification,
ProgressNotificationMethod, ProtocolVersion, ReadResourceRequest,
ReadResourceRequestParams, ReadResourceResult, RequestId, Role, SamplingMessage,
ServerNotification, ServerResult,
CallToolRequestParams, CallToolResult, CancelledNotificationParam, ClientCapabilities,
ClientInfo, ClientRequest, CreateMessageRequestParams, CreateMessageResult,
GetPromptRequestParams, GetPromptResult, Implementation, InitializeRequestParams,
InitializeResult, ListPromptsResult, ListResourcesResult, ListToolsResult, Notification,
PaginatedRequestParams, ProtocolVersion, ReadResourceRequestParams, ReadResourceResult,
Request, RequestId, RequestOptionalParam, Role, SamplingMessage, ServerNotification,
ServerResult,
},
service::{
ClientInitializeError, PeerRequestOptions, RequestContext, RequestHandle, RunningService,
@@ -171,13 +169,9 @@ impl ClientHandler for GooseClient {
.await
.iter()
.for_each(|handler| {
let _ = handler.try_send(ServerNotification::ProgressNotification(
ProgressNotification {
params: params.clone(),
method: ProgressNotificationMethod,
extensions: context.extensions.clone(),
},
));
let mut not = Notification::new(params.clone());
not.extensions = context.extensions.clone();
let _ = handler.try_send(ServerNotification::ProgressNotification(not));
});
}
@@ -191,13 +185,10 @@ impl ClientHandler for GooseClient {
.await
.iter()
.for_each(|handler| {
let _ = handler.try_send(ServerNotification::LoggingMessageNotification(
LoggingMessageNotification {
params: params.clone(),
method: LoggingMessageNotificationMethod,
extensions: context.extensions.clone(),
},
));
let mut notification = LoggingMessageNotification::new(params.clone());
notification.extensions = context.extensions.clone();
let _ =
handler.try_send(ServerNotification::LoggingMessageNotification(notification));
});
}
@@ -260,10 +251,8 @@ impl ClientHandler for GooseClient {
)
})?;
Ok(CreateMessageResult {
model: usage.model,
stop_reason: Some(CreateMessageResult::STOP_REASON_END_TURN.to_string()),
message: SamplingMessage::new(
Ok(CreateMessageResult::new(
SamplingMessage::new(
Role::Assistant,
if let Some(content) = response.content.first() {
match content {
@@ -283,7 +272,9 @@ impl ClientHandler for GooseClient {
SamplingMessageContent::text("")
},
),
})
usage.model,
)
.with_stop_reason(CreateMessageResult::STOP_REASON_END_TURN))
}
async fn create_elicitation(
@@ -344,24 +335,19 @@ impl ClientHandler for GooseClient {
);
}
ClientInfo {
meta: None,
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ClientCapabilities::builder()
InitializeRequestParams::new(
ClientCapabilities::builder()
.enable_extensions_with(extensions)
.enable_sampling()
.enable_elicitation()
.build(),
client_info: Implementation {
name: self.client_name.clone(),
version: std::env::var("GOOSE_MCP_CLIENT_VERSION")
Implementation::new(
self.client_name.clone(),
std::env::var("GOOSE_MCP_CLIENT_VERSION")
.unwrap_or(env!("CARGO_PKG_VERSION").to_owned()),
icons: None,
title: None,
description: None,
website_url: None,
},
}
),
)
.with_protocol_version(ProtocolVersion::V_2025_03_26)
}
}
@@ -491,12 +477,7 @@ async fn send_cancel_message(
reason: Option<String>,
) -> Result<(), ServiceError> {
peer.send_notification(
CancelledNotification {
params: CancelledNotificationParam { request_id, reason },
method: CancelledNotificationMethod,
extensions: Default::default(),
}
.into(),
Notification::new(CancelledNotificationParam { request_id, reason }).into(),
)
.await
}
@@ -517,11 +498,9 @@ impl McpClientTrait for McpClient {
.send_request_with_context(
session_id,
None,
ClientRequest::ListResourcesRequest(ListResourcesRequest {
params: Some(PaginatedRequestParams { meta: None, cursor }),
method: Default::default(),
extensions: Default::default(),
}),
ClientRequest::ListResourcesRequest(RequestOptionalParam::with_param(
PaginatedRequestParams::default().with_cursor(cursor),
)),
cancel_token,
)
.await?;
@@ -542,14 +521,9 @@ impl McpClientTrait for McpClient {
.send_request_with_context(
session_id,
None,
ClientRequest::ReadResourceRequest(ReadResourceRequest {
params: ReadResourceRequestParams {
meta: None,
uri: uri.to_string(),
},
method: Default::default(),
extensions: Default::default(),
}),
ClientRequest::ReadResourceRequest(Request::new(ReadResourceRequestParams::new(
uri.to_string(),
))),
cancel_token,
)
.await?;
@@ -570,11 +544,9 @@ impl McpClientTrait for McpClient {
.send_request_with_context(
session_id,
None,
ClientRequest::ListToolsRequest(ListToolsRequest {
params: Some(PaginatedRequestParams { meta: None, cursor }),
method: Default::default(),
extensions: Default::default(),
}),
ClientRequest::ListToolsRequest(RequestOptionalParam::with_param(
PaginatedRequestParams::default().with_cursor(cursor),
)),
cancel_token,
)
.await?;
@@ -593,16 +565,11 @@ impl McpClientTrait for McpClient {
working_dir: Option<&str>,
cancel_token: CancellationToken,
) -> Result<CallToolResult, Error> {
let request = ClientRequest::CallToolRequest(CallToolRequest {
params: CallToolRequestParams {
meta: None,
task: None,
name: name.to_string().into(),
arguments,
},
method: Default::default(),
extensions: Default::default(),
});
let mut params = CallToolRequestParams::new(name.to_string());
if let Some(args) = arguments {
params = params.with_arguments(args);
}
let request = ClientRequest::CallToolRequest(Request::new(params));
let result = self
.send_request_with_context(session_id, working_dir, request, cancel_token)
@@ -624,11 +591,9 @@ impl McpClientTrait for McpClient {
.send_request_with_context(
session_id,
None,
ClientRequest::ListPromptsRequest(ListPromptsRequest {
params: Some(PaginatedRequestParams { meta: None, cursor }),
method: Default::default(),
extensions: Default::default(),
}),
ClientRequest::ListPromptsRequest(RequestOptionalParam::with_param(
PaginatedRequestParams::default().with_cursor(cursor),
)),
cancel_token,
)
.await?;
@@ -650,19 +615,15 @@ impl McpClientTrait for McpClient {
Value::Object(map) => Some(map),
_ => None,
};
let mut params = GetPromptRequestParams::new(name.to_string());
if let Some(args) = arguments {
params = params.with_arguments(args);
}
let res = self
.send_request_with_context(
session_id,
None,
ClientRequest::GetPromptRequest(GetPromptRequest {
params: GetPromptRequestParams {
meta: None,
name: name.to_string(),
arguments,
},
method: Default::default(),
extensions: Default::default(),
}),
ClientRequest::GetPromptRequest(Request::new(params)),
cancel_token,
)
.await?;
@@ -791,72 +752,41 @@ mod tests {
}
fn list_resources_request(extensions: Extensions) -> ClientRequest {
ClientRequest::ListResourcesRequest(ListResourcesRequest {
params: Some(PaginatedRequestParams {
meta: None,
cursor: None,
}),
method: Default::default(),
extensions,
})
let mut req = RequestOptionalParam::with_param(PaginatedRequestParams::default());
req.extensions = extensions;
ClientRequest::ListResourcesRequest(req)
}
fn read_resource_request(extensions: Extensions) -> ClientRequest {
ClientRequest::ReadResourceRequest(ReadResourceRequest {
params: ReadResourceRequestParams {
meta: None,
uri: "test://resource".to_string(),
},
method: Default::default(),
extensions,
})
let mut req = Request::new(ReadResourceRequestParams::new(
"test://resource".to_string(),
));
req.extensions = extensions;
ClientRequest::ReadResourceRequest(req)
}
fn list_tools_request(extensions: Extensions) -> ClientRequest {
ClientRequest::ListToolsRequest(ListToolsRequest {
params: Some(PaginatedRequestParams {
meta: None,
cursor: None,
}),
method: Default::default(),
extensions,
})
let mut req = RequestOptionalParam::with_param(PaginatedRequestParams::default());
req.extensions = extensions;
ClientRequest::ListToolsRequest(req)
}
fn call_tool_request(extensions: Extensions) -> ClientRequest {
ClientRequest::CallToolRequest(CallToolRequest {
params: CallToolRequestParams {
meta: None,
task: None,
name: "tool".to_string().into(),
arguments: None,
},
method: Default::default(),
extensions,
})
let mut req = Request::new(CallToolRequestParams::new("tool".to_string()));
req.extensions = extensions;
ClientRequest::CallToolRequest(req)
}
fn list_prompts_request(extensions: Extensions) -> ClientRequest {
ClientRequest::ListPromptsRequest(ListPromptsRequest {
params: Some(PaginatedRequestParams {
meta: None,
cursor: None,
}),
method: Default::default(),
extensions,
})
let mut req = RequestOptionalParam::with_param(PaginatedRequestParams::default());
req.extensions = extensions;
ClientRequest::ListPromptsRequest(req)
}
fn get_prompt_request(extensions: Extensions) -> ClientRequest {
ClientRequest::GetPromptRequest(GetPromptRequest {
params: GetPromptRequestParams {
meta: None,
name: "prompt".to_string(),
arguments: None,
},
method: Default::default(),
extensions,
})
let mut req = Request::new(GetPromptRequestParams::new("prompt".to_string()));
req.extensions = extensions;
ClientRequest::GetPromptRequest(req)
}
#[test_case(
+6 -36
View File
@@ -114,44 +114,14 @@ mod tests {
Message::user().with_text("Search for something"),
Message::assistant()
.with_text("I'll search for you")
.with_tool_request(
"search_1",
Ok(CallToolRequestParams {
meta: None,
task: None,
name: "search".into(),
arguments: None,
}),
),
Message::user().with_tool_response(
"search_1",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
.with_tool_request("search_1", Ok(CallToolRequestParams::new("search"))),
Message::user()
.with_tool_response("search_1", Ok(rmcp::model::CallToolResult::success(vec![]))),
Message::assistant()
.with_text("I need to search more")
.with_tool_request(
"search_2",
Ok(CallToolRequestParams {
meta: None,
task: None,
name: "search".into(),
arguments: None,
}),
),
Message::user().with_tool_response(
"search_2",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
.with_tool_request("search_2", Ok(CallToolRequestParams::new("search"))),
Message::user()
.with_tool_response("search_2", Ok(rmcp::model::CallToolResult::success(vec![]))),
]);
let result = inject_moim("test-session-id", conv, &em, &working_dir).await;
@@ -13,7 +13,7 @@ use parser::{FileAnalysis, Parser};
use rayon::prelude::*;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities, Tool, ToolAnnotations, ToolsCapability,
ServerCapabilities, Tool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde::Deserialize;
@@ -54,40 +54,16 @@ pub struct AnalyzeClient {
impl AnalyzeClient {
pub fn new(_context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some("Analyze".to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(
indoc! {"
Analyze code structure using tree-sitter AST parsing. Three auto-selected modes:
- Directory path → structure overview (file tree with function/class counts)
- File path → semantic details (functions, classes, imports, call counts)
- Any path + focus parameter → symbol call graph (incoming/outgoing chains)
let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Analyze"))
.with_instructions(indoc! {"
Analyze code structure using tree-sitter AST parsing. Three auto-selected modes:
- Directory path → structure overview (file tree with function/class counts)
- File path → semantic details (functions, classes, imports, call counts)
- Any path + focus parameter → symbol call graph (incoming/outgoing chains)
For large codebases, delegate analysis to a subagent and retain only the summary.
"}
.to_string(),
),
};
For large codebases, delegate analysis to a subagent and retain only the summary.
"});
Ok(Self { info })
}
@@ -241,13 +217,13 @@ impl McpClientTrait for AnalyzeClient {
"Analyze code structure in 3 modes: 1) Directory overview - file tree with LOC/function/class counts to max_depth. 2) File details - functions, classes, imports. 3) Symbol focus - call graphs across directory to max_depth (requires file or directory path, case-sensitive). Typical flow: directory → files → symbols. Functions called >3x show •N.".to_string(),
Self::schema::<AnalyzeParams>(),
)
.annotate(ToolAnnotations {
title: Some("Analyze".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
});
.annotate(ToolAnnotations::from_raw(
Some("Analyze".to_string()),
Some(true),
Some(false),
Some(true),
Some(false),
));
Ok(ListToolsResult {
tools: vec![tool],
@@ -9,8 +9,8 @@ use crate::providers::base::Provider;
use async_trait::async_trait;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListResourcesResult,
ListToolsResult, Meta, ProtocolVersion, RawResource, ReadResourceResult, Resource,
ResourceContents, ResourcesCapability, ServerCapabilities, Tool as McpTool, ToolsCapability,
ListToolsResult, Meta, RawResource, ReadResourceResult, Resource, ResourceContents,
ServerCapabilities, Tool as McpTool,
};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -116,36 +116,16 @@ impl AppsManagerClient {
}
fn create_info() -> InitializeResult {
InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
resources: Some(ResourcesCapability {
subscribe: Some(false),
list_changed: Some(false),
}),
prompts: None,
completions: None,
experimental: None,
tasks: None,
logging: None,
extensions: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
title: Some("Apps Manager".to_string()),
version: "1.0.0".to_string(),
description: None,
icons: None,
website_url: None,
},
instructions: Some(
"Use this extension to create, manage, and iterate on custom HTML/CSS/JavaScript apps."
.to_string(),
),
}
InitializeResult::new(
ServerCapabilities::builder()
.enable_tools()
.enable_resources()
.build(),
)
.with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Apps Manager"))
.with_instructions(
"Use this extension to create, manage, and iterate on custom HTML/CSS/JavaScript apps.",
)
}
fn ensure_default_apps(&self) -> Result<(), String> {
@@ -641,9 +621,9 @@ impl McpClientTrait for AppsManagerClient {
.text
.unwrap_or_else(|| String::from("No content"));
Ok(ReadResourceResult {
contents: vec![ResourceContents::text(html, uri)],
})
Ok(ReadResourceResult::new(vec![ResourceContents::text(
html, uri,
)]))
}
fn get_info(&self) -> Option<&InitializeResult> {
@@ -5,7 +5,7 @@ use async_trait::async_trait;
use indoc::indoc;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities, Tool, ToolAnnotations, ToolsCapability,
ServerCapabilities, Tool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -39,29 +39,12 @@ pub struct ChatRecallClient {
impl ChatRecallClient {
pub fn new(context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some("Chat Recall".to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(indoc! {r#"
let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(
Implementation::new(EXTENSION_NAME.to_string(), "1.0.0".to_string())
.with_title("Chat Recall"),
)
.with_instructions(indoc! {r#"
Chat Recall
Search past conversations and load session summaries when the user expects some memory or context.
@@ -69,8 +52,7 @@ impl ChatRecallClient {
Two modes:
- Search mode: Use query with keywords/synonyms to find relevant messages
- Load mode: Use session_id to get first and last messages of a specific session
"#}.to_string()),
};
"#}.to_string());
Ok(Self { info, context })
}
@@ -264,13 +246,13 @@ impl ChatRecallClient {
.to_string(),
input_schema,
)
.annotate(ToolAnnotations {
title: Some("Recall past conversations".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
})]
.annotate(ToolAnnotations::from_raw(
Some("Recall past conversations".to_string()),
Some(true),
Some(false),
Some(true),
Some(false),
))]
}
}
@@ -8,8 +8,7 @@ use pctx_code_mode::model::{CallbackConfig, ExecuteInput, GetFunctionDetailsInpu
use pctx_code_mode::{CallbackRegistry, CodeMode};
use rmcp::model::{
CallToolRequestParams, CallToolResult, Content, Implementation, InitializeResult, JsonObject,
ListToolsResult, ProtocolVersion, RawContent, Role, ServerCapabilities, Tool as McpTool,
ToolAnnotations, ToolsCapability,
ListToolsResult, RawContent, Role, ServerCapabilities, Tool as McpTool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -53,29 +52,12 @@ pub struct ExecuteWithToolGraph {
impl CodeExecutionClient {
pub fn new(context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some("Code Mode".to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(indoc! {r#"
let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(
Implementation::new(EXTENSION_NAME.to_string(), "1.0.0".to_string())
.with_title("Code Mode"),
)
.with_instructions(indoc! {r#"
BATCH MULTIPLE TOOL CALLS INTO ONE execute CALL.
This extension exists to reduce round-trips. When a task requires multiple tool calls:
@@ -90,8 +72,7 @@ impl CodeExecutionClient {
all the namespaces returned by list_functions and get_function_details will be available
3. Chain results: use output from one tool as input to the next
4. Only return and console.log data you need, tools could have very large responses.
"#}.to_string()),
};
"#}.to_string());
Ok(Self {
info,
@@ -264,11 +245,12 @@ fn create_tool_callback(
let full_name = full_name.clone();
let manager = manager.clone();
Box::pin(async move {
let tool_call = CallToolRequestParams {
task: None,
meta: None,
name: full_name.into(),
arguments: args.and_then(|v| v.as_object().cloned()),
let tool_call = {
let mut params = CallToolRequestParams::new(full_name);
if let Some(args) = args.and_then(|v| v.as_object().cloned()) {
params = params.with_arguments(args);
}
params
};
match manager
.dispatch_tool_call(&session_id, tool_call, None, CancellationToken::new())
@@ -345,13 +327,13 @@ impl McpClientTrait for CodeExecutionClient {
.to_string(),
empty_schema,
)
.annotate(ToolAnnotations {
title: Some("List functions".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
}),
.annotate(ToolAnnotations::from_raw(
Some("List functions".to_string()),
Some(true),
Some(false),
Some(true),
Some(false),
)),
McpTool::new(
"get_function_details".to_string(),
indoc! {r#"
@@ -366,13 +348,13 @@ impl McpClientTrait for CodeExecutionClient {
.to_string(),
schema::<GetFunctionDetailsInput>(),
)
.annotate(ToolAnnotations {
title: Some("Get function details".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
}),
.annotate(ToolAnnotations::from_raw(
Some("Get function details".to_string()),
Some(true),
Some(false),
Some(true),
Some(false),
)),
McpTool::new(
"execute".to_string(),
indoc! {r#"
@@ -423,13 +405,13 @@ impl McpClientTrait for CodeExecutionClient {
.to_string(),
schema::<ExecuteWithToolGraph>(),
)
.annotate(ToolAnnotations {
title: Some("Execute TypeScript".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true),
idempotent_hint: Some(false),
open_world_hint: Some(true),
}),
.annotate(ToolAnnotations::from_raw(
Some("Execute TypeScript".to_string()),
Some(false),
Some(true),
Some(false),
Some(true),
)),
],
next_cursor: None,
meta: None,
@@ -10,7 +10,7 @@ use edit::{EditTools, FileEditParams, FileWriteParams};
use indoc::indoc;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities, Tool, ToolAnnotations, ToolsCapability,
ServerCapabilities, Tool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde_json::Value;
@@ -31,42 +31,23 @@ pub struct DeveloperClient {
impl DeveloperClient {
pub fn new(_context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some("Developer".to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(indoc! {"
Use the developer extension to build software and operate a terminal.
let info = InitializeResult::new(
ServerCapabilities::builder().enable_tools().build(),
)
.with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Developer"))
.with_instructions(indoc! {"
Use the developer extension to build software and operate a terminal.
Make sure to use the tools *efficiently* - reading all the content you need in as few
iterations as possible and then making the requested edits or running commands. You are
responsible for managing your context window, and to minimize unnecessary turns which
cost the user money.
Make sure to use the tools *efficiently* - reading all the content you need in as few
iterations as possible and then making the requested edits or running commands. You are
responsible for managing your context window, and to minimize unnecessary turns which
cost the user money.
For editing software, prefer the flow of using tree to understand the codebase structure
and file sizes. When you need to search, prefer rg which correctly respects gitignored
content. Then use cat or sed to gather the context you need, always reading before editing.
Use write and edit to efficiently make changes. Test and verify as appropriate.
"}.to_string()),
};
For editing software, prefer the flow of using tree to understand the codebase structure
and file sizes. When you need to search, prefer rg which correctly respects gitignored
content. Then use cat or sed to gather the context you need, always reading before editing.
Use write and edit to efficiently make changes. Test and verify as appropriate.
"});
Ok(Self {
info,
@@ -100,50 +81,50 @@ impl DeveloperClient {
"Create a new file or overwrite an existing file. Creates parent directories if needed.".to_string(),
Self::schema::<FileWriteParams>(),
)
.annotate(ToolAnnotations {
title: Some("Write".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
.annotate(ToolAnnotations::from_raw(
Some("Write".to_string()),
Some(false),
Some(true),
Some(false),
Some(false),
)),
Tool::new(
"edit".to_string(),
"Edit a file by finding and replacing text. The before text must match exactly and uniquely. Use empty after text to delete.".to_string(),
Self::schema::<FileEditParams>(),
)
.annotate(ToolAnnotations {
title: Some("Edit".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
.annotate(ToolAnnotations::from_raw(
Some("Edit".to_string()),
Some(false),
Some(true),
Some(false),
Some(false),
)),
Tool::new(
"shell".to_string(),
"Execute a shell command in the user's default shell in the current dir. Returns an object with stdout and stderr as separate fields. The output of each stream is limited to up to 2000 lines, and longer outputs will be saved to a temporary file.".to_string(),
Self::schema::<ShellParams>(),
)
.with_output_schema::<ShellOutput>()
.annotate(ToolAnnotations {
title: Some("Shell".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true),
idempotent_hint: Some(false),
open_world_hint: Some(true),
}),
.annotate(ToolAnnotations::from_raw(
Some("Shell".to_string()),
Some(false),
Some(true),
Some(false),
Some(true),
)),
Tool::new(
"tree".to_string(),
"List a directory tree with line counts. Traversal respects .gitignore rules.".to_string(),
Self::schema::<TreeParams>(),
)
.annotate(ToolAnnotations {
title: Some("Tree".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(true),
open_world_hint: Some(false),
}),
.annotate(ToolAnnotations::from_raw(
Some("Tree".to_string()),
Some(true),
Some(false),
Some(true),
Some(false),
)),
]
}
}
@@ -7,8 +7,7 @@ use indoc::indoc;
use rmcp::model::{
CallToolResult, Content, ErrorCode, ErrorData, GetPromptResult, Implementation,
InitializeResult, JsonObject, ListPromptsResult, ListResourcesResult, ListToolsResult,
ProtocolVersion, ReadResourceResult, ServerCapabilities, ServerNotification, Tool,
ToolAnnotations, ToolsCapability,
ReadResourceResult, ServerCapabilities, ServerNotification, Tool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -77,46 +76,27 @@ pub struct ExtensionManagerClient {
impl ExtensionManagerClient {
pub fn new(context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some(EXTENSION_NAME.to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(indoc! {r#"
Extension Management
let info = InitializeResult::new(
ServerCapabilities::builder().enable_tools().build(),
)
.with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title(EXTENSION_NAME))
.with_instructions(indoc! {r#"
Extension Management
Use these tools to discover, enable, and disable extensions, as well as review resources.
Use these tools to discover, enable, and disable extensions, as well as review resources.
Available tools:
- search_available_extensions: Find extensions available to enable/disable
- manage_extensions: Enable or disable extensions
- list_resources: List resources from extensions
- read_resource: Read specific resources from extensions
Available tools:
- search_available_extensions: Find extensions available to enable/disable
- manage_extensions: Enable or disable extensions
- list_resources: List resources from extensions
- read_resource: Read specific resources from extensions
When you lack the tools needed to complete a task, use search_available_extensions first
to discover what extensions can help.
When you lack the tools needed to complete a task, use search_available_extensions first
to discover what extensions can help.
Use manage_extensions to enable or disable specific extensions by name.
Use list_resources and read_resource to work with extension data and resources.
"#}.to_string()),
};
Use manage_extensions to enable or disable specific extensions by name.
Use list_resources and read_resource to work with extension data and resources.
"#});
Ok(Self { info, context })
}
@@ -302,13 +282,13 @@ impl ExtensionManagerClient {
.expect("Schema must be an object")
.clone()
),
).annotate(ToolAnnotations {
title: Some("Discover extensions".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
).annotate(ToolAnnotations::from_raw(
Some("Discover extensions".to_string()),
Some(true),
Some(false),
Some(false),
Some(false),
)),
Tool::new(
MANAGE_EXTENSIONS_TOOL_NAME.to_string(),
"Tool to manage extensions and tools in goose context.
@@ -322,13 +302,13 @@ impl ExtensionManagerClient {
.expect("Schema must be an object")
.clone()
),
).annotate(ToolAnnotations {
title: Some("Enable or disable an extension".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(false),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
).annotate(ToolAnnotations::from_raw(
Some("Enable or disable an extension".to_string()),
Some(false),
Some(false),
Some(false),
Some(false),
)),
];
if let Some(weak_ref) = &self.context.extension_manager {
@@ -352,13 +332,13 @@ impl ExtensionManagerClient {
.expect("Schema must be an object")
.clone()
),
).annotate(ToolAnnotations {
title: Some("List resources".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
).annotate(ToolAnnotations::from_raw(
Some("List resources".to_string()),
Some(true),
Some(false),
Some(false),
Some(false),
)),
Tool::new(
READ_RESOURCE_TOOL_NAME.to_string(),
indoc! {r#"
@@ -376,13 +356,13 @@ impl ExtensionManagerClient {
.expect("Schema must be an object")
.clone()
),
).annotate(ToolAnnotations {
title: Some("Read a resource".to_string()),
read_only_hint: Some(true),
destructive_hint: Some(false),
idempotent_hint: Some(false),
open_world_hint: Some(false),
}),
).annotate(ToolAnnotations::from_raw(
Some("Read a resource".to_string()),
Some(true),
Some(false),
Some(false),
Some(false),
)),
]);
}
}
@@ -448,12 +428,9 @@ impl McpClientTrait for ExtensionManagerClient {
match result {
Ok(content) => Ok(CallToolResult::success(content)),
Err(error) => Ok(CallToolResult {
content: vec![Content::text(error.to_string())],
is_error: Some(true),
structured_content: None,
meta: None,
}),
Err(error) => Ok(CallToolResult::error(vec![Content::text(
error.to_string(),
)])),
}
}
@@ -22,7 +22,7 @@ use anyhow::Result;
use async_trait::async_trait;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities, ServerNotification, Tool, ToolsCapability,
ServerCapabilities, ServerNotification, Tool,
};
use serde::Deserialize;
use std::collections::HashMap;
@@ -517,30 +517,9 @@ impl SummonClient {
None
};
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tasks: None,
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
resources: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
extensions: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
title: Some("Summon".to_string()),
version: "1.0.0".to_string(),
description: None,
icons: None,
website_url: None,
},
instructions,
};
let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Summon"))
.with_instructions(instructions.unwrap_or_default());
Ok(Self {
info,
@@ -2036,17 +2015,12 @@ You review code."#;
use crate::conversation::message::MessageContent;
use rmcp::model::CallToolRequestParams;
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "developer__shell".to_string().into(),
arguments: Some(
serde_json::json!({"command": "ls"})
.as_object()
.unwrap()
.clone(),
),
};
let tool_call = CallToolRequestParams::new("developer__shell").with_arguments(
serde_json::json!({"command": "ls"})
.as_object()
.unwrap()
.clone(),
);
let content = MessageContent::tool_request("req1", Ok(tool_call));
let notif = create_tool_notification(&content, "20260204_1").unwrap();
@@ -7,7 +7,7 @@ use async_trait::async_trait;
use indoc::indoc;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities, Tool, ToolAnnotations, ToolsCapability,
ServerCapabilities, Tool, ToolAnnotations,
};
use schemars::{schema_for, JsonSchema};
use serde::{Deserialize, Serialize};
@@ -27,29 +27,12 @@ pub struct TodoClient {
impl TodoClient {
pub fn new(context: PlatformExtensionContext) -> Result<Self> {
let info = InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(false),
}),
tasks: None,
resources: None,
extensions: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
description: None,
title: Some("Todo".to_string()),
version: "1.0.0".to_string(),
icons: None,
website_url: None,
},
instructions: Some(
let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(
Implementation::new(EXTENSION_NAME.to_string(), "1.0.0".to_string())
.with_title("Todo"),
)
.with_instructions(
indoc! {r#"
Your todo content is automatically available in your context.
@@ -66,8 +49,7 @@ impl TodoClient {
- [ ] Another task
"#}
.to_string(),
),
};
);
Ok(Self { info, context })
}
@@ -146,13 +128,13 @@ impl TodoClient {
.to_string(),
schema_value.as_object().unwrap().clone(),
)
.annotate(ToolAnnotations {
title: Some("Write TODO".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true),
idempotent_hint: Some(false),
open_world_hint: Some(false),
})]
.annotate(ToolAnnotations::from_raw(
Some("Write TODO".to_string()),
Some(false),
Some(true),
Some(false),
Some(false),
))]
}
}
@@ -4,7 +4,7 @@ use anyhow::Result;
use async_trait::async_trait;
use rmcp::model::{
CallToolResult, Content, Implementation, InitializeResult, JsonObject, ListToolsResult,
ProtocolVersion, ServerCapabilities,
ServerCapabilities,
};
use tokio::io::AsyncReadExt;
use tokio_util::sync::CancellationToken;
@@ -20,28 +20,10 @@ pub struct TomClient {
impl TomClient {
pub fn new(_context: PlatformExtensionContext) -> Result<Self> {
Ok(Self {
info: InitializeResult {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities {
tools: None,
tasks: None,
resources: None,
prompts: None,
completions: None,
experimental: None,
logging: None,
extensions: None,
},
server_info: Implementation {
name: EXTENSION_NAME.to_string(),
title: Some("Top Of Mind".to_string()),
version: "1.0.0".to_string(),
description: None,
icons: None,
website_url: None,
},
instructions: None,
},
info: InitializeResult::new(ServerCapabilities::builder().build()).with_server_info(
Implementation::new(EXTENSION_NAME.to_string(), "1.0.0".to_string())
.with_title("Top Of Mind"),
),
})
}
}
+1 -7
View File
@@ -37,11 +37,5 @@ pub fn manage_schedule_tool() -> Tool {
"session_id": {"type": "string", "description": "Session identifier for session_content action"}
}
}),
).annotate(ToolAnnotations {
title: Some("Manage scheduled recipes".to_string()),
read_only_hint: Some(false),
destructive_hint: Some(true), // Can kill jobs
idempotent_hint: Some(false),
open_world_hint: Some(false),
})
).annotate(ToolAnnotations::with_title("Manage scheduled recipes".to_string()).read_only(false).destructive(true).idempotent(false).open_world(false))
}
+11 -17
View File
@@ -10,8 +10,8 @@ use crate::{
use anyhow::{anyhow, Result};
use futures::StreamExt;
use rmcp::model::{
ErrorCode, ErrorData, LoggingLevel, LoggingMessageNotification,
LoggingMessageNotificationMethod, LoggingMessageNotificationParam, ServerNotification,
ErrorCode, ErrorData, LoggingLevel, LoggingMessageNotificationParam, Notification,
ServerNotification,
};
use serde::Serialize;
use std::future::Future;
@@ -272,12 +272,10 @@ pub fn create_tool_notification(
let tool_call = req.tool_call.as_ref().ok()?;
Some(ServerNotification::LoggingMessageNotification(
LoggingMessageNotification {
method: LoggingMessageNotificationMethod,
params: LoggingMessageNotificationParam {
level: LoggingLevel::Info,
logger: Some(format!("subagent:{}", subagent_id)),
data: serde_json::json!({
Notification::new(
LoggingMessageNotificationParam::new(
LoggingLevel::Info,
serde_json::json!({
"type": SUBAGENT_TOOL_REQUEST_TYPE,
"subagent_id": subagent_id,
"tool_call": {
@@ -285,9 +283,9 @@ pub fn create_tool_notification(
"arguments": tool_call.arguments
}
}),
},
extensions: Default::default(),
},
)
.with_logger(format!("subagent:{}", subagent_id)),
),
))
} else {
None
@@ -303,12 +301,8 @@ mod tests {
#[test]
fn create_tool_notification_for_tool_request() {
let tool_call = CallToolRequestParams {
meta: None,
task: None,
name: "developer__shell".to_string().into(),
arguments: Some(json!({"command": "ls"}).as_object().unwrap().clone()),
};
let tool_call = CallToolRequestParams::new("developer__shell".to_string())
.with_arguments(json!({"command": "ls"}).as_object().unwrap().clone());
let content = MessageContent::tool_request("req1", Ok(tool_call));
let notification =
create_tool_notification(&content, "session_1").expect("expected notification");
+1 -6
View File
@@ -123,12 +123,7 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response_with_metadata(
request.id.clone(),
Ok(rmcp::model::CallToolResult {
content: vec![Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
Ok(rmcp::model::CallToolResult::error(vec![Content::text(DECLINED_RESPONSE)])),
request.metadata.as_ref(),
);
}