Skip to content

Commit 1f8d206

Browse files
committed
chore: upgrade rmcp to v1.5
rmcp went from 0.17.0 to 1.x with a hard cutover: most of its public model and transport types picked up `#[non_exhaustive]`, `OAuthTokenResponse` switched from `EmptyExtraTokenFields` to `VendorExtraTokenFields`, and `StreamableHttpServerConfig` gained a required `allowed_hosts` field. This commit migrates every call site in the gateway, MCP adapter, credential store, and streamable-HTTP tests onto the new constructors (`Implementation::new`, `ClientInfo::new`, `ServerInfo::new`, `InitializeResult::new`, `CallToolRequestParams::new`, `ReadResourceResult::new`, `GetPromptRequestParams::new`, `OAuthClientConfig::new`, `StoredCredentials::new`, `CallToolResult::success` / `::error`, `AuthorizationSession::for_scope_upgrade`, `StreamableHttpServerConfig::default()`), bumps the `tests/rust` crate to the same rmcp 1.5 so only one version is linked into the workspace, and resolves a handful of latent clippy warnings (`op_ref` in keychain_dpapi test, unused `use super::*` in shell_env on Windows) that became reachable via `--all-targets`. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent fb58d9c commit 1f8d206

16 files changed

Lines changed: 181 additions & 250 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ os_pipe = "1"
5858

5959
# MCP Protocol
6060
# NOTE: Never use local path dependency - E:\one-mcp\rust-sdk is for source lookup only
61-
rmcp = { version = "0.17.0", features = [
61+
rmcp = { version = "1.5", features = [
6262
"client",
6363
"server",
6464
"transport-io",

crates/mcpmux-gateway/src/mcp/handler.rs

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ impl McpMuxGatewayHandler {
8383

8484
/// Build InitializeResult with negotiated protocol version
8585
fn build_initialize_result(&self, protocol_version: ProtocolVersion) -> InitializeResult {
86-
InitializeResult {
87-
protocol_version,
88-
capabilities: self.get_info().capabilities,
89-
server_info: self.get_info().server_info,
90-
instructions: self.get_info().instructions,
91-
}
86+
let info = self.get_info();
87+
let mut result = InitializeResult::new(info.capabilities);
88+
result.protocol_version = protocol_version;
89+
result.server_info = info.server_info;
90+
result.instructions = info.instructions;
91+
result
9292
}
9393
}
9494

@@ -98,32 +98,28 @@ impl ServerHandler for McpMuxGatewayHandler {
9898

9999
// Note: get_info is called frequently, no logging needed
100100

101-
ServerInfo {
102-
protocol_version: Default::default(),
103-
capabilities: ServerCapabilities::builder()
104-
.enable_tools_with(ToolsCapability {
105-
list_changed: Some(true),
106-
})
107-
.enable_prompts_with(PromptsCapability {
108-
list_changed: Some(true),
109-
})
110-
.enable_resources_with(ResourcesCapability {
111-
subscribe: Some(false),
112-
list_changed: Some(true),
113-
})
114-
.build(),
115-
server_info: Implementation {
116-
name: "mcpmux-gateway".to_string(),
117-
version: env!("CARGO_PKG_VERSION").to_string(),
118-
title: Some("McpMux".to_string()),
119-
..Default::default()
120-
},
121-
instructions: Some(
122-
"McpMux aggregates multiple MCP servers. Use tools/prompts/resources \
123-
from your authorized backend servers."
124-
.to_string(),
125-
),
126-
}
101+
let capabilities = ServerCapabilities::builder()
102+
.enable_tools_with(ToolsCapability {
103+
list_changed: Some(true),
104+
})
105+
.enable_prompts_with(PromptsCapability {
106+
list_changed: Some(true),
107+
})
108+
.enable_resources_with(ResourcesCapability {
109+
subscribe: Some(false),
110+
list_changed: Some(true),
111+
})
112+
.build();
113+
let mut server_info = Implementation::new("mcpmux-gateway", env!("CARGO_PKG_VERSION"));
114+
server_info.title = Some("McpMux".to_string());
115+
let mut info = ServerInfo::new(capabilities);
116+
info.server_info = server_info;
117+
info.instructions = Some(
118+
"McpMux aggregates multiple MCP servers. Use tools/prompts/resources \
119+
from your authorized backend servers."
120+
.to_string(),
121+
);
122+
info
127123
}
128124

129125
async fn initialize(
@@ -321,11 +317,10 @@ impl ServerHandler for McpMuxGatewayHandler {
321317
"call_tool result"
322318
);
323319

324-
let result = CallToolResult {
325-
content,
326-
structured_content: None,
327-
is_error: Some(tool_result.is_error),
328-
meta: None,
320+
let result = if tool_result.is_error {
321+
CallToolResult::error(content)
322+
} else {
323+
CallToolResult::success(content)
329324
};
330325

331326
Ok(result)
@@ -557,7 +552,7 @@ impl ServerHandler for McpMuxGatewayHandler {
557552
.filter_map(|v| serde_json::from_value(v).ok())
558553
.collect();
559554

560-
Ok(ReadResourceResult { contents })
555+
Ok(ReadResourceResult::new(contents))
561556
}
562557

563558
/// Override on_custom_request to handle "initialize" with flexible protocol negotiation

crates/mcpmux-gateway/src/pool/credential_store.rs

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -218,37 +218,37 @@ impl CredentialStore for DatabaseCredentialStore {
218218
self.space_id, self.server_id, reg.client_id
219219
);
220220
let token_response = Self::build_token_response(access, refresh_cred.as_ref());
221-
Some(StoredCredentials {
222-
client_id: reg.client_id,
223-
token_response: Some(token_response),
224-
granted_scopes: Vec::new(),
225-
token_received_at: Some(now_epoch_secs()),
226-
})
221+
Some(StoredCredentials::new(
222+
reg.client_id,
223+
Some(token_response),
224+
Vec::new(),
225+
Some(now_epoch_secs()),
226+
))
227227
}
228228
(Some(reg), None) => {
229229
debug!(
230230
"[CredentialStore] Loaded registration (no token) for {}/{}, client_id={} - will reuse for DCR",
231231
self.space_id, self.server_id, reg.client_id
232232
);
233-
Some(StoredCredentials {
234-
client_id: reg.client_id,
235-
token_response: None,
236-
granted_scopes: Vec::new(),
237-
token_received_at: Some(now_epoch_secs()),
238-
})
233+
Some(StoredCredentials::new(
234+
reg.client_id,
235+
None,
236+
Vec::new(),
237+
Some(now_epoch_secs()),
238+
))
239239
}
240240
(None, Some(access)) => {
241241
warn!(
242242
"[CredentialStore] Token without registration for {}/{}",
243243
self.space_id, self.server_id
244244
);
245245
let token_response = Self::build_token_response(access, refresh_cred.as_ref());
246-
Some(StoredCredentials {
247-
client_id: String::new(),
248-
token_response: Some(token_response),
249-
granted_scopes: Vec::new(),
250-
token_received_at: Some(now_epoch_secs()),
251-
})
246+
Some(StoredCredentials::new(
247+
String::new(),
248+
Some(token_response),
249+
Vec::new(),
250+
Some(now_epoch_secs()),
251+
))
252252
}
253253
(None, None) => {
254254
debug!(
@@ -286,12 +286,13 @@ fn build_token_response(
286286
refresh_token: Option<String>,
287287
expires_in: Option<std::time::Duration>,
288288
) -> OAuthTokenResponse {
289-
use oauth2::{EmptyExtraTokenFields, StandardTokenResponse};
289+
use oauth2::StandardTokenResponse;
290+
use rmcp::transport::auth::VendorExtraTokenFields;
290291

291292
let mut response = StandardTokenResponse::new(
292293
AccessToken::new(access_token),
293294
BasicTokenType::Bearer,
294-
EmptyExtraTokenFields {},
295+
VendorExtraTokenFields::default(),
295296
);
296297

297298
if let Some(refresh) = refresh_token {
@@ -601,12 +602,12 @@ mod tests {
601602
Some(std::time::Duration::from_secs(3600)),
602603
);
603604

604-
let credentials = StoredCredentials {
605-
client_id: "new-client-id".to_string(),
606-
token_response: Some(token_response),
607-
granted_scopes: Vec::new(),
608-
token_received_at: None,
609-
};
605+
let credentials = StoredCredentials::new(
606+
"new-client-id".to_string(),
607+
Some(token_response),
608+
Vec::new(),
609+
None,
610+
);
610611

611612
store.save(credentials).await.unwrap();
612613

@@ -660,12 +661,12 @@ mod tests {
660661
Some(std::time::Duration::from_secs(3600)),
661662
);
662663

663-
let credentials = StoredCredentials {
664-
client_id: "client-id".to_string(),
665-
token_response: Some(token_response),
666-
granted_scopes: Vec::new(),
667-
token_received_at: None,
668-
};
664+
let credentials = StoredCredentials::new(
665+
"client-id".to_string(),
666+
Some(token_response),
667+
Vec::new(),
668+
None,
669+
);
669670

670671
store.save(credentials).await.unwrap();
671672

crates/mcpmux-gateway/src/pool/instance.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,11 @@ impl McpClientHandler {
5050
event_tx: Option<tokio::sync::broadcast::Sender<DomainEvent>>,
5151
log_manager: Option<Arc<ServerLogManager>>,
5252
) -> Self {
53+
let mut client_info =
54+
Implementation::new(format!("mcpmux-{}", server_id), env!("CARGO_PKG_VERSION"));
55+
client_info.title = Some("McpMux Gateway".to_string());
5356
Self {
54-
info: ClientInfo {
55-
protocol_version: Default::default(),
56-
capabilities: ClientCapabilities::default(),
57-
client_info: Implementation {
58-
name: format!("mcpmux-{}", server_id),
59-
version: env!("CARGO_PKG_VERSION").to_string(),
60-
title: Some("McpMux Gateway".to_string()),
61-
icons: None,
62-
website_url: None,
63-
..Default::default()
64-
},
65-
meta: None,
66-
},
57+
info: ClientInfo::new(ClientCapabilities::default(), client_info),
6758
server_id: server_id.to_string(),
6859
space_id,
6960
event_tx,

crates/mcpmux-gateway/src/pool/oauth.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,12 +1049,11 @@ impl OutboundOAuthManager {
10491049
let scopes = Self::get_scopes_from_metadata(&discovered_metadata);
10501050

10511051
// Then configure client with the existing registration
1052-
let config = rmcp::transport::auth::OAuthClientConfig {
1053-
client_id: reg.client_id.clone(),
1054-
client_secret: None,
1055-
scopes: scopes.clone(),
1056-
redirect_uri: redirect_uri.clone(),
1057-
};
1052+
let mut config = rmcp::transport::auth::OAuthClientConfig::new(
1053+
reg.client_id.clone(),
1054+
redirect_uri.clone(),
1055+
);
1056+
config.scopes = scopes.clone();
10581057

10591058
if let Err(e) = manager.configure_client(config) {
10601059
self.log(
@@ -1084,17 +1083,23 @@ impl OutboundOAuthManager {
10841083
.await
10851084
.map_err(|e| anyhow::anyhow!("Failed to get auth URL: {}", e))?;
10861085

1087-
// Create session manually
1088-
oauth_state = OAuthState::Session(rmcp::transport::auth::AuthorizationSession {
1089-
auth_manager: std::mem::replace(
1090-
manager,
1091-
rmcp::transport::auth::AuthorizationManager::new(server_url)
1092-
.await
1093-
.map_err(|e| anyhow::anyhow!("Failed: {}", e))?,
1086+
// Create session manually (reusing the existing registration).
1087+
// We already called configure_client + get_authorization_url above,
1088+
// so we use `for_scope_upgrade` to wrap the pre-computed values without
1089+
// re-registering the client via DCR.
1090+
let taken_manager = std::mem::replace(
1091+
manager,
1092+
rmcp::transport::auth::AuthorizationManager::new(server_url)
1093+
.await
1094+
.map_err(|e| anyhow::anyhow!("Failed: {}", e))?,
1095+
);
1096+
oauth_state = OAuthState::Session(
1097+
rmcp::transport::auth::AuthorizationSession::for_scope_upgrade(
1098+
taken_manager,
1099+
auth_url.clone(),
1100+
&redirect_uri,
10941101
),
1095-
auth_url: auth_url.clone(),
1096-
redirect_uri: redirect_uri.clone(),
1097-
});
1102+
);
10981103
}
10991104
(false, None) // Not a new registration, no metadata to save
11001105
} else {

crates/mcpmux-gateway/src/pool/oauth_utils.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,16 @@ pub fn convert_to_stored_metadata(metadata: &AuthorizationMetadata) -> StoredOAu
101101
/// This is used when loading saved metadata and setting it on the RMCP manager
102102
/// to bypass discovery.
103103
pub fn convert_from_stored_metadata(stored: &StoredOAuthMetadata) -> AuthorizationMetadata {
104-
AuthorizationMetadata {
105-
authorization_endpoint: stored.authorization_endpoint.clone(),
106-
token_endpoint: stored.token_endpoint.clone(),
107-
registration_endpoint: stored.registration_endpoint.clone(),
108-
issuer: stored.issuer.clone(),
109-
jwks_uri: stored.jwks_uri.clone(),
110-
scopes_supported: stored.scopes_supported.clone(),
111-
response_types_supported: stored.response_types_supported.clone(),
112-
additional_fields: stored.additional_fields.clone(),
113-
..Default::default()
114-
}
104+
let mut metadata = AuthorizationMetadata::default();
105+
metadata.authorization_endpoint = stored.authorization_endpoint.clone();
106+
metadata.token_endpoint = stored.token_endpoint.clone();
107+
metadata.registration_endpoint = stored.registration_endpoint.clone();
108+
metadata.issuer = stored.issuer.clone();
109+
metadata.jwks_uri = stored.jwks_uri.clone();
110+
metadata.scopes_supported = stored.scopes_supported.clone();
111+
metadata.response_types_supported = stored.response_types_supported.clone();
112+
metadata.additional_fields = stored.additional_fields.clone();
113+
metadata
115114
}
116115

117116
#[cfg(test)]

crates/mcpmux-gateway/src/pool/routing.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,8 @@ impl RoutingService {
283283

284284
match client_handle {
285285
Some(client) => {
286-
let params = CallToolRequestParams {
287-
name: tool_name.into(),
288-
arguments: args.as_object().cloned(),
289-
task: None,
290-
meta: None,
291-
};
286+
let mut params = CallToolRequestParams::new(tool_name.to_string());
287+
params.arguments = args.as_object().cloned();
292288

293289
// Wrap call_tool with timeout to prevent hanging
294290
let res = tokio::time::timeout(TOOL_CALL_TIMEOUT, client.call_tool(params))

crates/mcpmux-gateway/src/pool/service.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,7 @@ impl PoolService {
164164
Some(client) => {
165165
use rmcp::model::ReadResourceRequestParams;
166166

167-
let params = ReadResourceRequestParams {
168-
uri: uri.into(),
169-
meta: None,
170-
};
167+
let params = ReadResourceRequestParams::new(uri);
171168

172169
let res = client
173170
.read_resource(params)
@@ -243,11 +240,8 @@ impl PoolService {
243240
Some(client) => {
244241
use rmcp::model::GetPromptRequestParams;
245242

246-
let params = GetPromptRequestParams {
247-
name: prompt_name.into(),
248-
arguments,
249-
meta: None,
250-
};
243+
let mut params = GetPromptRequestParams::new(prompt_name);
244+
params.arguments = arguments;
251245

252246
let res = client
253247
.get_prompt(params)

0 commit comments

Comments
 (0)