Skip to content

Commit 782a7d2

Browse files
authored
Merge branch 'modelcontextprotocol:main' into sep-1699
2 parents 9b6f017 + 61f7b7b commit 782a7d2

File tree

5 files changed

+124
-29
lines changed

5 files changed

+124
-29
lines changed

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ An official Rust Model Context Protocol SDK implementation with tokio async runt
1212

1313
This repository contains the following crates:
1414

15-
- [rmcp](crates/rmcp): The core crate providing the RMCP protocol implementation (If you want to get more information, please visit [rmcp](crates/rmcp/README.md))
16-
- [rmcp-macros](crates/rmcp-macros): A procedural macro crate for generating RMCP tool implementations (If you want to get more information, please visit [rmcp-macros](crates/rmcp-macros/README.md))
15+
- [rmcp](crates/rmcp): The core crate providing the RMCP protocol implementation - see [rmcp](crates/rmcp/README.md)
16+
- [rmcp-macros](crates/rmcp-macros): A procedural macro crate for generating RMCP tool implementations - see [rmcp-macros](crates/rmcp-macros/README.md)
1717

1818
## Usage
1919

@@ -25,14 +25,15 @@ rmcp = { version = "0.8.0", features = ["server"] }
2525
rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" }
2626
```
2727
### Third Dependencies
28-
Basic dependencies:
29-
- [tokio required](https://github.com/tokio-rs/tokio)
30-
- [serde required](https://github.com/serde-rs/serde)
31-
Json Schema generation(Must follow the 2020-12 version):
32-
- [shemars required](https://github.com/GREsau/schemars)
3328

29+
Basic dependencies:
30+
- [tokio](https://github.com/tokio-rs/tokio)
31+
- [serde](https://github.com/serde-rs/serde)
32+
Json Schema generation (version 2020-12):
33+
- [schemars](https://github.com/GREsau/schemars)
3434

3535
### Build a Client
36+
3637
<details>
3738
<summary>Start a client</summary>
3839

@@ -108,16 +109,16 @@ let quit_reason = server.cancel().await?;
108109

109110
## Examples
110111

111-
See [examples](examples/README.md)
112+
See [examples](examples/README.md).
112113

113114
## OAuth Support
114115

115-
See [oauth_support](docs/OAUTH_SUPPORT.md) for details.
116+
See [Oauth_support](docs/OAUTH_SUPPORT.md) for details.
116117

117118
## Related Resources
118119

119-
- [MCP Specification](https://spec.modelcontextprotocol.io/specification/2024-11-05/)
120-
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts)
120+
- [MCP Specification](https://modelcontextprotocol.io/specification/2025-11-25)
121+
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts)
121122

122123
## Related Projects
123124

crates/rmcp/src/transport/auth.rs

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -612,13 +612,16 @@ impl AuthorizationManager {
612612
let credentials = stored.and_then(|s| s.token_response);
613613

614614
if let Some(creds) = credentials.as_ref() {
615-
let expires_in = creds.expires_in().unwrap_or(Duration::from_secs(0));
616-
if expires_in <= Duration::from_secs(0) {
617-
tracing::info!("Access token expired, refreshing.");
618-
619-
let new_creds = self.refresh_token().await?;
620-
tracing::info!("Refreshed access token.");
621-
return Ok(new_creds.access_token().secret().to_string());
615+
// check token expiry if we have a refresh token or an expiry time
616+
if creds.refresh_token().is_some() || creds.expires_in().is_some() {
617+
let expires_in = creds.expires_in().unwrap_or(Duration::from_secs(0));
618+
if expires_in <= Duration::from_secs(0) {
619+
tracing::info!("Access token expired, refreshing.");
620+
621+
let new_creds = self.refresh_token().await?;
622+
tracing::info!("Refreshed access token.");
623+
return Ok(new_creds.access_token().secret().to_string());
624+
}
622625
}
623626

624627
Ok(creds.access_token().secret().to_string())
@@ -684,16 +687,40 @@ impl AuthorizationManager {
684687
}
685688
}
686689

690+
/// Generate discovery endpoint URLs following the priority order in spec-2025-11-25 4.3 "Authorization Server Metadata Discovery".
691+
fn generate_discovery_urls(base_url: &Url) -> Vec<Url> {
692+
let mut candidates = Vec::new();
693+
let path = base_url.path();
694+
let trimmed = path.trim_start_matches('/').trim_end_matches('/');
695+
let mut push_candidate = |discovery_path: String| {
696+
let mut discovery_url = base_url.clone();
697+
discovery_url.set_query(None);
698+
discovery_url.set_fragment(None);
699+
discovery_url.set_path(&discovery_path);
700+
candidates.push(discovery_url);
701+
};
702+
if trimmed.is_empty() {
703+
// No path components: try OAuth first, then OpenID Connect
704+
push_candidate("/.well-known/oauth-authorization-server".to_string());
705+
push_candidate("/.well-known/openid-configuration".to_string());
706+
} else {
707+
// Path components present: follow spec priority order
708+
// 1. OAuth 2.0 with path insertion
709+
push_candidate(format!("/.well-known/oauth-authorization-server/{trimmed}"));
710+
// 2. OpenID Connect with path insertion
711+
push_candidate(format!("/.well-known/openid-configuration/{trimmed}"));
712+
// 3. OpenID Connect with path appending
713+
push_candidate(format!("/{trimmed}/.well-known/openid-configuration"));
714+
}
715+
716+
candidates
717+
}
718+
687719
async fn try_discover_oauth_server(
688720
&self,
689721
base_url: &Url,
690722
) -> Result<Option<AuthorizationMetadata>, AuthError> {
691-
for candidate_path in Self::well_known_paths(base_url.path(), "oauth-authorization-server")
692-
{
693-
let mut discovery_url = base_url.clone();
694-
discovery_url.set_query(None);
695-
discovery_url.set_fragment(None);
696-
discovery_url.set_path(&candidate_path);
723+
for discovery_url in Self::generate_discovery_urls(base_url) {
697724
if let Some(metadata) = self.fetch_authorization_metadata(&discovery_url).await? {
698725
return Ok(Some(metadata));
699726
}
@@ -1457,4 +1484,71 @@ mod tests {
14571484
]
14581485
);
14591486
}
1487+
1488+
#[test]
1489+
fn generate_discovery_urls() {
1490+
// Test root URL (no path components): OAuth first, then OpenID Connect
1491+
let base_url = Url::parse("https://auth.example.com").unwrap();
1492+
let urls = AuthorizationManager::generate_discovery_urls(&base_url);
1493+
assert_eq!(urls.len(), 2);
1494+
assert_eq!(
1495+
urls[0].as_str(),
1496+
"https://auth.example.com/.well-known/oauth-authorization-server"
1497+
);
1498+
assert_eq!(
1499+
urls[1].as_str(),
1500+
"https://auth.example.com/.well-known/openid-configuration"
1501+
);
1502+
1503+
// Test URL with single path segment: follow spec priority order
1504+
let base_url = Url::parse("https://auth.example.com/tenant1").unwrap();
1505+
let urls = AuthorizationManager::generate_discovery_urls(&base_url);
1506+
assert_eq!(urls.len(), 3);
1507+
assert_eq!(
1508+
urls[0].as_str(),
1509+
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1"
1510+
);
1511+
assert_eq!(
1512+
urls[1].as_str(),
1513+
"https://auth.example.com/.well-known/openid-configuration/tenant1"
1514+
);
1515+
assert_eq!(
1516+
urls[2].as_str(),
1517+
"https://auth.example.com/tenant1/.well-known/openid-configuration"
1518+
);
1519+
1520+
// Test URL with path and trailing slash
1521+
let base_url = Url::parse("https://auth.example.com/v1/mcp/").unwrap();
1522+
let urls = AuthorizationManager::generate_discovery_urls(&base_url);
1523+
assert_eq!(urls.len(), 3);
1524+
assert_eq!(
1525+
urls[0].as_str(),
1526+
"https://auth.example.com/.well-known/oauth-authorization-server/v1/mcp"
1527+
);
1528+
assert_eq!(
1529+
urls[1].as_str(),
1530+
"https://auth.example.com/.well-known/openid-configuration/v1/mcp"
1531+
);
1532+
assert_eq!(
1533+
urls[2].as_str(),
1534+
"https://auth.example.com/v1/mcp/.well-known/openid-configuration"
1535+
);
1536+
1537+
// Test URL with multiple path segments
1538+
let base_url = Url::parse("https://auth.example.com/tenant1/subtenant").unwrap();
1539+
let urls = AuthorizationManager::generate_discovery_urls(&base_url);
1540+
assert_eq!(urls.len(), 3);
1541+
assert_eq!(
1542+
urls[0].as_str(),
1543+
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1/subtenant"
1544+
);
1545+
assert_eq!(
1546+
urls[1].as_str(),
1547+
"https://auth.example.com/.well-known/openid-configuration/tenant1/subtenant"
1548+
);
1549+
assert_eq!(
1550+
urls[2].as_str(),
1551+
"https://auth.example.com/tenant1/subtenant/.well-known/openid-configuration"
1552+
);
1553+
}
14601554
}

docs/OAUTH_SUPPORT.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Model Context Protocol OAuth Authorization
22

3-
This document describes the OAuth 2.1 authorization implementation for Model Context Protocol (MCP), following the [MCP 2025-03-26 Authorization Specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization/).
3+
This document describes the OAuth 2.1 authorization implementation for Model Context Protocol (MCP), following the [MCP 2025-03-26 Authorization Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization/).
44

55
## Features
66

@@ -117,7 +117,7 @@ If you encounter authorization issues, check the following:
117117

118118
## References
119119

120-
- [MCP Authorization Specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/authorization/)
120+
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization/)
121121
- [OAuth 2.1 Specification Draft](https://oauth.net/2.1/)
122122
- [RFC 8414: OAuth 2.0 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414)
123123
- [RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591)

docs/readme/README.zh-cn.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ let quit_reason = server.cancel().await?;
106106

107107
## 相关资源
108108

109-
- [MCP Specification](https://spec.modelcontextprotocol.io/specification/2024-11-05/)
110-
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts)
109+
- [MCP Specification](https://modelcontextprotocol.io/specification/2025-11-25)
110+
- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts)
111111

112112
## 相关项目
113113
- [containerd-mcp-server](https://github.com/jokemanfire/mcp-containerd) - 基于 containerd 实现的 MCP 服务

examples/servers/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ reqwest = { version = "0.12", features = ["json"] }
3939
chrono = "0.4"
4040
uuid = { version = "1.6", features = ["v4", "serde"] }
4141
serde_urlencoded = "0.7"
42-
askama = { version = "0.14" }
42+
askama = { version = "0.15" }
4343
tower-http = { version = "0.6", features = ["cors"] }
4444
hyper = { version = "1" }
4545
hyper-util = { version = "0", features = ["server"] }

0 commit comments

Comments
 (0)