From 7500ab4c8d0e556c28bafbf3e232dadec4d01b90 Mon Sep 17 00:00:00 2001 From: thomas Date: Mon, 17 Aug 2026 22:53:49 +0000 Subject: [PATCH] Update rmcp to 3.1.2 (#39101) ## What changed - Upgrade `rmcp` from 3.0.0 to 3.1.2. - Use `rmcp`'s native JSON-RPC decoding and remove the local compatibility layer for multi-round-trip tool results. - Preserve response metadata on `input_required` SSE results. - Support OAuth protected-resource metadata discovery and include the discovered resource audience when refreshing tokens. ## Testing - Add coverage for metadata preservation on modern SSE `input_required` responses. - Add an OAuth startup scenario that discovers protected-resource metadata and verifies the refresh-token resource audience. GitOrigin-RevId: 689f009b4a33552b640304111b74585b4009eeb8 --- MODULE.bazel.lock | 8 +- codex-rs/Cargo.lock | 57 ++++++- codex-rs/Cargo.toml | 2 +- .../src/executor_process_transport.rs | 4 +- .../rmcp-client/src/http_client_adapter.rs | 31 +--- codex-rs/rmcp-client/src/incoming_jsonrpc.rs | 58 ------- .../rmcp-client/src/incoming_jsonrpc_tests.rs | 151 ------------------ codex-rs/rmcp-client/src/lib.rs | 1 - .../rmcp-client/src/local_stdio_transport.rs | 4 +- codex-rs/rmcp-client/tests/mcp_2026_mrtr.rs | 45 ++++++ .../tests/streamable_http_oauth_startup.rs | 83 ++++++++-- 11 files changed, 182 insertions(+), 262 deletions(-) delete mode 100644 codex-rs/rmcp-client/src/incoming_jsonrpc.rs delete mode 100644 codex-rs/rmcp-client/src/incoming_jsonrpc_tests.rs diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index f291787f3d..76850d7bd3 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -894,12 +894,15 @@ "darling_0.20.11": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.20.11\"},{\"name\":\"darling_macro\",\"req\":\"=0.20.11\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"suggestions\":[\"darling_core/suggestions\"]}}", "darling_0.21.3": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.21.3\"},{\"name\":\"darling_macro\",\"req\":\"=0.21.3\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"serde\":[\"darling_core/serde\"],\"suggestions\":[\"darling_core/suggestions\"]}}", "darling_0.23.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.23.0\"},{\"name\":\"darling_macro\",\"req\":\"=0.23.0\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2.0.15\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"serde\":[\"darling_core/serde\"],\"suggestions\":[\"darling_core/suggestions\"]}}", + "darling_0.24.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.24.0\"},{\"name\":\"darling_macro\",\"req\":\"=0.24.0\"},{\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.9\",\"target\":\"cfg(compiletests)\"},{\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^3.0.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.89\",\"target\":\"cfg(compiletests)\"}],\"features\":{\"default\":[\"suggestions\"],\"diagnostics\":[\"darling_core/diagnostics\"],\"serde\":[\"darling_core/serde\"],\"suggestions\":[\"darling_core/suggestions\"]}}", "darling_core_0.20.11": "{\"dependencies\":[{\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", "darling_core_0.21.3": "{\"dependencies\":[{\"name\":\"fnv\",\"req\":\"^1.0.7\"},{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.210\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", "darling_core_0.23.0": "{\"dependencies\":[{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.210\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", + "darling_core_0.24.0": "{\"dependencies\":[{\"name\":\"ident_case\",\"req\":\"^1.0.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0.86\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.210\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.140\"},{\"name\":\"strsim\",\"optional\":true,\"req\":\"^0.11.1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^3.0.0\"}],\"features\":{\"diagnostics\":[],\"suggestions\":[\"strsim\"]}}", "darling_macro_0.20.11": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.20.11\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "darling_macro_0.21.3": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.21.3\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", "darling_macro_0.23.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.23.0\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^2.0.15\"}],\"features\":{}}", + "darling_macro_0.24.0": "{\"dependencies\":[{\"name\":\"darling_core\",\"req\":\"=0.24.0\"},{\"name\":\"quote\",\"req\":\"^1.0.18\"},{\"name\":\"syn\",\"req\":\"^3.0.0\"}],\"features\":{}}", "dashmap_6.1.0": "{\"dependencies\":[{\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.3.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"crossbeam-utils\",\"req\":\"^0.8\"},{\"default_features\":false,\"features\":[\"raw\"],\"name\":\"hashbrown\",\"req\":\"^0.14.0\"},{\"name\":\"lock_api\",\"req\":\"^0.4.10\"},{\"name\":\"once_cell\",\"req\":\"^1.18.0\"},{\"name\":\"parking_lot_core\",\"req\":\"^0.9.8\"},{\"name\":\"rayon\",\"optional\":true,\"req\":\"^1.7.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.188\"},{\"default_features\":false,\"name\":\"typesize\",\"optional\":true,\"req\":\"^0.1.8\"}],\"features\":{\"inline\":[\"hashbrown/inline-more\"],\"raw-api\":[],\"typesize\":[\"dep:typesize\"]}}", "data-encoding_2.10.0": "{\"dependencies\":[],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"std\":[\"alloc\"]}}", "dbus-secret-service_4.1.0": "{\"dependencies\":[{\"name\":\"aes\",\"optional\":true,\"req\":\"^0.8\"},{\"features\":[\"std\"],\"name\":\"block-padding\",\"optional\":true,\"req\":\"^0.3\"},{\"features\":[\"block-padding\",\"alloc\"],\"name\":\"cbc\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"dbus\",\"req\":\"^0.9\"},{\"name\":\"fastrand\",\"optional\":true,\"req\":\"^2.3\"},{\"name\":\"hkdf\",\"optional\":true,\"req\":\"^0.12\"},{\"name\":\"num\",\"optional\":true,\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.55\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10\"},{\"features\":[\"derive\"],\"name\":\"zeroize\",\"req\":\"^1.8\"}],\"features\":{\"crypto-openssl\":[\"dep:fastrand\",\"dep:num\",\"dep:once_cell\",\"dep:openssl\"],\"crypto-rust\":[\"dep:aes\",\"dep:block-padding\",\"dep:cbc\",\"dep:fastrand\",\"dep:hkdf\",\"dep:num\",\"dep:once_cell\",\"dep:sha2\"],\"vendored\":[\"dbus/vendored\",\"openssl?/vendored\"]}}", @@ -1542,8 +1545,8 @@ "resolv-conf_0.7.6": "{\"dependencies\":[],\"features\":{\"system\":[]}}", "rfc6979_0.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"},{\"default_features\":false,\"features\":[\"reset\"],\"name\":\"hmac\",\"req\":\"^0.12\"},{\"kind\":\"dev\",\"name\":\"sha2\",\"req\":\"^0.10\"},{\"default_features\":false,\"name\":\"subtle\",\"req\":\"^2\"}],\"features\":{}}", "ring_0.17.14": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"build\",\"name\":\"cc\",\"req\":\"^1.2.8\"},{\"default_features\":false,\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"getrandom\",\"req\":\"^0.2.10\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(all(any(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), all(target_arch = \\\"arm\\\", target_endian = \\\"little\\\")), any(target_os = \\\"android\\\", target_os = \\\"linux\\\")))\"},{\"default_features\":false,\"name\":\"libc\",\"req\":\"^0.2.155\",\"target\":\"cfg(all(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), target_vendor = \\\"apple\\\", any(target_os = \\\"ios\\\", target_os = \\\"macos\\\", target_os = \\\"tvos\\\", target_os = \\\"visionos\\\", target_os = \\\"watchos\\\")))\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(any(unix, windows, target_os = \\\"wasi\\\"))\"},{\"name\":\"untrusted\",\"req\":\"^0.9\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.37\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", target_os = \\\"unknown\\\"))\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Threading\"],\"name\":\"windows-sys\",\"req\":\"^0.52\",\"target\":\"cfg(all(all(target_arch = \\\"aarch64\\\", target_endian = \\\"little\\\"), target_os = \\\"windows\\\"))\"}],\"features\":{\"alloc\":[],\"default\":[\"alloc\",\"dev_urandom_fallback\"],\"dev_urandom_fallback\":[],\"less-safe-getrandom-custom-or-rdrand\":[],\"less-safe-getrandom-espidf\":[],\"slow_tests\":[],\"std\":[\"alloc\"],\"test_logging\":[],\"unstable-testing-arm-no-hw\":[],\"unstable-testing-arm-no-neon\":[],\"wasm32_unknown_unknown_js\":[\"getrandom/js\"]}}", - "rmcp-macros_3.0.0": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.23\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^2\"}],\"features\":{\"local\":[]}}", - "rmcp_3.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"axum\",\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.23\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"serde\",\"now\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"client\",\"http1\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"server\",\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"features\":[\"aws_lc_rs\"],\"name\":\"jsonwebtoken\",\"optional\":true,\"req\":\"^11\"},{\"default_features\":false,\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.2\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^3.0.0\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26.1\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.11\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2.4\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"which\",\"optional\":true,\"req\":\"^8\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"auth-client-credentials-jwt\":[\"auth\",\"dep:jsonwebtoken\",\"uuid\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\",\"dep:bytes\",\"base64\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[\"dep:url\"],\"local\":[\"rmcp-macros?/local\"],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"request-state\":[\"dep:hmac\",\"dep:sha2\",\"base64\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls\"],\"reqwest-native-tls\":[\"__reqwest\",\"reqwest?/native-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"schemars\",\"dep:pastey\",\"uuid\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"tower\",\"base64\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"__reqwest\"],\"transport-streamable-http-client-unix-socket\":[\"transport-streamable-http-client\",\"dep:hyper\",\"dep:hyper-util\",\"dep:http-body-util\",\"dep:http\",\"dep:bytes\",\"tokio/net\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"],\"which-command\":[\"transport-child-process\",\"dep:which\"]}}", + "rmcp-macros_3.1.2": "{\"dependencies\":[{\"name\":\"darling\",\"req\":\"^0.24\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"features\":[\"full\"],\"name\":\"syn\",\"req\":\"^3\"}],\"features\":{\"local\":[]}}", + "rmcp_3.1.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"name\":\"async-trait\",\"optional\":true,\"req\":\"^0.1.89\"},{\"kind\":\"dev\",\"name\":\"async-trait\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"http1\",\"tokio\"],\"kind\":\"dev\",\"name\":\"axum\",\"req\":\"^0.8\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.23\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"serde\",\"now\"],\"name\":\"chrono\",\"req\":\"^0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"},{\"name\":\"futures\",\"req\":\"^0.3\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"http\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"http-body-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"client\",\"http1\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"server\",\"http1\"],\"kind\":\"dev\",\"name\":\"hyper\",\"req\":\"^1\"},{\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"^0.1\"},{\"features\":[\"tokio\"],\"kind\":\"dev\",\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"features\":[\"aws_lc_rs\"],\"name\":\"jsonwebtoken\",\"optional\":true,\"req\":\"^11\"},{\"default_features\":false,\"name\":\"oauth2\",\"optional\":true,\"req\":\"^5.0\"},{\"name\":\"pastey\",\"optional\":true,\"req\":\"^0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"^0.2\"},{\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"^9.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.10\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"^0.13.2\"},{\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"^3.1.2\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26.1\"},{\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"chrono04\"],\"kind\":\"dev\",\"name\":\"schemars\",\"req\":\"^1.1.0\"},{\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.11\"},{\"name\":\"sse-stream\",\"optional\":true,\"req\":\"^0.2.4\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1\"},{\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"tokio-util\",\"req\":\"^0.7\"},{\"name\":\"tower-service\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"tower-service\",\"req\":\"^0.3\"},{\"name\":\"tracing\",\"req\":\"^0.1\"},{\"features\":[\"env-filter\",\"std\",\"fmt\"],\"kind\":\"dev\",\"name\":\"tracing-subscriber\",\"req\":\"^0.3\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2.4\"},{\"kind\":\"dev\",\"name\":\"url\",\"req\":\"^2.4\"},{\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"which\",\"optional\":true,\"req\":\"^8\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:async-trait\",\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"auth-client-credentials-jwt\":[\"auth\",\"dep:jsonwebtoken\",\"uuid\"],\"client\":[\"dep:tokio-stream\"],\"client-side-sse\":[\"dep:sse-stream\",\"dep:http\",\"dep:bytes\",\"base64\"],\"default\":[\"base64\",\"macros\",\"server\"],\"elicitation\":[\"dep:url\"],\"local\":[\"rmcp-macros?/local\"],\"macros\":[\"dep:rmcp-macros\",\"dep:pastey\"],\"request-state\":[\"dep:hmac\",\"dep:sha2\",\"base64\"],\"reqwest\":[\"__reqwest\",\"reqwest?/rustls\"],\"reqwest-native-tls\":[\"__reqwest\",\"reqwest?/native-tls\"],\"reqwest-tls-no-provider\":[\"__reqwest\",\"reqwest?/rustls-no-provider\"],\"schemars\":[\"dep:schemars\"],\"server\":[\"transport-async-rw\",\"schemars\",\"dep:pastey\",\"uuid\"],\"server-side-http\":[\"uuid\",\"dep:rand\",\"dep:tokio-stream\",\"dep:http\",\"dep:http-body\",\"dep:http-body-util\",\"dep:bytes\",\"dep:sse-stream\",\"tower\",\"base64\"],\"tower\":[\"dep:tower-service\"],\"transport-async-rw\":[\"tokio/io-util\",\"tokio-util/codec\"],\"transport-child-process\":[\"transport-async-rw\",\"tokio/process\",\"dep:process-wrap\"],\"transport-io\":[\"transport-async-rw\",\"tokio/io-std\"],\"transport-streamable-http-client\":[\"client-side-sse\",\"transport-worker\"],\"transport-streamable-http-client-reqwest\":[\"transport-streamable-http-client\",\"__reqwest\"],\"transport-streamable-http-client-unix-socket\":[\"transport-streamable-http-client\",\"dep:hyper\",\"dep:hyper-util\",\"dep:http-body-util\",\"dep:http\",\"dep:bytes\",\"tokio/net\"],\"transport-streamable-http-server\":[\"transport-streamable-http-server-session\",\"server-side-http\",\"transport-worker\"],\"transport-streamable-http-server-session\":[\"transport-async-rw\",\"dep:async-trait\",\"dep:tokio-stream\"],\"transport-worker\":[\"dep:tokio-stream\"],\"which-command\":[\"transport-child-process\",\"dep:which\"]}}", "rouille_3.6.2": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.13\"},{\"name\":\"brotli\",\"optional\":true,\"req\":\"^3.3.2\"},{\"default_features\":false,\"features\":[\"clock\"],\"name\":\"chrono\",\"req\":\"^0.4.19\"},{\"features\":[\"gzip\"],\"name\":\"deflate\",\"optional\":true,\"req\":\"^1.0.0\"},{\"name\":\"filetime\",\"req\":\"^0.2.0\"},{\"kind\":\"dev\",\"name\":\"log\",\"req\":\"^0.4\"},{\"default_features\":false,\"features\":[\"server\"],\"name\":\"multipart\",\"req\":\"^0.18\"},{\"name\":\"percent-encoding\",\"req\":\"^2\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"postgres\",\"req\":\"^0.19\"},{\"name\":\"rand\",\"req\":\"^0.8\"},{\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_derive\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"sha1_smol\",\"req\":\"^1.0.0\"},{\"name\":\"threadpool\",\"req\":\"^1\"},{\"features\":[\"local-offset\"],\"name\":\"time\",\"req\":\"^0.3.15\"},{\"default_features\":false,\"name\":\"tiny_http\",\"req\":\"^0.12.0\"},{\"name\":\"url\",\"req\":\"^2\"}],\"features\":{\"default\":[\"gzip\",\"brotli\"],\"gzip\":[\"deflate\"],\"rustls\":[\"tiny_http/ssl-rustls\"],\"ssl\":[\"tiny_http/ssl\"]}}", "rust-embed-impl_8.11.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"name\":\"rust-embed-utils\",\"req\":\"^8.11.0\"},{\"name\":\"shellexpand\",\"optional\":true,\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"proc-macro\",\"printing\"],\"name\":\"syn\",\"req\":\"^2\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"compression\":[],\"debug-embed\":[],\"deterministic-timestamps\":[],\"include-exclude\":[\"rust-embed-utils/include-exclude\"],\"interpolate-folder-path\":[\"shellexpand\"],\"mime-guess\":[\"rust-embed-utils/mime-guess\"]}}", "rust-embed-utils_8.11.0": "{\"dependencies\":[{\"name\":\"globset\",\"optional\":true,\"req\":\"^0.4.8\"},{\"name\":\"mime_guess\",\"optional\":true,\"req\":\"^2.0.4\"},{\"name\":\"sha2\",\"req\":\"^0.10.5\"},{\"name\":\"walkdir\",\"req\":\"^2.3.1\"}],\"features\":{\"debug-embed\":[],\"include-exclude\":[\"globset\"],\"mime-guess\":[\"mime_guess\"]}}", @@ -1698,6 +1701,7 @@ "symphonia_0.6.0": "{\"dependencies\":[{\"name\":\"lazy_static\",\"req\":\"^1.4.0\"},{\"name\":\"symphonia-bundle-flac\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"symphonia-bundle-mp3\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-codec-aac\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-codec-adpcm\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-codec-alac\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-codec-pcm\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-codec-vorbis\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-core\",\"req\":\"^0.6.0\"},{\"name\":\"symphonia-format-caf\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-format-isomp4\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-format-mkv\",\"optional\":true,\"req\":\"^0.6.0\"},{\"name\":\"symphonia-format-ogg\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"symphonia-format-riff\",\"optional\":true,\"req\":\"^0.6.0\"},{\"default_features\":false,\"name\":\"symphonia-metadata\",\"req\":\"^0.6.0\"}],\"features\":{\"aac\":[\"dep:symphonia-codec-aac\"],\"adpcm\":[\"dep:symphonia-codec-adpcm\"],\"aiff\":[\"dep:symphonia-format-riff\",\"symphonia-format-riff/aiff\"],\"alac\":[\"dep:symphonia-codec-alac\"],\"all\":[\"all-codecs\",\"all-formats\",\"all-meta\"],\"all-codecs\":[\"aac\",\"adpcm\",\"alac\",\"flac\",\"mp1\",\"mp2\",\"mp3\",\"pcm\",\"vorbis\"],\"all-formats\":[\"caf\",\"isomp4\",\"mkv\",\"ogg\",\"aiff\",\"wav\"],\"all-meta\":[\"ape\",\"id3v1\",\"id3v2\"],\"ape\":[\"symphonia-metadata/ape\"],\"caf\":[\"dep:symphonia-format-caf\"],\"default\":[\"opt-simd\",\"all-meta\",\"adpcm\",\"flac\",\"mkv\",\"ogg\",\"pcm\",\"vorbis\",\"wav\"],\"exp-subtitle-codecs\":[\"symphonia-core/exp-subtitle-codecs\"],\"exp-video-codecs\":[\"symphonia-core/exp-video-codecs\"],\"flac\":[\"dep:symphonia-bundle-flac\"],\"id3v1\":[\"symphonia-metadata/id3v1\"],\"id3v2\":[\"symphonia-metadata/id3v2\"],\"isomp4\":[\"dep:symphonia-format-isomp4\"],\"mkv\":[\"dep:symphonia-format-mkv\"],\"mp1\":[\"dep:symphonia-bundle-mp3\",\"symphonia-bundle-mp3/mp1\"],\"mp2\":[\"dep:symphonia-bundle-mp3\",\"symphonia-bundle-mp3/mp2\"],\"mp3\":[\"dep:symphonia-bundle-mp3\",\"symphonia-bundle-mp3/mp3\"],\"mpa\":[\"mp1\",\"mp2\",\"mp3\"],\"ogg\":[\"dep:symphonia-format-ogg\"],\"opt-simd\":[\"opt-simd-sse\",\"opt-simd-avx\",\"opt-simd-neon\"],\"opt-simd-avx\":[\"symphonia-core/opt-simd-avx\"],\"opt-simd-neon\":[\"symphonia-core/opt-simd-neon\"],\"opt-simd-sse\":[\"symphonia-core/opt-simd-sse\"],\"pcm\":[\"dep:symphonia-codec-pcm\"],\"vorbis\":[\"dep:symphonia-codec-vorbis\"],\"wav\":[\"dep:symphonia-format-riff\",\"symphonia-format-riff/wav\"]}}", "syn_1.0.109": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.46\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.0\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1.0\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.1\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", "syn_2.0.117": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.13\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1\"},{\"name\":\"unicode-ident\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\",\"target\":\"cfg(not(miri))\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"dep:quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote?/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", + "syn_3.0.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"insta\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.91\"},{\"default_features\":false,\"name\":\"quote\",\"optional\":true,\"req\":\"^1.0.35\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"ref-cast\",\"req\":\"^1\"},{\"features\":[\"blocking\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.13\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"syn-test-suite\",\"req\":\"^0\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4.16\",\"target\":\"cfg(not(miri))\"},{\"kind\":\"dev\",\"name\":\"termcolor\",\"req\":\"^1\"},{\"name\":\"unicode-ident\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"walkdir\",\"req\":\"^2.3.2\",\"target\":\"cfg(not(miri))\"}],\"features\":{\"clone-impls\":[],\"default\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"proc-macro\"],\"derive\":[],\"extra-traits\":[],\"fold\":[],\"full\":[],\"parsing\":[],\"printing\":[\"dep:quote\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"quote?/proc-macro\"],\"test\":[\"syn-test-suite/all-features\"],\"visit\":[],\"visit-mut\":[]}}", "sync_wrapper_1.0.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"pin-project-lite\",\"req\":\"^0.2.7\"}],\"features\":{\"futures\":[\"futures-core\"]}}", "synstructure_0.13.2": "{\"dependencies\":[{\"default_features\":false,\"name\":\"proc-macro2\",\"req\":\"^1.0.60\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\",\"parsing\",\"printing\",\"clone-impls\",\"visit\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"synstructure_test_traits\",\"req\":\"^0.1\"}],\"features\":{\"default\":[\"proc-macro\"],\"proc-macro\":[\"proc-macro2/proc-macro\",\"syn/proc-macro\",\"quote/proc-macro\"]}}", "syntect_5.3.0": "{\"dependencies\":[{\"name\":\"bincode\",\"optional\":true,\"req\":\"^1.0\"},{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"name\":\"fancy-regex\",\"optional\":true,\"req\":\"^0.16.2\"},{\"name\":\"flate2\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"fnv\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"getopts\",\"req\":\"^0.2\"},{\"name\":\"once_cell\",\"req\":\"^1.8\"},{\"default_features\":false,\"name\":\"onig\",\"optional\":true,\"req\":\"^6.5.1\"},{\"name\":\"plist\",\"optional\":true,\"req\":\"^1.3\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6\"},{\"kind\":\"dev\",\"name\":\"public-api\",\"req\":\"^0.50.1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.0\"},{\"name\":\"regex-syntax\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rustdoc-json\",\"req\":\"^0.9.7\"},{\"kind\":\"dev\",\"name\":\"rustup-toolchain\",\"req\":\"^0.1.5\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"thiserror\",\"req\":\"^2.0.12\"},{\"name\":\"walkdir\",\"req\":\"^2.0\"},{\"name\":\"yaml-rust\",\"optional\":true,\"req\":\"^0.4.5\"}],\"features\":{\"default\":[\"default-onig\"],\"default-fancy\":[\"parsing\",\"default-syntaxes\",\"default-themes\",\"html\",\"plist-load\",\"yaml-load\",\"dump-load\",\"dump-create\",\"regex-fancy\"],\"default-onig\":[\"parsing\",\"default-syntaxes\",\"default-themes\",\"html\",\"plist-load\",\"yaml-load\",\"dump-load\",\"dump-create\",\"regex-onig\"],\"default-syntaxes\":[\"parsing\",\"dump-load\"],\"default-themes\":[\"dump-load\"],\"dump-create\":[\"flate2\",\"bincode\"],\"dump-load\":[\"flate2\",\"bincode\"],\"html\":[\"parsing\"],\"metadata\":[\"parsing\",\"plist-load\",\"dep:serde_json\"],\"parsing\":[\"regex-syntax\",\"fnv\",\"dump-create\",\"dump-load\"],\"plist-load\":[\"plist\",\"dep:serde_json\"],\"regex-fancy\":[\"fancy-regex\"],\"regex-onig\":[\"onig\"],\"yaml-load\":[\"yaml-rust\",\"parsing\"]}}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index d648daa605..0b3b4f8dfd 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -5377,6 +5377,16 @@ dependencies = [ "darling_macro 0.23.0", ] +[[package]] +name = "darling" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88490bf1b990d87eaaa7ac8aa887f629a08e7359765b4911faf63c3763347d23" +dependencies = [ + "darling_core 0.24.0", + "darling_macro 0.24.0", +] + [[package]] name = "darling_core" version = "0.20.11" @@ -5418,6 +5428,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_core" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084e274f91c482280130e1e34e0b8d6e66776a060d7b6de7b84289ca778868c4" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 3.0.3", +] + [[package]] name = "darling_macro" version = "0.20.11" @@ -5451,6 +5474,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_macro" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5792fa0d41cd2325ce0ffa64f0a340eaebd4971a3a0c5e1ffd2cc488a355e" +dependencies = [ + "darling_core 0.24.0", + "quote", + "syn 3.0.3", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -11810,9 +11844,9 @@ dependencies = [ [[package]] name = "rmcp" -version = "3.0.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcd2b6dd3b18129368955f32661a7718969e8c152c7d8866434c09cf15a512e0" +checksum = "c8dddc5b1924b9a59fba420166160ca2c4663a4e01803e52eda33070f56d63c8" dependencies = [ "async-trait", "base64 0.23.0", @@ -11845,15 +11879,15 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "3.0.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1aa4b9345795260a43fc23d6d05e096407c8b953903f673af7c4404b49fb2d6" +checksum = "6898e24cd16342b59bfa8a53c2c04b9cf62fc8a2cfea57b9c038b09984bfc521" dependencies = [ - "darling 0.23.0", + "darling 0.24.0", "proc-macro2", "quote", "serde_json", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -13514,6 +13548,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index d796f62e05..1a43d6c143 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -396,7 +396,7 @@ rcgen = { version = "0.14.7", default-features = false, features = [ regex = "1.12.3" regex-lite = "0.1.8" reqwest = { version = "0.12", features = ["cookies"] } -rmcp = { version = "=3.0.0", default-features = false } +rmcp = { version = "=3.1.2", default-features = false } runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" } rustix = { version = "1.1.4", features = ["net"] } rustls = { version = "0.23", default-features = false, features = [ diff --git a/codex-rs/rmcp-client/src/executor_process_transport.rs b/codex-rs/rmcp-client/src/executor_process_transport.rs index f36c14f70e..b1fddbdf75 100644 --- a/codex-rs/rmcp-client/src/executor_process_transport.rs +++ b/codex-rs/rmcp-client/src/executor_process_transport.rs @@ -45,8 +45,6 @@ use tracing::debug; use tracing::info; use tracing::warn; -use crate::incoming_jsonrpc::deserialize_incoming_jsonrpc_message; - static PROCESS_COUNTER: AtomicUsize = AtomicUsize::new(1); // Tool results can make valid MCP responses large, so keep the protocol // ceiling well above ordinary messages while still bounding hostile input. @@ -452,7 +450,7 @@ impl ExecutorProcessTransport { None => return None, }; let line = Self::trim_trailing_carriage_return(line); - match deserialize_incoming_jsonrpc_message(&line) { + match serde_json::from_slice(&line) { Ok(message) => return Some(message), Err(error) => { debug!( diff --git a/codex-rs/rmcp-client/src/http_client_adapter.rs b/codex-rs/rmcp-client/src/http_client_adapter.rs index c2327735f8..71066fba34 100644 --- a/codex-rs/rmcp-client/src/http_client_adapter.rs +++ b/codex-rs/rmcp-client/src/http_client_adapter.rs @@ -57,8 +57,6 @@ use tokio::sync::oneshot; use crate::event_notification_transport::MAX_EVENT_NOTIFICATION_BYTES; use crate::http_client_redirect::SameOriginRedirectHttpClient; -use crate::incoming_jsonrpc::deserialize_incoming_jsonrpc_message; -use crate::incoming_jsonrpc::normalize_sse_jsonrpc_message; use crate::local_stdio_transport::MAX_MCP_STDIO_LINE_BYTES; mod www_authenticate; @@ -357,11 +355,7 @@ impl StreamableHttpClient for StreamableHttpClientAdapter { } match content_type.as_deref() { Some(content_type) if content_type.starts_with(EVENT_STREAM_MIME_TYPE) => { - let mut event_stream = sse_stream_from_body( - body_stream, - maximum_response_bytes, - is_discovery_request || uses_modern_protocol, - ); + let mut event_stream = sse_stream_from_body(body_stream, maximum_response_bytes); if mcp_method.as_deref() == Some(DiscoverRequestMethod::VALUE) { while let Some(event) = event_stream.next().await { let event = event.map_err(StreamableHttpError::Sse)?; @@ -375,7 +369,7 @@ impl StreamableHttpClient for StreamableHttpClientAdapter { continue; } - let response = deserialize_incoming_jsonrpc_message(data.as_bytes()) + let response = serde_json::from_slice(data.as_bytes()) .map_err(StreamableHttpError::Deserialize)?; let response = legacy_discovery_fallback_response( &message, response, /*allow_uncorrelated_http_rejection*/ false, @@ -425,8 +419,8 @@ impl StreamableHttpClient for StreamableHttpClientAdapter { } Some(content_type) if content_type.starts_with(JSON_MIME_TYPE) => { let body = collect_body(&mut body_stream, maximum_response_bytes).await?; - let response_message = deserialize_incoming_jsonrpc_message(&body) - .map_err(StreamableHttpError::Deserialize)?; + let response_message = + serde_json::from_slice(&body).map_err(StreamableHttpError::Deserialize)?; Ok(StreamableHttpPostResponse::Json( legacy_discovery_fallback_response( &message, @@ -593,11 +587,7 @@ impl StreamableHttpClient for StreamableHttpClientAdapter { .and_then(|value| value.to_str().ok()) .is_some_and(|version| version == ProtocolVersion::V_2026_07_28.as_str()); let maximum_response_bytes = uses_modern_protocol.then_some(MAX_MCP_STDIO_LINE_BYTES); - Ok(sse_stream_from_body( - body_stream, - maximum_response_bytes, - uses_modern_protocol, - )) + Ok(sse_stream_from_body(body_stream, maximum_response_bytes)) } } @@ -948,7 +938,6 @@ async fn collect_body( fn sse_stream_from_body( body_stream: HttpResponseBodyStream, maximum_event_bytes: Option, - modern_session: bool, ) -> BoxStream<'static, std::result::Result> { SseStream::from_bytes_stream(stream::unfold( (body_stream, SseEventSizeLimit::new(maximum_event_bytes)), @@ -966,16 +955,6 @@ fn sse_stream_from_body( } }, )) - .map(move |event| { - event.map(|mut event| { - if let Some(payload) = event.data.as_deref() - && let Some(normalized) = normalize_sse_jsonrpc_message(payload, modern_session) - { - event.data = Some(normalized); - } - event - }) - }) .boxed() } diff --git a/codex-rs/rmcp-client/src/incoming_jsonrpc.rs b/codex-rs/rmcp-client/src/incoming_jsonrpc.rs deleted file mode 100644 index a8bc5c145d..0000000000 --- a/codex-rs/rmcp-client/src/incoming_jsonrpc.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Compatibility decoding for multi-round-trip tool results that rmcp's -//! untagged result union does not yet represent correctly. - -use rmcp::model::InputRequiredResult; -use rmcp::model::JsonRpcResponse; -use rmcp::model::ServerJsonRpcMessage; -use rmcp::model::ServerResult; -use serde_json::Value; - -/// Decode at the transport boundary, before rmcp's untagged `ServerResult` -/// deserializer can mistake a modern result for a completed tool call. -pub(crate) fn deserialize_incoming_jsonrpc_message( - bytes: &[u8], -) -> serde_json::Result { - let message: Value = serde_json::from_slice(bytes)?; - - if message - .pointer("/result/resultType") - .and_then(Value::as_str) - == Some("input_required") - { - let response: JsonRpcResponse = serde_json::from_value(message)?; - return Ok(ServerJsonRpcMessage::response( - ServerResult::InputRequiredResult(response.result), - response.id, - )); - } - - serde_json::from_value(message) -} - -/// Normalize an SSE payload before rmcp parses it internally. SSE does not -/// expose a typed-message hook, so response-level MRTR metadata cannot be -/// retained until the upstream untagged-result decoder is fixed. -pub(crate) fn normalize_sse_jsonrpc_message(payload: &str, modern_session: bool) -> Option { - if !modern_session { - return None; - } - - let mut message: Value = serde_json::from_str(payload).ok()?; - if message - .pointer("/result/resultType") - .and_then(Value::as_str) - != Some("input_required") - { - return None; - } - - message - .get_mut("result") - .and_then(Value::as_object_mut)? - .remove("_meta")?; - serde_json::to_string(&message).ok() -} - -#[cfg(test)] -#[path = "incoming_jsonrpc_tests.rs"] -mod tests; diff --git a/codex-rs/rmcp-client/src/incoming_jsonrpc_tests.rs b/codex-rs/rmcp-client/src/incoming_jsonrpc_tests.rs deleted file mode 100644 index 321c73ec66..0000000000 --- a/codex-rs/rmcp-client/src/incoming_jsonrpc_tests.rs +++ /dev/null @@ -1,151 +0,0 @@ -use pretty_assertions::assert_eq; -use rmcp::model::JsonRpcMessage; -use rmcp::model::ServerResult; -use serde_json::json; - -use super::deserialize_incoming_jsonrpc_message; -use super::normalize_sse_jsonrpc_message; - -#[test] -fn discovery_accepts_metadata_namespaced_server_identity() { - let message = json!({ - "jsonrpc": "2.0", - "id": 1, - "result": { - "resultType": "complete", - "supportedVersions": ["2026-07-28"], - "capabilities": {"tools": {}}, - "ttlMs": 0, - "cacheScope": "private", - "_meta": { - "io.modelcontextprotocol/serverInfo": { - "name": "modern-server", - "version": "1.0.0", - }, - "retained": "metadata", - }, - }, - }); - - let decoded = deserialize_incoming_jsonrpc_message(&serde_json::to_vec(&message).unwrap()) - .expect("metadata-only identity must decode as discovery"); - let JsonRpcMessage::Response(response) = decoded else { - panic!("expected a discovery response"); - }; - let ServerResult::DiscoverResult(result) = response.result else { - panic!("metadata-only identity must not become a completed tool result"); - }; - assert_eq!( - result.server_info().map(|server_info| server_info.name), - Some("modern-server".to_owned()) - ); - assert_eq!( - result.meta.and_then(|meta| meta.get("retained").cloned()), - Some(json!("metadata")) - ); -} - -#[test] -fn extension_result_with_discovery_like_fields_remains_a_custom_result() { - let result = json!({ - "supportedVersions": ["custom-extension-v1"], - "capabilities": {"custom": true}, - }); - let message = json!({ - "jsonrpc": "2.0", - "id": 2, - "result": result, - }); - - let decoded = deserialize_incoming_jsonrpc_message(&serde_json::to_vec(&message).unwrap()) - .expect("legacy extension result must deserialize normally"); - let JsonRpcMessage::Response(response) = decoded else { - panic!("expected a legacy extension response"); - }; - let ServerResult::CustomResult(custom) = response.result else { - panic!("legacy extension response must remain a custom result"); - }; - assert_eq!(custom.0, result); -} - -#[test] -fn input_required_discriminator_wins_over_tool_result_metadata() { - let message = json!({ - "jsonrpc": "2.0", - "id": "tool-call", - "result": { - "resultType": "input_required", - "requestState": "opaque-server-state", - "_meta": {"retained": "response-metadata"}, - }, - }); - - let decoded = deserialize_incoming_jsonrpc_message(&serde_json::to_vec(&message).unwrap()) - .expect("input-required result must decode"); - let JsonRpcMessage::Response(response) = decoded else { - panic!("expected an input-required response"); - }; - let ServerResult::InputRequiredResult(result) = response.result else { - panic!("input_required must not become an empty completed tool result"); - }; - assert_eq!(result.request_state.as_deref(), Some("opaque-server-state")); - assert_eq!( - result.meta.and_then(|meta| meta.get("retained").cloned()), - Some(json!("response-metadata")) - ); -} - -#[test] -fn sse_discovery_preserves_metadata_namespaced_identity_without_normalization() { - let message = json!({ - "jsonrpc": "2.0", - "id": 1, - "result": { - "resultType": "complete", - "supportedVersions": ["2026-07-28"], - "capabilities": {}, - "ttlMs": 0, - "cacheScope": "private", - "_meta": { - "io.modelcontextprotocol/serverInfo": { - "name": "sse-server", - "version": "1.0.0", - }, - }, - }, - }); - assert_eq!( - normalize_sse_jsonrpc_message(&message.to_string(), /*modern_session*/ true), - None - ); - - let decoded = deserialize_incoming_jsonrpc_message(&serde_json::to_vec(&message).unwrap()) - .expect("rmcp must natively decode metadata-only SSE discovery"); - let JsonRpcMessage::Response(response) = decoded else { - panic!("expected a discovery response"); - }; - let ServerResult::DiscoverResult(result) = response.result else { - panic!("metadata-only identity must decode as discovery without normalization"); - }; - assert_eq!( - result.server_info().map(|server_info| server_info.name), - Some("sse-server".to_owned()) - ); -} - -#[test] -fn legacy_sse_payload_preserves_intermediate_response_metadata() { - let message = json!({ - "jsonrpc": "2.0", - "id": "legacy-extension", - "result": { - "resultType": "input_required", - "_meta": {"preserved": "legacy-extension-metadata"}, - }, - }); - - assert_eq!( - normalize_sse_jsonrpc_message(&message.to_string(), /*modern_session*/ false), - None - ); -} diff --git a/codex-rs/rmcp-client/src/lib.rs b/codex-rs/rmcp-client/src/lib.rs index 4203788638..32ccd8ef28 100644 --- a/codex-rs/rmcp-client/src/lib.rs +++ b/codex-rs/rmcp-client/src/lib.rs @@ -6,7 +6,6 @@ mod http_client_adapter; mod http_client_redirect; mod http_headers; mod in_process_transport; -mod incoming_jsonrpc; mod local_stdio_transport; mod logging_client_handler; mod oauth; diff --git a/codex-rs/rmcp-client/src/local_stdio_transport.rs b/codex-rs/rmcp-client/src/local_stdio_transport.rs index 1d69afa9d0..40e0e72946 100644 --- a/codex-rs/rmcp-client/src/local_stdio_transport.rs +++ b/codex-rs/rmcp-client/src/local_stdio_transport.rs @@ -23,8 +23,6 @@ use tokio::sync::Mutex; use tracing::debug; use tracing::warn; -use crate::incoming_jsonrpc::deserialize_incoming_jsonrpc_message; - /// Match the existing executor stdio transport and the production MCP limit. pub(crate) const MAX_MCP_STDIO_LINE_BYTES: usize = 8 * 1024 * 1024; @@ -118,7 +116,7 @@ impl LocalStdioTransport { fn decode_pending_message(&mut self) -> Option> { let line = std::mem::take(&mut self.pending_line); let line = line.strip_suffix(b"\r").unwrap_or(&line); - match deserialize_incoming_jsonrpc_message(line) { + match serde_json::from_slice(line) { Ok(message) => Some(message), Err(error) => { debug!( diff --git a/codex-rs/rmcp-client/tests/mcp_2026_mrtr.rs b/codex-rs/rmcp-client/tests/mcp_2026_mrtr.rs index 5ace4180b2..6f6a8555f9 100644 --- a/codex-rs/rmcp-client/tests/mcp_2026_mrtr.rs +++ b/codex-rs/rmcp-client/tests/mcp_2026_mrtr.rs @@ -20,6 +20,7 @@ use rmcp::model::Implementation; use rmcp::model::InitializeRequestParams; use rmcp::model::ProtocolVersion; use rmcp::model::ReadResourceRequestParams; +use rmcp::model::ServerResult; use rmcp::model::UrlElicitationCapability; use serde_json::Value; use serde_json::json; @@ -170,6 +171,50 @@ async fn create_client( Ok(client) } +#[tokio::test] +async fn modern_sse_input_required_preserves_response_metadata() -> anyhow::Result<()> { + let server = MockServer::start().await; + let expected_result = json!({ + "resultType": "input_required", + "requestState": OPAQUE_STATE, + "_meta": {"responseContext": "preserved"}, + }); + let result = expected_result.clone(); + + Mock::given(method("POST")) + .and(path("/mcp")) + .respond_with(move |request: &Request| { + let body: Value = request.body_json().expect("valid JSON-RPC request"); + match body["method"].as_str() { + Some("server/discover") => discover_response(&body), + Some("tools/call") => sse_result_response(&body, result.clone()), + other => panic!("unexpected MRTR request: {other:?}"), + } + }) + .expect(2) + .mount(&server) + .await; + + let client = create_client(&server, Arc::new(Mutex::new(Vec::new()))).await?; + let result = client + .send_custom_request_with_timeout( + "tools/call", + Some(json!({"name": "confirm", "arguments": {}})), + Some(Duration::from_secs(5)), + ) + .await?; + let ServerResult::InputRequiredResult(result) = result else { + anyhow::bail!("SSE response must remain an input_required result"); + }; + + assert_eq!(serde_json::to_value(result)?, expected_result); + + client.shutdown().await; + server.verify().await; + + Ok(()) +} + #[tokio::test] async fn modern_tool_mrtr_drives_form_and_url_elicitation_and_preserves_metadata() -> anyhow::Result<()> { diff --git a/codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs b/codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs index 349a69e757..67300209b1 100644 --- a/codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs +++ b/codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs @@ -55,28 +55,82 @@ const UNREFRESHABLE_SERVER_URL: &str = "https://unrefreshable.example/mcp"; const UNEXPIRED_SERVER_URL: &str = "https://unexpired.example/mcp"; const REFRESHABLE_SERVER_URL: &str = "https://refreshable.example/mcp"; +#[derive(Clone, Copy)] +enum OAuthStartupScenario { + DirectAuthorizationMetadata, + GatewayHeadersHelper, + ProtectedResourceMetadata, +} + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn refreshes_expired_persisted_token_before_initialize() -> anyhow::Result<()> { - assert_expired_token_refresh(/*with_headers_helper*/ false).await + assert_expired_token_refresh(OAuthStartupScenario::DirectAuthorizationMetadata).await } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn refreshes_oauth_with_gateway_headers_helper() -> anyhow::Result<()> { - assert_expired_token_refresh(/*with_headers_helper*/ true).await + assert_expired_token_refresh(OAuthStartupScenario::GatewayHeadersHelper).await } -async fn assert_expired_token_refresh(with_headers_helper: bool) -> anyhow::Result<()> { +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn refresh_uses_discovered_protected_resource_audience() -> anyhow::Result<()> { + assert_expired_token_refresh(OAuthStartupScenario::ProtectedResourceMetadata).await +} + +async fn assert_expired_token_refresh(scenario: OAuthStartupScenario) -> anyhow::Result<()> { let server = MockServer::start().await; let authorization_server = MockServer::start().await; + let resource_url = format!("{}/mcp", server.uri()); + let (server_url, mcp_path, authorization_metadata_path) = match scenario { + OAuthStartupScenario::DirectAuthorizationMetadata + | OAuthStartupScenario::GatewayHeadersHelper => ( + resource_url.clone(), + "/mcp", + "/.well-known/oauth-authorization-server/mcp", + ), + OAuthStartupScenario::ProtectedResourceMetadata => ( + format!("{resource_url}/"), + "/mcp/", + "/.well-known/oauth-authorization-server", + ), + }; + + if matches!(scenario, OAuthStartupScenario::ProtectedResourceMetadata) { + let resource_metadata_url = format!("{}/resource-metadata", server.uri()); + Mock::given(method("GET")) + .and(path(mcp_path)) + .respond_with(ResponseTemplate::new(401).insert_header( + "www-authenticate", + format!("Bearer resource_metadata=\"{resource_metadata_url}\""), + )) + .expect(1) + .mount(&server) + .await; + Mock::given(method("GET")) + .and(path("/resource-metadata")) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "resource": resource_url, + "authorization_servers": [server.uri()], + }))) + .expect(1) + .mount(&server) + .await; + } + + let mut authorization_metadata = json!({ + "authorization_endpoint": format!("{}/oauth/authorize", authorization_server.uri()), + "token_endpoint": format!("{}/oauth/token", authorization_server.uri()), + "scopes_supported": [""], + }); + if matches!(scenario, OAuthStartupScenario::ProtectedResourceMetadata) { + authorization_metadata["issuer"] = json!(server.uri()); + } + Mock::given(method("GET")) - .and(path("/.well-known/oauth-authorization-server/mcp")) + .and(path(authorization_metadata_path)) .and(header("user-agent", RESOURCE_USER_AGENT)) .and(header("x-api-key", RESOURCE_API_KEY)) - .respond_with(ResponseTemplate::new(200).set_body_json(json!({ - "authorization_endpoint": format!("{}/oauth/authorize", authorization_server.uri()), - "token_endpoint": format!("{}/oauth/token", authorization_server.uri()), - "scopes_supported": [""], - }))) + .respond_with(ResponseTemplate::new(200).set_body_json(authorization_metadata)) .expect(1) .mount(&server) .await; @@ -87,6 +141,13 @@ async fn assert_expired_token_refresh(with_headers_helper: bool) -> anyhow::Resu .and(body_string_contains(format!( "refresh_token={REFRESH_TOKEN}" ))) + .and({ + let expected_resource = resource_url.clone(); + move |request: &Request| { + url::form_urlencoded::parse(&request.body) + .any(|(name, value)| name == "resource" && value == expected_resource) + } + }) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "access_token": REFRESHED_ACCESS_TOKEN, "token_type": "Bearer", @@ -97,7 +158,7 @@ async fn assert_expired_token_refresh(with_headers_helper: bool) -> anyhow::Resu .mount(&authorization_server) .await; Mock::given(method("POST")) - .and(path("/mcp")) + .and(path(mcp_path)) .and(header("user-agent", RESOURCE_USER_AGENT)) .and(header("x-api-key", RESOURCE_API_KEY)) .and(header( @@ -137,7 +198,7 @@ async fn assert_expired_token_refresh(with_headers_helper: bool) -> anyhow::Resu .await; let codex_home = TempDir::new()?; - let server_url = format!("{}/mcp", server.uri()); + let with_headers_helper = matches!(scenario, OAuthStartupScenario::GatewayHeadersHelper); // Credential storage resolves CODEX_HOME from the process environment. // Run the client half of the test in an ignored helper test so it can use