Skip to content

Commit db54d13

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/workspace-root-routing
# Conflicts: # crates/mcpmux-gateway/src/oauth/dcr.rs
2 parents 443649e + 661f162 commit db54d13

2 files changed

Lines changed: 94 additions & 5 deletions

File tree

.github/workflows/e2e-desktop.yml

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,54 @@ jobs:
131131
if: matrix.os == 'windows-latest'
132132
shell: pwsh
133133
run: |
134-
# Tauri uses the WebView2 runtime, not Edge directly.
135-
# msedgedriver must match the WebView2 runtime version, not the Edge browser version.
134+
# Tauri uses the WebView2 runtime, not Edge directly, so msedgedriver must be
135+
# compatible with the WebView2 runtime (matching the major version is sufficient).
136+
# Microsoft does not publish a driver for every exact runtime build, so try the
137+
# exact version first, then fall back to the latest driver for the same major
138+
# version, then LATEST_STABLE. This keeps the job from breaking whenever the
139+
# runner's WebView2 build outpaces the published Edge drivers.
140+
$ErrorActionPreference = 'Stop'
136141
$wv2Key = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}'
137142
if (-not (Test-Path $wv2Key)) { $wv2Key = 'HKLM:\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}' }
138143
$wv2Version = (Get-ItemProperty $wv2Key).pv
139-
Write-Host "WebView2 runtime version: $wv2Version"
140-
$driverUrl = "https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/$wv2Version/edgedriver_win64.zip"
144+
$wv2Major = $wv2Version.Split('.')[0]
145+
Write-Host "WebView2 runtime version: $wv2Version (major $wv2Major)"
146+
147+
# Microsoft locked down the old msedgewebdriverstorage blob (now returns HTTP 409
148+
# "Public access is not permitted"); the current host is msedgedriver.microsoft.com.
149+
$base = "https://msedgedriver.microsoft.com"
150+
151+
# Ordered list of candidate driver versions to try.
152+
$candidates = [System.Collections.Generic.List[string]]::new()
153+
$candidates.Add($wv2Version)
154+
foreach ($marker in @("LATEST_RELEASE_${wv2Major}_WINDOWS", "LATEST_STABLE")) {
155+
try {
156+
$resolved = (Invoke-WebRequest -UseBasicParsing -Uri "$base/$marker").Content
157+
# Markers are UTF-16/BOM encoded; keep only version characters.
158+
$resolved = ($resolved -replace '[^0-9\.]', '').Trim()
159+
if ($resolved) { $candidates.Add($resolved); Write-Host "marker $marker -> $resolved" }
160+
} catch {
161+
Write-Host "marker $marker unavailable: $($_.Exception.Message)"
162+
}
163+
}
164+
141165
$zipPath = "$env:TEMP\edgedriver.zip"
142166
$extractDir = "$env:TEMP\edgedriver"
143-
Invoke-WebRequest -Uri $driverUrl -OutFile $zipPath
167+
$ok = $false
168+
foreach ($v in ($candidates | Select-Object -Unique)) {
169+
$driverUrl = "$base/$v/edgedriver_win64.zip"
170+
Write-Host "Trying EdgeDriver $v -> $driverUrl"
171+
try {
172+
Invoke-WebRequest -UseBasicParsing -Uri $driverUrl -OutFile $zipPath
173+
Write-Host "Downloaded EdgeDriver $v"
174+
$ok = $true
175+
break
176+
} catch {
177+
Write-Host " not available: $($_.Exception.Message)"
178+
}
179+
}
180+
if (-not $ok) { throw "No compatible msedgedriver found for WebView2 $wv2Version (tried: $($candidates -join ', '))" }
181+
144182
Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force
145183
$driverDir = "$extractDir\edgedriver_win64"
146184
if (-not (Test-Path "$driverDir\msedgedriver.exe")) { $driverDir = $extractDir }

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,3 +1654,54 @@ impl Default for OutboundOAuthManager {
16541654
Self::new()
16551655
}
16561656
}
1657+
1658+
#[cfg(test)]
1659+
mod resource_param_tests {
1660+
use rmcp::transport::auth::{AuthorizationManager, AuthorizationMetadata};
1661+
1662+
/// The gateway delegates authorize-URL construction entirely to rmcp's
1663+
/// `AuthorizationManager::get_authorization_url` (via `create_auth_manager` /
1664+
/// `start_oauth_flow`). rmcp appends the RFC 8707 `resource` parameter itself, so the
1665+
/// gateway must NOT add a second one. This guards against re-introducing the removed
1666+
/// `add_resource_parameter` wrapper, which produced `?resource=...&resource=...` —
1667+
/// rejected by strict authorization servers (e.g. Supabase) and broke OAuth login.
1668+
#[tokio::test]
1669+
async fn authorize_url_has_exactly_one_resource_param() {
1670+
let base_url = "https://mcp.example.test/";
1671+
1672+
let mut manager = AuthorizationManager::new(base_url)
1673+
.await
1674+
.expect("construct AuthorizationManager");
1675+
1676+
manager.set_metadata(AuthorizationMetadata {
1677+
authorization_endpoint: "https://auth.example.test/authorize".to_string(),
1678+
token_endpoint: "https://auth.example.test/token".to_string(),
1679+
response_types_supported: Some(vec!["code".to_string()]),
1680+
code_challenge_methods_supported: Some(vec!["S256".to_string()]),
1681+
..Default::default()
1682+
});
1683+
manager
1684+
.configure_client_id("test-client")
1685+
.expect("configure client id");
1686+
1687+
let auth_url = manager
1688+
.get_authorization_url(&["openid"])
1689+
.await
1690+
.expect("generate authorization url");
1691+
1692+
let parsed = url::Url::parse(&auth_url).expect("authorize url should parse");
1693+
let resource_values: Vec<String> = parsed
1694+
.query_pairs()
1695+
.filter(|(k, _)| k == "resource")
1696+
.map(|(_, v)| v.into_owned())
1697+
.collect();
1698+
1699+
assert_eq!(
1700+
resource_values.len(),
1701+
1,
1702+
"authorize URL must carry exactly one RFC 8707 resource param, \
1703+
got {resource_values:?} in {auth_url}"
1704+
);
1705+
assert_eq!(resource_values[0], base_url);
1706+
}
1707+
}

0 commit comments

Comments
 (0)