diff --git a/Cargo.lock b/Cargo.lock index 06aa290e74..4d141b57c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -940,7 +940,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -1043,7 +1043,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1795,7 +1795,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1968,7 +1968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2951,7 +2951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.4", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3433,7 +3433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19753d40da53d0ec41604750eeb969097a90fb2d7f7992730d904541c04e2c19" dependencies = [ "bstr", - "hashbrown 0.16.1", + "hashbrown 0.17.0", ] [[package]] @@ -4572,7 +4572,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -4831,7 +4831,7 @@ dependencies = [ "socket2 0.6.3", "widestring", "windows-registry", - "windows-result 0.4.1", + "windows-result", "windows-sys 0.61.2", ] @@ -4868,7 +4868,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4920,7 +4920,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5540,18 +5540,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nix" version = "0.31.2" @@ -5607,7 +5595,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5951,7 +5939,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6294,16 +6282,16 @@ dependencies = [ [[package]] name = "process-wrap" -version = "8.2.1" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ef4f2f0422f23a82ec9f628ea2acd12871c81a9362b02c43c1aa86acfc3ba1" +checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" dependencies = [ "futures", "indexmap 2.14.0", - "nix 0.30.1", + "nix", "tokio", "tracing", - "windows 0.61.3", + "windows 0.62.2", ] [[package]] @@ -6749,7 +6737,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "winreg 0.50.0", ] @@ -6794,7 +6782,7 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots", ] @@ -6830,12 +6818,14 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-rustls 0.26.4", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams 0.5.0", "web-sys", ] @@ -6861,9 +6851,9 @@ dependencies = [ [[package]] name = "rmcp" -version = "0.10.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b18323edc657390a6ed4d7a9110b0dec2dc3ed128eb2a123edfbafabdbddc5" +checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" dependencies = [ "async-trait", "base64 0.22.1", @@ -6874,7 +6864,7 @@ dependencies = [ "pastey", "pin-project-lite", "process-wrap", - "reqwest 0.12.28", + "reqwest 0.13.4", "rmcp-macros", "schemars 1.2.1", "serde", @@ -6890,11 +6880,11 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "0.10.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75d0a62676bf8c8003c4e3c348e2ceb6a7b3e48323681aaf177fdccdac2ce50" +checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", "serde_json", @@ -7054,7 +7044,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7134,7 +7124,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7184,7 +7174,7 @@ dependencies = [ "libc", "log", "memchr", - "nix 0.31.2", + "nix", "radix_trie", "unicode-segmentation", "unicode-width 0.2.2", @@ -8072,7 +8062,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -8135,7 +8125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9116,6 +9106,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmparser" version = "0.244.0" @@ -9224,7 +9227,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -9244,38 +9247,16 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - [[package]] name = "windows" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", ] [[package]] @@ -9284,20 +9265,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.62.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-core", ] [[package]] @@ -9308,20 +9276,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement 0.60.2", "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -9330,9 +9287,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "windows-core", + "windows-link", + "windows-threading", ] [[package]] @@ -9379,36 +9336,20 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - [[package]] name = "windows-numerics" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", + "windows-core", + "windows-link", ] [[package]] @@ -9417,18 +9358,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -9437,16 +9369,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -9455,7 +9378,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -9500,7 +9423,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -9549,22 +9472,13 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-threading" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f6aaba955c..6b689ee4e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,9 +120,8 @@ whoami = "2.1.0" fnv_rs = "0.4.3" merge = { version = "0.2", features = ["derive"] } hex = "0.4.3" -rmcp = { version = "0.10.0", features = [ +rmcp = { version = "1.0.0", features = [ "client", - "transport-sse-client-reqwest", "transport-child-process", "transport-streamable-http-client-reqwest", "auth", diff --git a/crates/forge_infra/src/auth/mcp_token_storage.rs b/crates/forge_infra/src/auth/mcp_token_storage.rs index 544833fe40..50fde300d9 100644 --- a/crates/forge_infra/src/auth/mcp_token_storage.rs +++ b/crates/forge_infra/src/auth/mcp_token_storage.rs @@ -98,11 +98,11 @@ impl CredentialStore for McpTokenStorage { if let Some(entry) = store.get(&self.server_url) { use oauth2::basic::BasicTokenType; use oauth2::{AccessToken, RefreshToken}; - use rmcp::transport::auth::OAuthTokenResponse; + use rmcp::transport::auth::{OAuthTokenResponse, VendorExtraTokenFields}; let access_token = AccessToken::new(entry.tokens.access_token.clone()); let token_type = BasicTokenType::Bearer; - let extra_fields = oauth2::EmptyExtraTokenFields {}; + let extra_fields = VendorExtraTokenFields::default(); let mut token_response = OAuthTokenResponse::new(access_token, token_type, extra_fields); @@ -127,14 +127,16 @@ impl CredentialStore for McpTokenStorage { } } - Ok(Some(StoredCredentials { - client_id: entry + Ok(Some(StoredCredentials::new( + entry .client_registration .as_ref() .map(|r| r.client_id.clone()) .unwrap_or_default(), - token_response: Some(token_response), - })) + Some(token_response), + vec![], + None, + ))) } else { Ok(None) } diff --git a/crates/forge_infra/src/mcp_client.rs b/crates/forge_infra/src/mcp_client.rs index 23e246a1f3..1c0b5db47a 100644 --- a/crates/forge_infra/src/mcp_client.rs +++ b/crates/forge_infra/src/mcp_client.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use std::collections::BTreeMap; use std::future::Future; -use std::str::FromStr; use std::sync::{Arc, OnceLock, RwLock}; use backon::{ExponentialBuilder, Retryable}; @@ -10,13 +9,11 @@ use forge_app::McpClientInfra; use forge_domain::{ Environment, Image, McpHttpServer, McpServerConfig, ToolDefinition, ToolName, ToolOutput, }; -use reqwest::Client; -use reqwest::header::{HeaderName, HeaderValue}; -use rmcp::model::{CallToolRequestParam, ClientInfo, Implementation, InitializeRequestParam}; +use http::{HeaderName, HeaderValue}; +use rmcp::model::{CallToolRequestParams, ClientInfo, Implementation, InitializeRequestParams}; use rmcp::service::RunningService; -use rmcp::transport::sse_client::SseClientConfig; use rmcp::transport::streamable_http_client::StreamableHttpClientTransportConfig; -use rmcp::transport::{SseClientTransport, StreamableHttpClientTransport, TokioChildProcess}; +use rmcp::transport::{StreamableHttpClientTransport, TokioChildProcess}; use rmcp::{RoleClient, ServiceExt}; use schemars::Schema; use serde_json::Value; @@ -30,12 +27,11 @@ const VERSION: &str = match option_env!("APP_VERSION") { None => env!("CARGO_PKG_VERSION"), }; -type RmcpClient = RunningService; +type RmcpClient = RunningService; #[derive(Clone)] pub struct ForgeMcpClient { client: Arc>>>, - http_client: Arc, config: McpServerConfig, env_vars: BTreeMap, environment: Environment, @@ -43,49 +39,13 @@ pub struct ForgeMcpClient { } impl ForgeMcpClient { - /// Build a reqwest client with default headers from the MCP server config. - fn build_http_client(http: &McpHttpServer) -> anyhow::Result { - let mut header_map = reqwest::header::HeaderMap::new(); - for (key, value) in &http.headers { - if let Ok(name) = HeaderName::from_str(key) - && let Ok(val) = HeaderValue::from_str(value) - { - header_map.insert(name, val); - } - } - - Ok(Client::builder().default_headers(header_map).build()?) - } - pub fn new( config: McpServerConfig, env_vars: &BTreeMap, environment: Environment, ) -> Self { - // Try to resolve config early so we can extract headers for the HTTP client. - // If resolution fails, fall back to a plain client (headers will be missing - // but the error will surface when create_connection is called). - let resolved = resolve_http_templates( - match &config { - McpServerConfig::Http(http) => http.clone(), - McpServerConfig::Stdio(_) => McpHttpServer { - url: String::new(), - headers: BTreeMap::new(), - timeout: None, - disable: false, - oauth: forge_domain::McpOAuthSetting::default(), - }, - }, - env_vars, - ); - - let http_client = resolved - .and_then(|http| Self::build_http_client(&http)) - .unwrap_or_default(); - Self { client: Default::default(), - http_client: Arc::new(http_client), config, env_vars: env_vars.clone(), environment, @@ -107,17 +67,7 @@ impl ForgeMcpClient { } fn client_info(&self) -> ClientInfo { - ClientInfo { - protocol_version: Default::default(), - capabilities: Default::default(), - client_info: Implementation { - name: "Forge".to_string(), - version: VERSION.to_string(), - icons: None, - title: None, - website_url: None, - }, - } + ClientInfo::new(Default::default(), Implementation::new("Forge", VERSION)) } /// Connects to the MCP server. If `force` is true, it will reconnect even @@ -219,23 +169,10 @@ impl ForgeMcpClient { &self, http: &McpHttpServer, ) -> anyhow::Result { - // Try HTTP first, fall back to SSE if it fails - let client = self.reqwest_client(); - let transport = StreamableHttpClientTransport::with_client( - client.as_ref().clone(), - StreamableHttpClientTransportConfig::with_uri(http.url.clone()), - ); - match self.client_info().serve(transport).await { - Ok(client) => Ok(client), - Err(_e) => { - let transport = SseClientTransport::start_with_client( - client.as_ref().clone(), - SseClientConfig { sse_endpoint: http.url.clone().into(), ..Default::default() }, - ) - .await?; - Ok(self.client_info().serve(transport).await?) - } - } + let config = StreamableHttpClientTransportConfig::with_uri(http.url.clone()) + .custom_headers(build_header_map(&http.headers)); + let transport = StreamableHttpClientTransport::from_config(config); + Ok(self.client_info().serve(transport).await?) } /// Create an OAuth-enabled connection using rmcp's OAuth support. @@ -368,10 +305,12 @@ impl ForgeMcpClient { { use rmcp::transport::auth::CredentialStore; let save_store = McpTokenStorage::new(http.url.clone(), self.environment.clone()); - let stored = rmcp::transport::auth::StoredCredentials { - client_id: credentials.0, - token_response: credentials.1, - }; + let stored = rmcp::transport::auth::StoredCredentials::new( + credentials.0, + credentials.1, + vec![], + None, + ); save_store .save(stored) .await @@ -397,12 +336,9 @@ impl ForgeMcpClient { http: &McpHttpServer, token: &str, ) -> anyhow::Result> { - let client = self.reqwest_client(); - let transport = StreamableHttpClientTransport::with_client( - client.as_ref().clone(), - StreamableHttpClientTransportConfig::with_uri(http.url.clone()).auth_header(token), - ); - + let config = + StreamableHttpClientTransportConfig::with_uri(http.url.clone()).auth_header(token); + let transport = StreamableHttpClientTransport::from_config(config); Ok(Arc::new(self.client_info().serve(transport).await?)) } @@ -495,14 +431,6 @@ impl ForgeMcpClient { Ok((code, state)) } - fn reqwest_client(&self) -> Arc { - // Reuse the cached HTTP client (with pre-configured default headers) - // to prevent file descriptor leaks. Each reqwest::Client manages its - // own connection pool, so creating new clients for each connection - // leads to "Too many open files" errors. - self.http_client.clone() - } - async fn list(&self) -> anyhow::Result> { let client = self.connect().await?; let tools = client.list_tools(None).await?; @@ -527,13 +455,12 @@ impl ForgeMcpClient { async fn call(&self, tool_name: &ToolName, input: &Value) -> anyhow::Result { let client = self.connect().await?; let result = client - .call_tool(CallToolRequestParam { - name: Cow::Owned(tool_name.to_string()), - arguments: if let Value::Object(args) = input { - Some(args.clone()) - } else { - None - }, + .call_tool({ + let mut params = CallToolRequestParams::new(Cow::Owned(tool_name.to_string())); + if let Value::Object(args) = input { + params = params.with_arguments(args.clone()); + } + params }) .await?; @@ -627,6 +554,19 @@ fn resolve_http_templates( Ok(http) } +fn build_header_map( + headers: &BTreeMap, +) -> std::collections::HashMap { + headers + .iter() + .filter_map(|(k, v)| { + let name = k.parse::().ok()?; + let val = v.parse::().ok()?; + Some((name, val)) + }) + .collect() +} + /// Trigger OAuth authentication for a specific MCP server URL. /// /// Runs the full OAuth flow: metadata discovery, dynamic registration, @@ -734,10 +674,8 @@ pub async fn mcp_auth(server_url: &str, env: &Environment) -> anyhow::Result<()> .map_err(|e| anyhow::anyhow!("Failed to get credentials: {}", e))?; let save_store = McpTokenStorage::new(server_url.to_string(), env.clone()); - let stored = rmcp::transport::auth::StoredCredentials { - client_id: credentials.0, - token_response: credentials.1, - }; + let stored = + rmcp::transport::auth::StoredCredentials::new(credentials.0, credentials.1, vec![], None); save_store .save(stored) .await