Upgrade to rmcp 3.0 (#10789)
This commit is contained in:
Generated
+35
-28
@@ -201,7 +201,7 @@ version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -212,7 +212,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1207,6 +1207,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b25655df2c3cdd83c5e5b293b88acd880332b2ddadd7c30ac43144fdc0033da9"
|
||||
|
||||
[[package]]
|
||||
name = "base64-simd"
|
||||
version = "0.8.0"
|
||||
@@ -2429,7 +2435,7 @@ version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3692,7 +3698,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4011,7 +4017,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4986,7 +4992,7 @@ dependencies = [
|
||||
"rcgen",
|
||||
"regex",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"rubato",
|
||||
"rustls",
|
||||
"schemars 1.2.1",
|
||||
@@ -5082,7 +5088,7 @@ dependencies = [
|
||||
"rand 0.10.2",
|
||||
"regex",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"rustyline",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5142,7 +5148,7 @@ dependencies = [
|
||||
"minijinja",
|
||||
"regex",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"safemlx",
|
||||
"safemlx-lm",
|
||||
"safemlx-lm-utils",
|
||||
@@ -5170,7 +5176,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"process-wrap",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"schemars 1.2.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5199,7 +5205,7 @@ dependencies = [
|
||||
"rand 0.10.2",
|
||||
"regex",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum 0.28.0",
|
||||
@@ -5230,7 +5236,7 @@ dependencies = [
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"sec1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5256,7 +5262,7 @@ dependencies = [
|
||||
"futures",
|
||||
"goose-providers",
|
||||
"goose-sdk-types",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
@@ -5290,7 +5296,7 @@ dependencies = [
|
||||
"axum",
|
||||
"env-lock",
|
||||
"opentelemetry 0.32.0",
|
||||
"rmcp 2.2.0",
|
||||
"rmcp 3.0.0",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
@@ -7110,7 +7116,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7281,7 +7287,7 @@ version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
"getrandom 0.2.17",
|
||||
"http 1.4.2",
|
||||
@@ -8607,7 +8613,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9099,11 +9105,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rmcp"
|
||||
version = "2.2.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14db48ee17a9ba61810ab1a9c1beb7d06d8136ae39ac25a1137f10d357af01af"
|
||||
checksum = "fcd2b6dd3b18129368955f32661a7718969e8c152c7d8866434c09cf15a512e0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.23.0",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"futures",
|
||||
@@ -9118,7 +9125,7 @@ dependencies = [
|
||||
"process-wrap",
|
||||
"rand 0.10.2",
|
||||
"reqwest 0.13.4",
|
||||
"rmcp-macros 2.2.0",
|
||||
"rmcp-macros 3.0.0",
|
||||
"schemars 1.2.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9148,9 +9155,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rmcp-macros"
|
||||
version = "2.2.0"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783d787bf21813b285f13019adc49e11af501c658890c1e519f31f937c68b7e3"
|
||||
checksum = "e1aa4b9345795260a43fc23d6d05e096407c8b953903f673af7c4404b49fb2d6"
|
||||
dependencies = [
|
||||
"darling 0.23.0",
|
||||
"proc-macro2",
|
||||
@@ -9254,7 +9261,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9312,7 +9319,7 @@ dependencies = [
|
||||
"security-framework 3.7.0",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10323,7 +10330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10605,7 +10612,7 @@ dependencies = [
|
||||
"cfg-if 1.0.4",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11444,10 +11451,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.3",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -13241,7 +13248,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ uninlined_format_args = "allow"
|
||||
string_slice = "warn"
|
||||
|
||||
[workspace.dependencies]
|
||||
rmcp = { version = "2", default-features = false, features = ["schemars", "auth"] }
|
||||
rmcp = { version = "3.0.0", default-features = false, features = ["schemars", "auth"] }
|
||||
agent-client-protocol-schema = { version = "1.1", default-features = false, features = ["unstable"] }
|
||||
agent-client-protocol = { version = "1.0", default-features = false }
|
||||
agent-client-protocol-http = { version = "1.0", default-features = false, features = ["server", "unstable_cancel_request"] }
|
||||
|
||||
@@ -52,6 +52,7 @@ impl McpClientTrait for MockClient {
|
||||
resources: vec![],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,6 +91,7 @@ impl McpClientTrait for MockClient {
|
||||
tools: rmcp_tools,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -120,6 +122,7 @@ impl McpClientTrait for MockClient {
|
||||
prompts: vec![],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ use rmcp::{
|
||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||
model::{
|
||||
CallToolResult, ContentBlock, ErrorCode, ErrorData, Implementation, InitializeResult,
|
||||
ListResourcesResult, Meta, PaginatedRequestParams, ReadResourceRequestParams,
|
||||
ReadResourceResult, Resource, ResourceContents, ServerCapabilities, ServerInfo,
|
||||
ListResourcesResult, MetaObject, PaginatedRequestParams, ReadResourceRequestParams,
|
||||
ReadResourceResponse, ReadResourceResult, Resource, ResourceContents, ServerCapabilities,
|
||||
ServerInfo,
|
||||
},
|
||||
service::RequestContext,
|
||||
tool, tool_handler, tool_router, RoleServer, ServerHandler,
|
||||
@@ -18,8 +19,8 @@ use std::path::PathBuf;
|
||||
const MCP_APPS_MIME_TYPE: &str = "text/html;profile=mcp-app";
|
||||
|
||||
/// Build a Meta object with `_meta.ui.resourceUri` for linking a tool to a UI resource.
|
||||
fn ui_resource_meta(uri: &str) -> Meta {
|
||||
let mut meta = Meta::new();
|
||||
fn ui_resource_meta(uri: &str) -> MetaObject {
|
||||
let mut meta = MetaObject::new();
|
||||
meta.0
|
||||
.insert("ui".to_string(), json!({ "resourceUri": uri }));
|
||||
meta
|
||||
@@ -694,6 +695,7 @@ impl ServerHandler for AutoVisualiserRouter {
|
||||
resources,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -701,10 +703,10 @@ impl ServerHandler for AutoVisualiserRouter {
|
||||
&self,
|
||||
params: ReadResourceRequestParams,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, ErrorData> {
|
||||
) -> Result<ReadResourceResponse, ErrorData> {
|
||||
let html = self.get_template_html(¶ms.uri)?;
|
||||
|
||||
let mut meta = Meta::new();
|
||||
let mut meta = MetaObject::new();
|
||||
meta.0
|
||||
.insert("ui".to_string(), json!({ "prefersBorder": true }));
|
||||
|
||||
@@ -715,7 +717,7 @@ impl ServerHandler for AutoVisualiserRouter {
|
||||
meta: Some(meta),
|
||||
};
|
||||
|
||||
Ok(ReadResourceResult::new(vec![resource_contents]))
|
||||
Ok(ReadResourceResult::new(vec![resource_contents]).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ use rmcp::{
|
||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||
model::{
|
||||
CallToolResult, ContentBlock, ErrorCode, ErrorData, Implementation, InitializeResult,
|
||||
ListResourcesResult, PaginatedRequestParams, ReadResourceRequestParams, ReadResourceResult,
|
||||
Resource, ResourceContents, ServerCapabilities, ServerInfo,
|
||||
ListResourcesResult, PaginatedRequestParams, ReadResourceRequestParams,
|
||||
ReadResourceResponse, ReadResourceResult, Resource, ResourceContents, ServerCapabilities,
|
||||
ServerInfo,
|
||||
},
|
||||
schemars::JsonSchema,
|
||||
service::RequestContext,
|
||||
@@ -1649,6 +1650,7 @@ impl ServerHandler for ComputerControllerServer {
|
||||
resources,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1656,7 +1658,7 @@ impl ServerHandler for ComputerControllerServer {
|
||||
&self,
|
||||
params: ReadResourceRequestParams,
|
||||
_context: RequestContext<RoleServer>,
|
||||
) -> Result<ReadResourceResult, ErrorData> {
|
||||
) -> Result<ReadResourceResponse, ErrorData> {
|
||||
let active_resources = self.active_resources.lock().unwrap();
|
||||
let resource = active_resources.get(¶ms.uri).ok_or_else(|| {
|
||||
ErrorData::new(
|
||||
@@ -1667,6 +1669,6 @@ impl ServerHandler for ComputerControllerServer {
|
||||
})?;
|
||||
|
||||
// Clone the resource to return
|
||||
Ok(ReadResourceResult::new(vec![resource.clone()]))
|
||||
Ok(ReadResourceResult::new(vec![resource.clone()]).into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use indoc::formatdoc;
|
||||
use rmcp::{
|
||||
handler::server::{router::tool::ToolRouter, wrapper::Parameters},
|
||||
model::{
|
||||
CallToolResult, ContentBlock, ErrorCode, ErrorData, Implementation, InitializeResult, Meta,
|
||||
ServerCapabilities, ServerInfo,
|
||||
CallToolResult, ContentBlock, ErrorCode, ErrorData, Implementation, InitializeResult,
|
||||
MetaObject, ServerCapabilities, ServerInfo,
|
||||
},
|
||||
schemars::JsonSchema,
|
||||
service::RequestContext,
|
||||
@@ -20,7 +20,7 @@ use std::{
|
||||
|
||||
const WORKING_DIR_HEADER: &str = "agent-working-dir";
|
||||
|
||||
fn extract_working_dir_from_meta(meta: &Meta) -> Option<PathBuf> {
|
||||
fn extract_working_dir_from_meta(meta: &MetaObject) -> Option<PathBuf> {
|
||||
meta.0
|
||||
.get(WORKING_DIR_HEADER)
|
||||
.and_then(|v| v.as_str())
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use goose_test_support::mcp::McpFixtureServer;
|
||||
use rmcp::service::ServiceExt;
|
||||
use rmcp::transport::streamable_http_server::{
|
||||
session::local::LocalSessionManager, StreamableHttpServerConfig, StreamableHttpService,
|
||||
};
|
||||
@@ -7,7 +6,7 @@ use rmcp::transport::streamable_http_server::{
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let service = StreamableHttpService::new(
|
||||
|| Ok(McpFixtureServer::new().into_dyn()),
|
||||
|| Ok(McpFixtureServer::new()),
|
||||
LocalSessionManager::default().into(),
|
||||
StreamableHttpServerConfig::default(),
|
||||
);
|
||||
|
||||
@@ -1,93 +1,17 @@
|
||||
use crate::session::SESSION_ID_HEADER;
|
||||
use crate::ExpectedSessionId;
|
||||
use rmcp::model::{
|
||||
Annotations, CallToolResult, ClientNotification, ClientRequest, ContentBlock, ErrorCode,
|
||||
Implementation, InitializeResult, Meta, ProtocolVersion, Role, ServerCapabilities, ServerInfo,
|
||||
TextContent,
|
||||
Annotations, CallToolResult, ContentBlock, Implementation, InitializeResult, ProtocolVersion,
|
||||
Role, ServerCapabilities, ServerInfo, TextContent,
|
||||
};
|
||||
use rmcp::service::{DynService, NotificationContext, RequestContext, ServiceExt, ServiceRole};
|
||||
use rmcp::transport::streamable_http_server::{
|
||||
session::local::LocalSessionManager, StreamableHttpServerConfig, StreamableHttpService,
|
||||
};
|
||||
use rmcp::{
|
||||
tool, tool_handler, tool_router, ErrorData as McpError, RoleServer, ServerHandler, Service,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use rmcp::{tool, tool_handler, tool_router, ErrorData as McpError, ServerHandler};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
pub const FAKE_CODE: &str = "test-uuid-12345-67890";
|
||||
|
||||
pub const TEST_IMAGE_B64: &str = include_str!("test_assets/test_image.b64").trim_ascii_end();
|
||||
|
||||
pub trait HasMeta {
|
||||
fn meta(&self) -> &Meta;
|
||||
}
|
||||
|
||||
impl<R: ServiceRole> HasMeta for RequestContext<R> {
|
||||
fn meta(&self) -> &Meta {
|
||||
&self.meta
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ServiceRole> HasMeta for NotificationContext<R> {
|
||||
fn meta(&self) -> &Meta {
|
||||
&self.meta
|
||||
}
|
||||
}
|
||||
|
||||
struct ValidatingService<S> {
|
||||
inner: S,
|
||||
expected_session_id: Arc<dyn ExpectedSessionId>,
|
||||
}
|
||||
|
||||
impl<S> ValidatingService<S> {
|
||||
fn new(inner: S, expected_session_id: Arc<dyn ExpectedSessionId>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
expected_session_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn validate<C: HasMeta>(&self, context: &C) -> Result<(), McpError> {
|
||||
let actual = context
|
||||
.meta()
|
||||
.0
|
||||
.get(SESSION_ID_HEADER)
|
||||
.and_then(|v| v.as_str());
|
||||
self.expected_session_id
|
||||
.validate(actual)
|
||||
.map_err(|e| McpError::new(ErrorCode::INVALID_REQUEST, e, None))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Service<RoleServer>> Service<RoleServer> for ValidatingService<S> {
|
||||
async fn handle_request(
|
||||
&self,
|
||||
request: ClientRequest,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<rmcp::model::ServerResult, McpError> {
|
||||
if !matches!(request, ClientRequest::InitializeRequest(_)) {
|
||||
self.validate(&context)?;
|
||||
}
|
||||
self.inner.handle_request(request, context).await
|
||||
}
|
||||
|
||||
async fn handle_notification(
|
||||
&self,
|
||||
notification: ClientNotification,
|
||||
context: NotificationContext<RoleServer>,
|
||||
) -> Result<(), McpError> {
|
||||
if !matches!(notification, ClientNotification::InitializedNotification(_)) {
|
||||
self.validate(&context).ok();
|
||||
}
|
||||
self.inner.handle_notification(notification, context).await
|
||||
}
|
||||
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
self.inner.get_info()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct McpFixtureServer;
|
||||
|
||||
@@ -129,7 +53,7 @@ impl McpFixtureServer {
|
||||
impl ServerHandler for McpFixtureServer {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
InitializeResult::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_protocol_version(ProtocolVersion::V_2025_03_26)
|
||||
.with_protocol_version(ProtocolVersion::LATEST)
|
||||
.with_server_info(Implementation::new("mcp-fixture", "1.0.0"))
|
||||
.with_instructions("Test server with code, image, and audience-scoped content tools.")
|
||||
}
|
||||
@@ -146,17 +70,9 @@ impl Drop for McpFixture {
|
||||
}
|
||||
}
|
||||
|
||||
type McpServiceFactory =
|
||||
Box<dyn Fn() -> Result<Box<dyn DynService<RoleServer>>, std::io::Error> + Send + Sync>;
|
||||
|
||||
impl McpFixture {
|
||||
pub async fn new(expected_session_id: Arc<dyn ExpectedSessionId>) -> Self {
|
||||
let service_factory: McpServiceFactory = Box::new(move || {
|
||||
Ok(
|
||||
ValidatingService::new(McpFixtureServer::new(), expected_session_id.clone())
|
||||
.into_dyn(),
|
||||
)
|
||||
});
|
||||
pub async fn new() -> Self {
|
||||
let service_factory = || Ok::<_, std::io::Error>(McpFixtureServer::new());
|
||||
|
||||
let service = StreamableHttpService::new(
|
||||
service_factory,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use rmcp::{
|
||||
model::{CallToolResult, Meta},
|
||||
model::{CallToolResult, MetaObject},
|
||||
object,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ pub trait AcpAwareToolMeta {
|
||||
|
||||
impl AcpAwareToolMeta for CallToolResult {
|
||||
fn with_acp_aware_meta(self) -> Self {
|
||||
self.with_meta(Some(Meta(object!({ACP_AWARE_META_KEY: true}))))
|
||||
self.with_meta(Some(MetaObject(object!({ACP_AWARE_META_KEY: true}))))
|
||||
}
|
||||
|
||||
fn is_acp_aware(&self) -> bool {
|
||||
|
||||
@@ -50,7 +50,7 @@ use crate::prompt_template;
|
||||
use crate::subprocess::configure_subprocess;
|
||||
use rmcp::model::{
|
||||
CallToolRequestParams, CallToolResult, ContentBlock, ErrorCode, ErrorData, GetPromptResult,
|
||||
Meta, Prompt, Resource, ResourceContents, ServerInfo, Tool,
|
||||
MetaObject, Prompt, Resource, ResourceContents, ServerInfo, Tool,
|
||||
};
|
||||
use rmcp::transport::auth::{AuthClient, CredentialStore};
|
||||
use schemars::_private::NoSerialize;
|
||||
@@ -364,7 +364,7 @@ fn insert_trusted_tool_update_meta(
|
||||
TRUSTED_TOOL_UPDATE_META_KEY.to_string(),
|
||||
Value::Object(trusted_meta),
|
||||
);
|
||||
result.meta = Some(Meta(meta_map));
|
||||
result.meta = Some(MetaObject(meta_map));
|
||||
}
|
||||
|
||||
fn is_unprefixed_extension(config: &ExtensionConfig) -> bool {
|
||||
@@ -1436,7 +1436,7 @@ impl ExtensionManager {
|
||||
);
|
||||
|
||||
tool.name = public_name.into();
|
||||
tool.meta = Some(rmcp::model::Meta(meta_map));
|
||||
tool.meta = Some(rmcp::model::MetaObject(meta_map));
|
||||
|
||||
let mut schema = (*tool.input_schema).clone();
|
||||
if super::tool_schema_normalize::normalize_input_schema(
|
||||
@@ -2213,7 +2213,7 @@ mod tests {
|
||||
"Render a chart".to_string(),
|
||||
Arc::new(json!({}).as_object().unwrap().clone()),
|
||||
);
|
||||
t.meta = Some(Meta(
|
||||
t.meta = Some(MetaObject(
|
||||
json!({ "ui": { "resourceUri": "ui://autovisualiser/chart" } })
|
||||
.as_object()
|
||||
.unwrap()
|
||||
@@ -2224,6 +2224,7 @@ mod tests {
|
||||
],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2741,6 +2742,7 @@ mod tests {
|
||||
],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2887,7 +2889,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_remove_untrusted_mcp_app_meta_strips_spoofed_payload() {
|
||||
let mut result = CallToolResult::success(vec![]);
|
||||
result.meta = Some(Meta(
|
||||
result.meta = Some(MetaObject(
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"goose": {
|
||||
"mcpApp": {
|
||||
|
||||
@@ -2,25 +2,25 @@ use crate::action_required_manager::{ActionRequiredManager, ElicitationOutcome};
|
||||
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};
|
||||
#[expect(deprecated)]
|
||||
use rmcp::model::{
|
||||
CreateElicitationRequestParams, CreateElicitationResult, ListRootsResult,
|
||||
LoggingMessageNotification, Root, SamplingMessageContent,
|
||||
};
|
||||
/// MCP client implementation for Goose
|
||||
#[expect(deprecated)]
|
||||
use rmcp::model::{CreateMessageRequestParams, CreateMessageResult, SamplingMessage};
|
||||
#[expect(deprecated)]
|
||||
use rmcp::model::{
|
||||
ElicitationAction, ErrorCode, ExtensionCapabilities, Extensions, JsonObject, Meta,
|
||||
ElicitRequestParams, ElicitResult, ListRootsResult, LoggingMessageNotification, Root,
|
||||
SamplingMessageContentBlock,
|
||||
};
|
||||
use rmcp::model::{
|
||||
ElicitationAction, ErrorCode, ExtensionCapabilities, Extensions, JsonObject, MetaObject,
|
||||
};
|
||||
use rmcp::{
|
||||
model::{
|
||||
CallToolRequestParams, CallToolResult, CancelledNotificationParam, ClientCapabilities,
|
||||
ClientInfo, ClientRequest, GetPromptRequestParams, GetPromptResult, Implementation,
|
||||
InitializeRequestParams, InitializeResult, ListPromptsResult, ListResourcesResult,
|
||||
ListToolsResult, Notification, PaginatedRequestParams, ProtocolVersion,
|
||||
ReadResourceRequestParams, ReadResourceResult, Request, RequestId, RequestOptionalParam,
|
||||
Role, ServerNotification, ServerResult,
|
||||
ListToolsResult, Notification, PaginatedRequestParams, ReadResourceRequestParams,
|
||||
ReadResourceResult, Request, RequestId, RequestOptionalParam, Role, ServerNotification,
|
||||
ServerResult,
|
||||
},
|
||||
service::{
|
||||
ClientInitializeError, PeerRequestOptions, RequestContext, RequestHandle, RunningService,
|
||||
@@ -230,7 +230,7 @@ impl GooseClient {
|
||||
}
|
||||
|
||||
fn session_id_from_extensions(extensions: &Extensions) -> Option<String> {
|
||||
let meta = extensions.get::<Meta>()?;
|
||||
let meta = extensions.get::<MetaObject>()?;
|
||||
meta.0
|
||||
.iter()
|
||||
.find(|(key, _)| key.eq_ignore_ascii_case(SESSION_ID_HEADER))
|
||||
@@ -239,7 +239,7 @@ impl GooseClient {
|
||||
}
|
||||
|
||||
fn tool_call_request_id_from_extensions(extensions: &Extensions) -> Option<String> {
|
||||
let meta = extensions.get::<Meta>()?;
|
||||
let meta = extensions.get::<MetaObject>()?;
|
||||
meta.0
|
||||
.iter()
|
||||
.find(|(key, _)| key.eq_ignore_ascii_case(TOOL_CALL_REQUEST_ID_HEADER))
|
||||
@@ -448,18 +448,18 @@ impl ClientHandler for GooseClient {
|
||||
if let Some(content) = response.content.first() {
|
||||
match content {
|
||||
crate::conversation::message::MessageContent::Text(text) => {
|
||||
SamplingMessageContent::text(&text.text)
|
||||
SamplingMessageContentBlock::text(&text.text)
|
||||
}
|
||||
crate::conversation::message::MessageContent::Image(img) => {
|
||||
SamplingMessageContent::Image(rmcp::model::ImageContent::new(
|
||||
SamplingMessageContentBlock::Image(rmcp::model::ImageContent::new(
|
||||
img.data.clone(),
|
||||
img.mime_type.clone(),
|
||||
))
|
||||
}
|
||||
_ => SamplingMessageContent::text(""),
|
||||
_ => SamplingMessageContentBlock::text(""),
|
||||
}
|
||||
} else {
|
||||
SamplingMessageContent::text("")
|
||||
SamplingMessageContentBlock::text("")
|
||||
},
|
||||
),
|
||||
usage.model,
|
||||
@@ -467,12 +467,11 @@ impl ClientHandler for GooseClient {
|
||||
.with_stop_reason(CreateMessageResult::STOP_REASON_END_TURN))
|
||||
}
|
||||
|
||||
#[expect(deprecated)]
|
||||
async fn create_elicitation(
|
||||
&self,
|
||||
request: CreateElicitationRequestParams,
|
||||
request: ElicitRequestParams,
|
||||
context: RequestContext<RoleClient>,
|
||||
) -> Result<CreateElicitationResult, ErrorData> {
|
||||
) -> Result<ElicitResult, ErrorData> {
|
||||
let session_id = self
|
||||
.resolve_session_id(&context.extensions)
|
||||
.await
|
||||
@@ -487,7 +486,7 @@ impl ClientHandler for GooseClient {
|
||||
self.resolve_tool_call_request_id(&session_id, &context.extensions)?;
|
||||
|
||||
let (message, schema_value) = match &request {
|
||||
CreateElicitationRequestParams::FormElicitationParams {
|
||||
ElicitRequestParams::FormElicitationParams {
|
||||
message,
|
||||
requested_schema,
|
||||
..
|
||||
@@ -501,7 +500,7 @@ impl ClientHandler for GooseClient {
|
||||
})?;
|
||||
(message.clone(), schema_value)
|
||||
}
|
||||
CreateElicitationRequestParams::UrlElicitationParams { message, url, .. } => {
|
||||
ElicitRequestParams::UrlElicitationParams { message, url, .. } => {
|
||||
(message.clone(), serde_json::json!({ "url": url }))
|
||||
}
|
||||
_ => (String::new(), serde_json::json!({})),
|
||||
@@ -518,14 +517,10 @@ impl ClientHandler for GooseClient {
|
||||
.await
|
||||
.map(|response| match response {
|
||||
ElicitationOutcome::Accept(user_data) => {
|
||||
CreateElicitationResult::new(ElicitationAction::Accept).with_content(user_data)
|
||||
}
|
||||
ElicitationOutcome::Decline => {
|
||||
CreateElicitationResult::new(ElicitationAction::Decline)
|
||||
}
|
||||
ElicitationOutcome::Cancel => {
|
||||
CreateElicitationResult::new(ElicitationAction::Cancel)
|
||||
ElicitResult::new(ElicitationAction::Accept).with_content(user_data)
|
||||
}
|
||||
ElicitationOutcome::Decline => ElicitResult::new(ElicitationAction::Decline),
|
||||
ElicitationOutcome::Cancel => ElicitResult::new(ElicitationAction::Cancel),
|
||||
})
|
||||
.map_err(|e| {
|
||||
ErrorData::new(
|
||||
@@ -536,11 +531,11 @@ impl ClientHandler for GooseClient {
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(deprecated)]
|
||||
fn get_info(&self) -> ClientInfo {
|
||||
let extensions = self.resolved_extensions();
|
||||
|
||||
InitializeRequestParams::new(
|
||||
#[expect(deprecated)]
|
||||
ClientCapabilities::builder()
|
||||
.enable_roots()
|
||||
.enable_extensions_with(extensions)
|
||||
@@ -549,7 +544,6 @@ impl ClientHandler for GooseClient {
|
||||
.build(),
|
||||
self.resolved_client_info(),
|
||||
)
|
||||
.with_protocol_version(ProtocolVersion::V_2025_03_26)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,7 +612,16 @@ impl McpClient {
|
||||
);
|
||||
let client: rmcp::service::RunningService<rmcp::RoleClient, GooseClient> =
|
||||
client.serve(transport).await?;
|
||||
let server_info = client.peer_info().map(|info| (*info).clone());
|
||||
let server_info = client.peer_info().map(|info| {
|
||||
let mut initialize_result = InitializeResult::new(info.capabilities.clone())
|
||||
.with_protocol_version(info.protocol_version.clone());
|
||||
if let Some(server_info) = &info.server_info {
|
||||
initialize_result = initialize_result.with_server_info(server_info.clone());
|
||||
}
|
||||
initialize_result.instructions = info.instructions.clone();
|
||||
initialize_result.meta = info.meta.clone();
|
||||
initialize_result
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
client: Mutex::new(client),
|
||||
@@ -897,7 +900,7 @@ fn inject_session_context_into_extensions(
|
||||
let working_dir = working_dir.filter(|dir| !dir.is_empty());
|
||||
let tool_call_request_id = tool_call_request_id.filter(|id| !id.is_empty());
|
||||
let mut meta_map = extensions
|
||||
.get::<Meta>()
|
||||
.get::<MetaObject>()
|
||||
.map(|meta| meta.0.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -929,7 +932,7 @@ fn inject_session_context_into_extensions(
|
||||
);
|
||||
}
|
||||
|
||||
extensions.insert(Meta(meta_map));
|
||||
extensions.insert(MetaObject(meta_map));
|
||||
extensions
|
||||
}
|
||||
|
||||
@@ -1203,7 +1206,7 @@ mod tests {
|
||||
let session_id = "test-session-id";
|
||||
let mut extensions = Extensions::new();
|
||||
extensions.insert(
|
||||
serde_json::from_value::<Meta>(json!({
|
||||
serde_json::from_value::<MetaObject>(json!({
|
||||
"Goose-Session-Id": "old-session-id",
|
||||
"other-key": "preserve-me"
|
||||
}))
|
||||
@@ -1214,7 +1217,7 @@ mod tests {
|
||||
let request = inject_session_context_into_request(request, Some(session_id), None, None);
|
||||
let extensions = request_extensions(&request).expect("request should have extensions");
|
||||
let meta = extensions
|
||||
.get::<Meta>()
|
||||
.get::<MetaObject>()
|
||||
.expect("extensions should contain meta");
|
||||
|
||||
assert_eq!(
|
||||
@@ -1239,7 +1242,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let mcp_meta = extensions.get::<Meta>().unwrap();
|
||||
let mcp_meta = extensions.get::<MetaObject>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mcp_meta.0,
|
||||
@@ -1282,7 +1285,7 @@ mod tests {
|
||||
|
||||
let mut extensions = Extensions::new();
|
||||
extensions.insert(
|
||||
from_value::<Meta>(json!({
|
||||
from_value::<MetaObject>(json!({
|
||||
SESSION_ID_HEADER: "old-session-1",
|
||||
"Agent-Session-Id": "old-session-2",
|
||||
"other-key": "preserve-me"
|
||||
@@ -1291,7 +1294,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let extensions = inject_session_context_into_extensions(extensions, session_id, None, None);
|
||||
let mcp_meta = extensions.get::<Meta>().unwrap();
|
||||
let mcp_meta = extensions.get::<MetaObject>().unwrap();
|
||||
|
||||
assert_eq!(&mcp_meta.0, expected_meta.as_object().unwrap());
|
||||
}
|
||||
@@ -1308,7 +1311,7 @@ mod tests {
|
||||
Some(tool_call_request_id),
|
||||
);
|
||||
let call_meta = request_extensions(&call_request)
|
||||
.and_then(|extensions| extensions.get::<Meta>())
|
||||
.and_then(|extensions| extensions.get::<MetaObject>())
|
||||
.expect("call request should have meta");
|
||||
assert_eq!(
|
||||
call_meta.0.get(TOOL_CALL_REQUEST_ID_HEADER),
|
||||
@@ -1322,7 +1325,7 @@ mod tests {
|
||||
Some(tool_call_request_id),
|
||||
);
|
||||
let tools_meta = request_extensions(&tools_request)
|
||||
.and_then(|extensions| extensions.get::<Meta>())
|
||||
.and_then(|extensions| extensions.get::<MetaObject>())
|
||||
.expect("list tools request should have meta");
|
||||
assert!(!tools_meta.0.contains_key(TOOL_CALL_REQUEST_ID_HEADER));
|
||||
}
|
||||
|
||||
@@ -233,6 +233,7 @@ impl McpClientTrait for AnalyzeClient {
|
||||
tools: vec![tool],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ use crate::providers::base::Provider;
|
||||
use async_trait::async_trait;
|
||||
use rmcp::model::{
|
||||
CallToolResult, ContentBlock, Implementation, InitializeResult, JsonObject,
|
||||
ListResourcesResult, ListToolsResult, Meta, ReadResourceResult, Resource, ResourceContents,
|
||||
ServerCapabilities, Tool as McpTool,
|
||||
ListResourcesResult, ListToolsResult, MetaObject, ReadResourceResult, Resource,
|
||||
ResourceContents, ServerCapabilities, Tool as McpTool,
|
||||
};
|
||||
use schemars::{schema_for, JsonSchema};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -544,6 +544,7 @@ impl McpClientTrait for AppsManagerClient {
|
||||
tools,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -587,7 +588,7 @@ impl McpClientTrait for AppsManagerClient {
|
||||
for name in app_names {
|
||||
if let Ok(app) = self.load_app(&name) {
|
||||
let meta = if let Some(ref window_props) = app.window_props {
|
||||
let mut meta_obj = Meta::new();
|
||||
let mut meta_obj = MetaObject::new();
|
||||
meta_obj.insert(
|
||||
"window".to_string(),
|
||||
json!({
|
||||
@@ -616,6 +617,7 @@ impl McpClientTrait for AppsManagerClient {
|
||||
resources,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -287,6 +287,7 @@ impl McpClientTrait for ChatRecallClient {
|
||||
tools: Self::get_tools(),
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -560,6 +560,7 @@ impl McpClientTrait for CodeExecutionClient {
|
||||
meta: None,
|
||||
next_cursor: None,
|
||||
tools,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ impl McpClientTrait for DeveloperClient {
|
||||
tools: Self::get_tools(),
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -408,6 +408,7 @@ impl McpClientTrait for ExtensionManagerClient {
|
||||
tools: self.get_tools().await,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -261,7 +261,9 @@ impl PlatformExtensionContext {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.meta = Some(rmcp::model::Meta(meta_value.as_object().unwrap().clone()));
|
||||
result.meta = Some(rmcp::model::MetaObject(
|
||||
meta_value.as_object().unwrap().clone(),
|
||||
));
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -626,6 +626,7 @@ impl McpClientTrait for OrchestratorClient {
|
||||
tools,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ impl McpClientTrait for SummarizeClient {
|
||||
tools: Self::get_tools(),
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use async_trait::async_trait;
|
||||
use goose_sdk_types::custom_requests::{SourceEntry, SourceType};
|
||||
use rmcp::model::{
|
||||
CallToolResult, ContentBlock, Implementation, InitializeResult, JsonObject, ListToolsResult,
|
||||
Meta, ServerCapabilities, ServerNotification, Tool,
|
||||
MetaObject, ServerCapabilities, ServerNotification, Tool,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
@@ -888,7 +888,7 @@ impl SummonClient {
|
||||
|
||||
if is_session_id(name) {
|
||||
let task_result = self.handle_load_task_result(name, cancel, peek).await?;
|
||||
let mut meta = Meta::new();
|
||||
let mut meta = MetaObject::new();
|
||||
meta.0.insert(
|
||||
"subagent_session_id".to_string(),
|
||||
serde_json::Value::String(name.to_string()),
|
||||
@@ -1270,7 +1270,7 @@ impl SummonClient {
|
||||
|
||||
if params.r#async {
|
||||
let (content, task_id) = self.handle_async_delegate(session_id, params).await?;
|
||||
let mut meta = Meta::new();
|
||||
let mut meta = MetaObject::new();
|
||||
meta.0.insert(
|
||||
"subagent_session_id".to_string(),
|
||||
serde_json::Value::String(task_id),
|
||||
@@ -1326,7 +1326,7 @@ impl SummonClient {
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut meta = Meta::new();
|
||||
let mut meta = MetaObject::new();
|
||||
meta.0.insert(
|
||||
"subagent_session_id".to_string(),
|
||||
serde_json::Value::String(subagent_session_id),
|
||||
@@ -1953,6 +1953,7 @@ impl McpClientTrait for SummonClient {
|
||||
tools,
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ impl McpClientTrait for TodoClient {
|
||||
tools: Self::get_tools(),
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ impl McpClientTrait for TomClient {
|
||||
tools: vec![],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1379,7 +1379,7 @@ mod tests {
|
||||
let agent = crate::agents::Agent::new();
|
||||
|
||||
let registry_tool = Tool::new("test_tool", "a test tool", object!({ "type": "object" }))
|
||||
.with_meta(rmcp::model::Meta(
|
||||
.with_meta(rmcp::model::MetaObject(
|
||||
serde_json::json!({ "ui": { "visibility": ["model"] } })
|
||||
.as_object()
|
||||
.unwrap()
|
||||
@@ -1471,7 +1471,7 @@ mod tests {
|
||||
let mut tool = Tool::new("test_tool", "a test tool", object!({ "type": "object" }));
|
||||
if let Some(v) = meta_json {
|
||||
let obj = v.as_object().unwrap().clone();
|
||||
tool = tool.with_meta(rmcp::model::Meta(obj));
|
||||
tool = tool.with_meta(rmcp::model::MetaObject(obj));
|
||||
}
|
||||
tool
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use axum::routing::get;
|
||||
use axum::Router;
|
||||
use minijinja::render;
|
||||
use oauth2::TokenResponse;
|
||||
use rmcp::transport::auth::{CredentialStore, OAuthState, StoredCredentials};
|
||||
use rmcp::transport::auth::{AuthorizationRequest, CredentialStore, OAuthState, StoredCredentials};
|
||||
use rmcp::transport::AuthorizationManager;
|
||||
use serde::Deserialize;
|
||||
use std::net::SocketAddr;
|
||||
@@ -146,11 +146,10 @@ pub async fn oauth_flow(
|
||||
|
||||
let redirect_uri = format!("http://127.0.0.1:{}/oauth_callback", used_addr.port());
|
||||
oauth_state
|
||||
.start_authorization_with_metadata_url(
|
||||
&[],
|
||||
redirect_uri.as_str(),
|
||||
Some("goose"),
|
||||
Some(CLIENT_METADATA_URL),
|
||||
.start_authorization(
|
||||
AuthorizationRequest::new(redirect_uri)
|
||||
.with_client_name("goose")
|
||||
.with_client_metadata_url(CLIENT_METADATA_URL),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ impl TestProvider {
|
||||
..
|
||||
}) => {
|
||||
result.is_error = None;
|
||||
result.result_type = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ impl McpClientTrait for SkillsClient {
|
||||
tools: vec![tool],
|
||||
next_cursor: None,
|
||||
meta: None,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ pub async fn run_config_mcp<C: Connection>() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let prompt = "Use the get_code tool and output only its result.";
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
|
||||
let config_yaml = format!(
|
||||
"GOOSE_MODEL: {TEST_MODEL}\nGOOSE_PROVIDER: openai\nextensions:\n mcp-fixture:\n enabled: true\n type: streamable_http\n name: mcp-fixture\n description: MCP fixture\n uri: \"{}\"\n",
|
||||
@@ -460,7 +460,7 @@ pub async fn run_load_mode<C: Connection>() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let prompt = "Use the get_code tool and output only its result.";
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
|
||||
let config_yaml = format!(
|
||||
"GOOSE_MODEL: {TEST_MODEL}\nGOOSE_PROVIDER: openai\nextensions:\n mcp-fixture:\n enabled: true\n type: streamable_http\n name: mcp-fixture\n description: MCP fixture\n uri: \"{}\"\n",
|
||||
@@ -560,7 +560,7 @@ pub async fn run_load_model<C: Connection>() {
|
||||
pub async fn run_load_session_mcp<C: Connection>() {
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let prompt = "Use the get_code tool and output only its result.";
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let mcp_url = mcp.url.clone();
|
||||
|
||||
// Two rounds of tool call + tool result: one for new session, one for loaded session.
|
||||
@@ -751,7 +751,7 @@ async fn run_mode_set_impl<C: Connection>(via: SetModeVia) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let prompt = "Use the get_code tool and output only its result.";
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
|
||||
let config_yaml = format!(
|
||||
"GOOSE_MODEL: {TEST_MODEL}\nGOOSE_PROVIDER: openai\nextensions:\n mcp-fixture:\n enabled: true\n type: streamable_http\n name: mcp-fixture\n description: MCP fixture\n uri: \"{}\"\n",
|
||||
@@ -1101,7 +1101,7 @@ pub async fn run_permission_persistence<C: Connection>() {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let prompt = "Use the get_code tool and output only its result.";
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let openai = OpenAiFixture::new(
|
||||
vec![
|
||||
(
|
||||
@@ -1194,7 +1194,7 @@ pub async fn run_prompt_codemode<C: Connection>() {
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let prompt =
|
||||
"Search for getCode and write tools. Use them to save the code to /tmp/result.txt.";
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let openai = OpenAiFixture::new(
|
||||
vec![
|
||||
(
|
||||
@@ -1241,7 +1241,7 @@ pub async fn run_prompt_codemode<C: Connection>() {
|
||||
|
||||
pub async fn run_prompt_image<C: Connection>() {
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let openai = OpenAiFixture::new(
|
||||
vec![
|
||||
(
|
||||
@@ -1318,7 +1318,7 @@ pub async fn run_prompt_image_attachment<C: Connection>() {
|
||||
|
||||
pub async fn run_prompt_mcp<C: Connection>() {
|
||||
let expected_session_id = C::expected_session_id();
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let openai = OpenAiFixture::new(
|
||||
vec![
|
||||
(
|
||||
|
||||
@@ -2925,7 +2925,7 @@ mod tests {
|
||||
use goose_providers::conversation::token_usage::{ProviderUsage, Usage};
|
||||
use goose_providers::errors::ProviderError;
|
||||
use goose_providers::model::ModelConfig;
|
||||
use goose_test_support::{IgnoreSessionId, McpFixture};
|
||||
use goose_test_support::McpFixture;
|
||||
use rmcp::model::{CallToolRequestParams, Tool};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
@@ -2994,7 +2994,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn live_tool_result_projects_user_content_but_persists_canonical_result() -> Result<()>
|
||||
{
|
||||
let mcp = McpFixture::new(Arc::new(IgnoreSessionId)).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
let extension =
|
||||
ExtensionConfig::streamable_http("mcp-fixture", &mcp.url, "MCP fixture", 30_u64);
|
||||
let temp_dir = tempfile::tempdir()?;
|
||||
|
||||
@@ -151,15 +151,6 @@ enum TestMode {
|
||||
],
|
||||
vec!["GITHUB_PERSONAL_ACCESS_TOKEN"]
|
||||
)]
|
||||
#[test_case(
|
||||
vec!["uvx", "mcp-server-fetch"],
|
||||
vec![
|
||||
CallToolRequestParams::new("fetch").with_arguments(object!({
|
||||
"url": "https://example.com",
|
||||
}))
|
||||
],
|
||||
vec![]
|
||||
)]
|
||||
#[test_case(
|
||||
vec!["uv", "run", "--with", "fastmcp==2.14.4", "fastmcp", "run", "tests/fastmcp_test_server.py"],
|
||||
vec![
|
||||
@@ -180,6 +171,7 @@ async fn test_replayed_session(
|
||||
("GOOSE_MCP_CLIENT_VERSION", Some("0.0.0")),
|
||||
("GOOSE_PROVIDER", Some("openai")),
|
||||
("GOOSE_MODEL", Some("gpt-4o")),
|
||||
("GOOSE_WORKING_DIR", Some("/tmp/goose_test")),
|
||||
]);
|
||||
|
||||
// Setup test file for developer extension tests
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,14 +3,14 @@
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "successfully downloaded text file (SHA: de9bdde7f260549bf3a083651842f30ab29cf4e9)"
|
||||
"text": "successfully downloaded text file (SHA: 94c3fce7d230011289145bbdd9d45714280792ba)"
|
||||
},
|
||||
{
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
"uri": "repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md",
|
||||
"mimeType": "text/plain; charset=utf-8",
|
||||
"text": "<div align=\"center\">\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n<p align=\"center\">\n <a href=\"https://opensource.org/licenses/Apache-2.0\">\n <img src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\">\n </a>\n <a href=\"https://discord.gg/7GaTvbDwga\">\n <img src=\"https://img.shields.io/discord/1287729918100246654?logo=discord&logoColor=white&label=Join+Us&color=blueviolet\" alt=\"Discord\">\n </a>\n <a href=\"https://github.com/block/goose/actions/workflows/ci.yml\">\n <img src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\">\n </a>\n</p>\n</div>\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n[](https://youtu.be/D-DpDunrbpo)\n\n# Quick Links\n- [Quickstart](https://goose-docs.ai/docs/quickstart)\n- [Installation](https://goose-docs.ai/docs/getting-started/installation)\n- [Tutorials](https://goose-docs.ai/docs/category/tutorials)\n- [Documentation](https://goose-docs.ai/docs/category/getting-started)\n\n\n# a little goose humor 🦢\n\n> Why did the developer choose goose as their AI agent?\n> \n> Because it always helps them \"migrate\" their code to production! 🚀\n\n# goose around with us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@goose-oss)\n- [LinkedIn](https://www.linkedin.com/company/goose-oss)\n- [Twitter/X](https://x.com/goose_oss)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"
|
||||
"mimeType": "text/html; charset=utf-8",
|
||||
"text": "<div align=\"center\">\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n<p align=\"center\">\n <a href=\"https://opensource.org/licenses/Apache-2.0\">\n <img src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\">\n </a>\n <a href=\"https://discord.gg/7GaTvbDwga\">\n <img src=\"https://img.shields.io/discord/1287729918100246654?logo=discord&logoColor=white&label=Join+Us&color=blueviolet\" alt=\"Discord\">\n </a>\n <a href=\"https://github.com/block/goose/actions/workflows/ci.yml\">\n <img src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\">\n </a>\n</p>\n</div>\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n[](https://youtu.be/D-DpDunrbpo)\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# a little goose humor 🦢\n\n> Why did the developer choose goose as their AI agent?\n> \n> Because it always helps them \"migrate\" their code to production! 🚀\n\n# goose around with us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@goose-oss)\n- [LinkedIn](https://www.linkedin.com/company/goose-oss)\n- [Twitter/X](https://x.com/goose_oss)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+12
-8
File diff suppressed because one or more lines are too long
+2
-2
@@ -31,9 +31,9 @@
|
||||
}
|
||||
],
|
||||
"structuredContent": {
|
||||
"temperature": 33,
|
||||
"conditions": "Cloudy",
|
||||
"humidity": 82,
|
||||
"temperature": 33
|
||||
"humidity": 82
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+15
-4
@@ -1,4 +1,8 @@
|
||||
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"elicitation":{},"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}},"roots": {},"sampling":{}},"clientInfo":{"name":"goose-desktop","version":"0.0.0"}}}
|
||||
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}},"roots":{},"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose-desktop","version":"0.0.0"}}}
|
||||
STDERR: warning: The `native-tls` setting is deprecated and will be removed in a future release. Use `system-certs` instead.
|
||||
STDERR: /Users/jackamadeo/.cache/uv/archive-v0/qVA6xesA-RY1n0SG/lib/python3.14/site-packages/fastmcp/server/auth/providers/jwt.py:10: AuthlibDeprecationWarning: authlib.jose module is deprecated, please use joserfc instead.
|
||||
STDERR: It will be compatible before version 2.0.0.
|
||||
STDERR: from authlib.jose import JsonWebKey, JsonWebToken
|
||||
STDERR:
|
||||
STDERR:
|
||||
STDERR: ╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
@@ -8,6 +12,7 @@ STDERR: │ ▄▀▀ ▄▀█ █▀▀ ▀█▀ █
|
||||
STDERR: │ █▀ █▀█ ▄▄█ █ █ ▀ █ █▄▄ █▀▀ │
|
||||
STDERR: │ │
|
||||
STDERR: │ │
|
||||
STDERR: │ │
|
||||
STDERR: │ FastMCP 2.14.4 │
|
||||
STDERR: │ https://gofastmcp.com │
|
||||
STDERR: │ │
|
||||
@@ -19,13 +24,19 @@ STDERR: ╭───────────────────────
|
||||
STDERR: │ ✨ FastMCP 3.0 is coming! │
|
||||
STDERR: │ Pin `fastmcp < 3` in production, then upgrade when you're ready. │
|
||||
STDERR: ╰──────────────────────────────────────────────────────────────────────────────╯
|
||||
STDERR: ╭──────────────────────────────────────────────────────────────────────────────╮
|
||||
STDERR: │ 🎉 Update available: 3.4.5 │
|
||||
STDERR: │ Run: pip install --upgrade fastmcp │
|
||||
STDERR: ╰──────────────────────────────────────────────────────────────────────────────╯
|
||||
STDERR:
|
||||
STDERR:
|
||||
STDERR: [01/23/26 15:56:13] INFO Starting MCP server 'mymcp' with server.py:2506
|
||||
STDERR: [07/29/26 10:52:55] INFO Starting MCP server 'mymcp' with server.py:2506
|
||||
STDERR: transport 'stdio'
|
||||
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":true},"tasks":{"list":{},"cancel":{},"requests":{"tools":{"call":{}},"prompts":{"get":{}},"resources":{"read":{}}}}},"serverInfo":{"name":"mymcp","version":"2.14.4"}}}
|
||||
STDERR: /Users/jackamadeo/.cache/uv/archive-v0/qVA6xesA-RY1n0SG/lib/python3.14/site-packages/redis/asyncio/connection.py:1628: DeprecationWarning: FakeConnection is deprecated. Use FakeAsyncRedisConnection instead
|
||||
STDERR: return self.connection_class(**self.connection_kwargs)
|
||||
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-11-25","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":true},"tasks":{"list":{},"cancel":{},"requests":{"tools":{"call":{}},"prompts":{"get":{}},"resources":{"read":{}}}}},"serverInfo":{"name":"mymcp","version":"2.14.4"}}}
|
||||
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
|
||||
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"agent-session-id":"test-session-id","progressToken":0}}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"divide","description":"Divide two numbers","inputSchema":{"type":"object","properties":{"dividend":{"type":"number"},"divisor":{"type":"number"}},"required":["dividend","divisor"]}}]}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"divide","description":"Divide two numbers and return the result.","inputSchema":{"properties":{"dividend":{"description":"Dividend/numerator of the division.","type":"number"},"divisor":{"description":"Divisor/denominator of the division.","type":"number"}},"required":["dividend","divisor"],"type":"object"},"outputSchema":{"description":"Generic wrapper for non-object return types.","properties":{"result":{"type":"number"}},"required":["result"],"type":"object","x-fastmcp-wrap-result":true},"_meta":{"_fastmcp":{"tags":[]}}}]}}
|
||||
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":"divide","arguments":{"dividend":10,"divisor":2}}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"5.0"}],"structuredContent":{"result":5.0},"isError":false}}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"elicitation":{},"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}},"roots": {},"sampling":{}},"clientInfo":{"name":"goose-desktop","version":"0.0.0"}}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"mcp-fetch","version":"1.25.0"}}}
|
||||
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
|
||||
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"agent-session-id":"test-session-id","progressToken":0}}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"fetch","description":"Fetch a URL","inputSchema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}]}}
|
||||
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":"fetch","arguments":{"url":"https://example.com"}}}
|
||||
STDOUT: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"Contents of https://example.com/:\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)"}],"isError":false}}
|
||||
@@ -1,11 +0,0 @@
|
||||
[
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Contents of https://example.com/:\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)"
|
||||
}
|
||||
],
|
||||
"isError": false
|
||||
}
|
||||
]
|
||||
@@ -220,7 +220,7 @@ impl ProviderFixture {
|
||||
let guard = env_lock::lock_env(env_vars);
|
||||
|
||||
let expected_session_id = (config.expected_session_id)();
|
||||
let mcp = McpFixture::new(expected_session_id.clone()).await;
|
||||
let mcp = McpFixture::new().await;
|
||||
|
||||
let mcp_extension =
|
||||
ExtensionConfig::streamable_http("mcp-fixture", &mcp.url, "MCP fixture", 30_u64);
|
||||
|
||||
Reference in New Issue
Block a user