diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ea12d29eea..b0077619b6 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1085,6 +1085,8 @@ "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4_process_security_environment_spec": "{\"dependencies\":[{\"name\":\"flatbuffers\",\"req\":\"25\"}],\"features\":{},\"strip_prefix\":\"core/generated/process_security_environment_specification\"}", "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4_sandbox_spec": "{\"dependencies\":[{\"name\":\"flatbuffers\",\"req\":\"25\"}],\"features\":{},\"strip_prefix\":\"core/generated/base_container_specification\"}", "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4_wxc_common": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"0.22\"},{\"name\":\"cidr\",\"req\":\"0.3.2\"},{\"name\":\"getrandom\",\"req\":\"0.2\"},{\"default_features\":true,\"features\":[],\"name\":\"mxc_config_contract\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"mxc_schema_support\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"mxc_telemetry\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"nanvix_common\",\"optional\":true},{\"name\":\"schemars\",\"optional\":true,\"req\":\"0.8\"},{\"name\":\"semver\",\"req\":\"1\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":false,\"req\":\"1\"},{\"features\":[\"raw_value\"],\"name\":\"serde_json\",\"req\":\"1\"},{\"name\":\"serde_path_to_error\",\"req\":\"0.1\"},{\"name\":\"thiserror\",\"req\":\"2\"},{\"name\":\"unicode-general-category\",\"req\":\"1\"},{\"name\":\"url\",\"req\":\"2\"},{\"default_features\":true,\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"1\"},{\"name\":\"widestring\",\"req\":\"1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"default_features\":true,\"features\":[\"Win32_Foundation\",\"Win32_Globalization\",\"Win32_NetworkManagement_WindowsFirewall\",\"Win32_Networking_WinSock\",\"Win32_Security\",\"Win32_Security_Authorization\",\"Win32_Security_Credentials\",\"Win32_Security_Isolation\",\"Win32_Storage_FileSystem\",\"Win32_System_Com\",\"Win32_System_Console\",\"Win32_System_Diagnostics_Debug\",\"Win32_System_Diagnostics_Etw\",\"Win32_System_Diagnostics_ToolHelp\",\"Win32_System_Environment\",\"Win32_System_IO\",\"Win32_System_JobObjects\",\"Win32_System_LibraryLoader\",\"Win32_System_Memory\",\"Win32_System_Ole\",\"Win32_System_Pipes\",\"Win32_System_ProcessStatus\",\"Win32_System_Registry\",\"Win32_System_SystemInformation\",\"Win32_System_SystemServices\",\"Win32_System_Threading\",\"Win32_System_Time\",\"Win32_System_Variant\",\"Win32_System_WindowsProgramming\",\"Win32_UI_Shell\"],\"name\":\"windows\",\"optional\":false,\"req\":\"0.62\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"windows-core\",\"req\":\"0.62\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"winreg\",\"req\":\"0.55\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"libc\",\"req\":\"0.2\",\"target\":\"cfg(unix)\"}],\"features\":{\"microvm\":[\"dep:nanvix_common\",\"dep:uuid\"],\"schema-gen\":[\"dep:mxc_schema_support\",\"dep:schemars\"]},\"strip_prefix\":\"core/wxc_common\"}", + "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df_rmcp": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"async-trait\",\"optional\":true,\"req\":\"0.1.89\"},{\"default_features\":true,\"features\":[],\"name\":\"base64\",\"optional\":true,\"req\":\"0.23\"},{\"default_features\":true,\"features\":[],\"name\":\"bytes\",\"optional\":true,\"req\":\"1\"},{\"name\":\"futures\",\"req\":\"0.3\"},{\"default_features\":true,\"features\":[],\"name\":\"hmac\",\"optional\":true,\"req\":\"0.13\"},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true,\"req\":\"1\"},{\"default_features\":true,\"features\":[],\"name\":\"http-body\",\"optional\":true,\"req\":\"1\"},{\"default_features\":true,\"features\":[],\"name\":\"http-body-util\",\"optional\":true,\"req\":\"0.1\"},{\"default_features\":true,\"features\":[\"client\",\"http1\"],\"name\":\"hyper\",\"optional\":true,\"req\":\"1\"},{\"default_features\":true,\"features\":[\"tokio\"],\"name\":\"hyper-util\",\"optional\":true,\"req\":\"0.1\"},{\"default_features\":true,\"features\":[\"serde\"],\"name\":\"indexmap\",\"optional\":false,\"req\":\"2\"},{\"default_features\":true,\"features\":[\"aws_lc_rs\"],\"name\":\"jsonwebtoken\",\"optional\":true,\"req\":\"11\"},{\"default_features\":false,\"features\":[],\"name\":\"oauth2\",\"optional\":true,\"req\":\"5.0\"},{\"default_features\":true,\"features\":[],\"name\":\"pastey\",\"optional\":true,\"req\":\"0.2.0\"},{\"name\":\"pin-project-lite\",\"req\":\"0.2\"},{\"default_features\":true,\"features\":[\"tokio1\"],\"name\":\"process-wrap\",\"optional\":true,\"req\":\"10.0\"},{\"default_features\":true,\"features\":[],\"name\":\"rand\",\"optional\":true,\"req\":\"0.10\"},{\"default_features\":false,\"features\":[\"json\",\"stream\"],\"name\":\"reqwest\",\"optional\":true,\"req\":\"0.13.2\"},{\"default_features\":true,\"features\":[],\"name\":\"rmcp-macros\",\"optional\":true,\"req\":\"3.3.0\"},{\"default_features\":true,\"features\":[\"chrono04\"],\"name\":\"schemars\",\"optional\":true,\"req\":\"1.0\"},{\"default_features\":true,\"features\":[\"derive\",\"rc\"],\"name\":\"serde\",\"optional\":false,\"req\":\"1.0\"},{\"name\":\"serde_json\",\"req\":\"1.0\"},{\"default_features\":true,\"features\":[],\"name\":\"sha2\",\"optional\":true,\"req\":\"0.11\"},{\"default_features\":true,\"features\":[],\"name\":\"sse-stream\",\"optional\":true,\"req\":\"0.2.4\"},{\"name\":\"thiserror\",\"req\":\"2\"},{\"default_features\":true,\"features\":[\"sync\",\"macros\",\"rt\",\"time\"],\"name\":\"tokio\",\"optional\":false,\"req\":\"1\"},{\"default_features\":true,\"features\":[],\"name\":\"tokio-stream\",\"optional\":true,\"req\":\"0.1\"},{\"default_features\":true,\"features\":[],\"name\":\"tokio-util\",\"optional\":false,\"req\":\"0.7\"},{\"default_features\":true,\"features\":[],\"name\":\"tower-service\",\"optional\":true,\"req\":\"0.3\"},{\"default_features\":true,\"features\":[],\"name\":\"tracing\",\"optional\":false,\"req\":\"0.1\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true,\"req\":\"2.4\"},{\"default_features\":true,\"features\":[\"v4\"],\"name\":\"uuid\",\"optional\":true,\"req\":\"1\"},{\"default_features\":true,\"features\":[],\"name\":\"which\",\"optional\":true,\"req\":\"8\"},{\"default_features\":true,\"features\":[],\"name\":\"zeroize\",\"optional\":true,\"req\":\"1\"},{\"default_features\":false,\"features\":[\"serde\",\"clock\",\"std\",\"oldtime\"],\"name\":\"chrono\",\"optional\":false,\"req\":\"0.4.38\",\"target\":\"cfg(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\"))\"},{\"default_features\":false,\"features\":[\"serde\",\"now\"],\"name\":\"chrono\",\"optional\":false,\"req\":\"0.4.38\",\"target\":\"cfg(not(all(target_family = \\\"wasm\\\", target_os = \\\"unknown\\\")))\"}],\"features\":{\"__reqwest\":[\"dep:reqwest\"],\"auth\":[\"dep:async-trait\",\"dep:oauth2\",\"__reqwest\",\"dep:url\"],\"auth-client-credentials-jwt\":[\"auth\",\"dep:jsonwebtoken\",\"uuid\"],\"auth-enterprise-managed\":[\"auth\",\"base64\"],\"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\",\"dep:zeroize\",\"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\"]},\"strip_prefix\":\"./crates/rmcp\"}", + "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df_rmcp-macros": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"darling\",\"optional\":false,\"req\":\"0.24\"},{\"name\":\"proc-macro2\",\"req\":\"1\"},{\"name\":\"quote\",\"req\":\"1\"},{\"name\":\"serde_json\",\"req\":\"1.0\"},{\"default_features\":true,\"features\":[\"full\"],\"name\":\"syn\",\"optional\":false,\"req\":\"3\"}],\"features\":{\"local\":[]},\"strip_prefix\":\"./crates/rmcp-macros\"}", "git+https://github.com/openai-oss-forks/crossterm?rev=efa177859fd9623d57b9fe7ae9bf491ae1ac6ec4#efa177859fd9623d57b9fe7ae9bf491ae1ac6ec4_crossterm": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"base64\",\"optional\":true,\"req\":\"0.22\"},{\"default_features\":true,\"features\":[],\"name\":\"bitflags\",\"optional\":false,\"req\":\"2.9\"},{\"default_features\":true,\"features\":[\"is_variant\"],\"name\":\"derive_more\",\"optional\":true,\"req\":\"2.0.0\"},{\"name\":\"document-features\",\"req\":\"0.2.11\"},{\"default_features\":false,\"features\":[],\"name\":\"futures-core\",\"optional\":true,\"req\":\"0.3\"},{\"name\":\"parking_lot\",\"req\":\"0.12\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"1.0\"},{\"default_features\":true,\"features\":[],\"name\":\"filedescriptor\",\"optional\":true,\"req\":\"0.8\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[],\"name\":\"libc\",\"optional\":true,\"req\":\"0.2\",\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"os-poll\"],\"name\":\"mio\",\"optional\":true,\"req\":\"1.0\",\"target\":\"cfg(unix)\"},{\"default_features\":false,\"features\":[\"std\",\"stdio\",\"termios\"],\"name\":\"rustix\",\"optional\":false,\"req\":\"1\",\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"signal-hook\",\"optional\":true,\"req\":\"0.3.17\",\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"support-v1_0\"],\"name\":\"signal-hook-mio\",\"optional\":true,\"req\":\"0.2.4\",\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[],\"name\":\"crossterm_winapi\",\"optional\":true,\"req\":\"0.9.1\",\"target\":\"cfg(windows)\"},{\"default_features\":true,\"features\":[\"winuser\",\"winerror\"],\"name\":\"winapi\",\"optional\":true,\"req\":\"0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{\"bracketed-paste\":[],\"default\":[\"bracketed-paste\",\"events\",\"windows\",\"derive-more\"],\"derive-more\":[\"dep:derive_more\"],\"event-stream\":[\"dep:futures-core\",\"events\"],\"events\":[\"dep:mio\",\"dep:signal-hook\",\"dep:signal-hook-mio\"],\"osc52\":[\"dep:base64\"],\"serde\":[\"dep:serde\",\"bitflags/serde\"],\"use-dev-tty\":[\"filedescriptor\",\"rustix/process\"],\"windows\":[\"dep:winapi\",\"dep:crossterm_winapi\"]},\"strip_prefix\":\"\"}", "git+https://github.com/openai-oss-forks/tokio-tungstenite?rev=0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186#0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186_tokio-tungstenite": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"sink\",\"std\"],\"name\":\"futures-util\",\"optional\":false},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\",\"tokio-rustls\",\"stream\",\"tungstenite/__rustls-tls\",\"handshake\"],\"connect\":[\"stream\",\"tokio/net\",\"tokio/time\",\"handshake\"],\"default\":[\"connect\",\"handshake\"],\"handshake\":[\"tungstenite/handshake\"],\"native-tls\":[\"native-tls-crate\",\"tokio-native-tls\",\"stream\",\"tungstenite/native-tls\",\"handshake\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\",\"tungstenite/native-tls-vendored\"],\"proxy\":[\"tungstenite/proxy\",\"tokio/net\",\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"stream\":[],\"url\":[\"tungstenite/url\"]},\"strip_prefix\":\"\"}", "git+https://github.com/openai-oss-forks/tungstenite-rs?rev=4fffad30fe373adbdcffab9545e9e9bf4f2fc19f#4fffad30fe373adbdcffab9545e9e9bf4f2fc19f_tungstenite": "{\"dependencies\":[{\"name\":\"bytes\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true},{\"default_features\":false,\"features\":[\"zlib-rs\"],\"name\":\"flate2\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"headers\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true},{\"name\":\"log\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\"},{\"name\":\"rand\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true},{\"name\":\"thiserror\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true},{\"name\":\"utf-8\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true}],\"features\":{\"__rustls-tls\":[\"rustls\",\"rustls-pki-types\"],\"default\":[\"handshake\"],\"deflate\":[\"headers\",\"flate2\"],\"handshake\":[\"data-encoding\",\"headers\",\"httparse\",\"sha1\"],\"headers\":[\"http\",\"dep:headers\"],\"native-tls\":[\"native-tls-crate\"],\"native-tls-vendored\":[\"native-tls\",\"native-tls-crate/vendored\"],\"proxy\":[\"handshake\"],\"rustls-tls-native-roots\":[\"__rustls-tls\",\"rustls-native-certs\"],\"rustls-tls-webpki-roots\":[\"__rustls-tls\",\"webpki-roots\"],\"url\":[\"dep:url\"]},\"strip_prefix\":\"\"}", @@ -1508,7 +1510,7 @@ "proc-macro-error-attr2_2.0.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"}],\"features\":{}}", "proc-macro-error2_2.0.1": "{\"dependencies\":[{\"name\":\"proc-macro-error-attr2\",\"req\":\"=2.0.0\"},{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"features\":[\"full\"],\"kind\":\"dev\",\"name\":\"syn\",\"req\":\"^2\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.99\"}],\"features\":{\"default\":[\"syn-error\"],\"nightly\":[],\"syn-error\":[\"dep:syn\"]}}", "proc-macro2_1.0.106": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"flate2\",\"req\":\"^1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tar\",\"req\":\"^0.4\"},{\"name\":\"unicode-ident\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"proc-macro\"],\"nightly\":[],\"proc-macro\":[],\"span-locations\":[]}}", - "process-wrap_9.0.1": "{\"dependencies\":[{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"indexmap\",\"req\":\"^2.9.0\"},{\"default_features\":false,\"features\":[\"fs\",\"poll\",\"signal\"],\"name\":\"nix\",\"optional\":true,\"req\":\"^0.30.1\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"remoteprocess\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.20.0\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.38.2\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\",\"rt-multi-thread\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.38.2\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"name\":\"windows\",\"optional\":true,\"req\":\"^0.62.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"creation-flags\":[\"dep:windows\",\"windows/Win32_System_Threading\"],\"default\":[\"creation-flags\",\"job-object\",\"kill-on-drop\",\"process-group\",\"process-session\",\"tracing\"],\"job-object\":[\"dep:windows\",\"windows/Win32_Security\",\"windows/Win32_System_Diagnostics_ToolHelp\",\"windows/Win32_System_IO\",\"windows/Win32_System_JobObjects\",\"windows/Win32_System_Threading\"],\"kill-on-drop\":[],\"process-group\":[],\"process-session\":[\"process-group\"],\"reset-sigmask\":[],\"std\":[\"dep:nix\"],\"tokio1\":[\"dep:nix\",\"dep:futures\",\"dep:tokio\"],\"tracing\":[\"dep:tracing\"]}}", + "process-wrap_10.0.0": "{\"dependencies\":[{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.30\"},{\"name\":\"indexmap\",\"req\":\"^2.9.0\"},{\"default_features\":false,\"features\":[\"fs\",\"poll\",\"signal\"],\"name\":\"nix\",\"optional\":true,\"req\":\"^0.31.1\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"remoteprocess\",\"req\":\"^0.5.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.20.0\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1.38.2\"},{\"features\":[\"io-util\",\"macros\",\"process\",\"rt\",\"rt-multi-thread\",\"time\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.38.2\"},{\"name\":\"tracing\",\"optional\":true,\"req\":\"^0.1.40\"},{\"name\":\"windows\",\"optional\":true,\"req\":\"^0.62.2\",\"target\":\"cfg(windows)\"}],\"features\":{\"creation-flags\":[\"dep:windows\",\"windows/Win32_System_Threading\"],\"default\":[\"creation-flags\",\"job-object\",\"kill-on-drop\",\"process-group\",\"process-session\",\"tracing\"],\"job-object\":[\"dep:windows\",\"windows/Win32_Security\",\"windows/Win32_System_Diagnostics_ToolHelp\",\"windows/Win32_System_IO\",\"windows/Win32_System_JobObjects\",\"windows/Win32_System_Threading\"],\"kill-on-drop\":[],\"process-group\":[],\"process-session\":[\"process-group\"],\"reset-sigmask\":[],\"std\":[\"dep:nix\"],\"tokio1\":[\"dep:nix\",\"dep:futures\",\"dep:tokio\"],\"tracing\":[\"dep:tracing\"]}}", "prodash_31.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"argh\",\"req\":\"^0.1.3\"},{\"kind\":\"dev\",\"name\":\"async-executor\",\"req\":\"^1.1.0\"},{\"name\":\"async-io\",\"optional\":true,\"req\":\"^2.2.1\"},{\"kind\":\"dev\",\"name\":\"async-io\",\"req\":\"^2.2.1\"},{\"kind\":\"dev\",\"name\":\"blocking\",\"req\":\"^1.0.0\"},{\"name\":\"bytesize\",\"optional\":true,\"req\":\"^2.0.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.8.1\"},{\"default_features\":false,\"name\":\"crosstermion\",\"optional\":true,\"req\":\"^0.16.0\"},{\"default_features\":false,\"features\":[\"termination\"],\"name\":\"ctrlc\",\"optional\":true,\"req\":\"^3.1.4\"},{\"default_features\":false,\"name\":\"dashmap\",\"optional\":true,\"req\":\"^6.0.1\"},{\"default_features\":false,\"features\":[\"humantime\"],\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.0\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.5\"},{\"default_features\":false,\"name\":\"futures-core\",\"optional\":true,\"req\":\"^0.3.4\"},{\"name\":\"futures-lite\",\"optional\":true,\"req\":\"^2.1.0\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"futures-util\",\"req\":\"^0.3.4\"},{\"name\":\"human_format\",\"optional\":true,\"req\":\"^1.0.3\"},{\"name\":\"is-terminal\",\"optional\":true,\"req\":\"^0.4.9\"},{\"kind\":\"dev\",\"name\":\"is-terminal\",\"req\":\"^0.4.9\"},{\"name\":\"jiff\",\"optional\":true,\"req\":\"^0.2.4\"},{\"name\":\"log\",\"optional\":true,\"req\":\"^0.4.8\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.4.0\"},{\"default_features\":false,\"name\":\"parking_lot\",\"optional\":true,\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.8.1\"},{\"default_features\":false,\"name\":\"signal-hook\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"tui\",\"optional\":true,\"package\":\"ratatui\",\"req\":\"^0.30.0\"},{\"name\":\"tui-react\",\"optional\":true,\"req\":\"^0.24.0\"},{\"name\":\"unicode-segmentation\",\"optional\":true,\"req\":\"^1.6.0\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2.2\"}],\"features\":{\"default\":[\"progress-tree\"],\"local-time\":[\"jiff\"],\"progress-log\":[\"log\"],\"progress-tree\":[\"parking_lot\"],\"progress-tree-hp-hashmap\":[\"dashmap\"],\"progress-tree-log\":[\"log\"],\"render-line\":[\"crosstermion/color\",\"jiff\",\"unicode-width\"],\"render-line-autoconfigure\":[\"is-terminal\"],\"render-line-crossterm\":[\"crosstermion/crossterm\"],\"render-tui\":[\"tui\",\"unicode-segmentation\",\"unicode-width\",\"crosstermion/input-async\",\"tui-react\",\"futures-lite\",\"futures-core\",\"async-io\",\"jiff\"],\"render-tui-crossterm\":[\"crosstermion/tui-react-crossterm\",\"crosstermion/input-async-crossterm\"],\"unit-bytes\":[\"bytesize\"],\"unit-duration\":[\"jiff\"],\"unit-human\":[\"human_format\"]}}", "proptest_1.9.0": "{\"dependencies\":[{\"name\":\"bit-set\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"bit-vec\",\"optional\":true,\"req\":\"^0.8.0\"},{\"name\":\"bitflags\",\"req\":\"^2.9\"},{\"default_features\":false,\"name\":\"num-traits\",\"req\":\"^0.2.15\"},{\"name\":\"proptest-macro\",\"optional\":true,\"req\":\"^0.4.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"rand\",\"req\":\"^0.9\"},{\"default_features\":false,\"name\":\"rand_chacha\",\"req\":\"^0.9\"},{\"name\":\"rand_xorshift\",\"req\":\"^0.4\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.0\"},{\"name\":\"regex-syntax\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"rusty-fork\",\"optional\":true,\"req\":\"^0.3.0\"},{\"name\":\"tempfile\",\"optional\":true,\"req\":\"^3.0\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"=1.0.112\"},{\"name\":\"unarray\",\"req\":\"^0.1.4\"},{\"name\":\"x86\",\"optional\":true,\"req\":\"^0.52.0\"}],\"features\":{\"alloc\":[],\"atomic64bit\":[],\"attr-macro\":[\"proptest-macro\"],\"bit-set\":[\"dep:bit-set\",\"dep:bit-vec\"],\"default\":[\"std\",\"fork\",\"timeout\",\"bit-set\"],\"default-code-coverage\":[\"std\",\"fork\",\"timeout\",\"bit-set\"],\"fork\":[\"std\",\"rusty-fork\",\"tempfile\"],\"handle-panics\":[\"std\"],\"hardware-rng\":[\"x86\"],\"no_std\":[\"num-traits/libm\"],\"std\":[\"rand/std\",\"rand/os_rng\",\"regex-syntax\",\"num-traits/std\"],\"timeout\":[\"fork\",\"rusty-fork/timeout\"],\"unstable\":[]}}", "prost-build_0.14.3": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11\"},{\"name\":\"heck\",\"req\":\">=0.4, <=0.5\"},{\"default_features\":false,\"features\":[\"use_alloc\"],\"name\":\"itertools\",\"req\":\">=0.10, <=0.14\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"default_features\":false,\"name\":\"multimap\",\"req\":\">=0.8, <=0.10\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"petgraph\",\"req\":\"^0.8\"},{\"name\":\"prettyplease\",\"optional\":true,\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"prost\",\"req\":\"^0.14.3\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.14.3\"},{\"default_features\":false,\"name\":\"pulldown-cmark\",\"optional\":true,\"req\":\"^0.13\"},{\"name\":\"pulldown-cmark-to-cmark\",\"optional\":true,\"req\":\"^22\"},{\"default_features\":false,\"features\":[\"std\",\"unicode-bool\"],\"name\":\"regex\",\"req\":\"^1.8.1\"},{\"features\":[\"full\"],\"name\":\"syn\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"tempfile\",\"req\":\"^3\"}],\"features\":{\"cleanup-markdown\":[\"dep:pulldown-cmark\",\"dep:pulldown-cmark-to-cmark\"],\"default\":[\"format\"],\"format\":[\"dep:prettyplease\",\"dep:syn\"]}}", @@ -1615,8 +1617,6 @@ "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\"]}}", "rkyv_0.8.18": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"ahash\",\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"arrayvec-0_7\",\"optional\":true,\"package\":\"arrayvec\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"simdutf8\"],\"name\":\"bytecheck\",\"optional\":true,\"req\":\"^0.8\"},{\"default_features\":false,\"name\":\"bytes-1\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"divan\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.17\"},{\"default_features\":false,\"name\":\"hashbrown-0_14\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"hashbrown-0_15\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.15\"},{\"default_features\":false,\"name\":\"hashbrown-0_16\",\"optional\":true,\"package\":\"hashbrown\",\"req\":\"^0.16\"},{\"default_features\":false,\"name\":\"indexmap-2\",\"optional\":true,\"package\":\"indexmap\",\"req\":\"^2\"},{\"default_features\":false,\"name\":\"munge\",\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"ptr_meta\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"rancor\",\"req\":\"^0.1\"},{\"default_features\":false,\"name\":\"rend\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"rkyv_derive\",\"req\":\"=0.8.18\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"smallvec-1\",\"optional\":true,\"package\":\"smallvec\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"smol_str-0_2\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"smol_str-0_3\",\"optional\":true,\"package\":\"smol_str\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"thin-vec-0_2\",\"optional\":true,\"package\":\"thin-vec\",\"req\":\"^0.2.12\"},{\"default_features\":false,\"name\":\"tinyvec-1\",\"optional\":true,\"package\":\"tinyvec\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"triomphe-0_1\",\"optional\":true,\"package\":\"triomphe\",\"req\":\"^0.1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"uuid-1\",\"optional\":true,\"package\":\"uuid\",\"req\":\"^1\"}],\"features\":{\"aligned\":[],\"alloc\":[\"dep:hashbrown\",\"tinyvec-1?/alloc\",\"rancor/alloc\"],\"big_endian\":[],\"bytecheck\":[\"dep:bytecheck\",\"rend/bytecheck\",\"rkyv_derive/bytecheck\"],\"default\":[\"std\",\"bytecheck\"],\"hashbrown-0_17\":[\"dep:hashbrown\"],\"indexmap-2\":[\"dep:indexmap-2\",\"alloc\"],\"little_endian\":[],\"pointer_width_16\":[],\"pointer_width_32\":[],\"pointer_width_64\":[],\"std\":[\"alloc\",\"bytes-1?/std\",\"indexmap-2?/std\",\"ptr_meta/std\",\"uuid-1?/std\"],\"triomphe-0_1\":[\"dep:triomphe-0_1\",\"alloc\"],\"unaligned\":[],\"uuid-1\":[\"dep:uuid-1\",\"bytecheck?/uuid-1\"]}}", "rkyv_derive_0.8.18": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"proc-macro\"],\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"clone-impls\",\"derive\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^3.0\"}],\"features\":{\"bytecheck\":[],\"default\":[]}}", - "rmcp-macros_3.2.0": "{\"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.2.0": "{\"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\":[\"serde\"],\"name\":\"indexmap\",\"req\":\"^2\"},{\"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.2.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\"},{\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"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\",\"dep:zeroize\",\"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\"]}}", "rtc-datachannel_0.20.3": "{\"dependencies\":[{\"name\":\"bytes\",\"req\":\"^1.12.0\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.45\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.11\"},{\"name\":\"log\",\"req\":\"^0.4.33\"},{\"name\":\"sansio\",\"req\":\"^1\"},{\"name\":\"sctp\",\"package\":\"rtc-sctp\",\"req\":\"^0.20.3\"},{\"default_features\":false,\"features\":[\"marshal\"],\"name\":\"shared\",\"package\":\"rtc-shared\",\"req\":\"^0.20.3\"}],\"features\":{}}", "rtc-dtls_0.20.3": "{\"dependencies\":[{\"name\":\"aes\",\"req\":\"^0.8.4\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.80\"},{\"default_features\":false,\"features\":[\"aws-lc-sys\"],\"name\":\"aws-lc-rs\",\"optional\":true,\"req\":\"^1.17.3\"},{\"name\":\"bytecheck\",\"req\":\"^0.8\"},{\"name\":\"byteorder\",\"req\":\"^1.5.0\"},{\"name\":\"bytes\",\"req\":\"^1.12.0\"},{\"features\":[\"block-padding\",\"alloc\"],\"name\":\"cbc\",\"req\":\"^0.1.2\"},{\"name\":\"ccm\",\"req\":\"^0.5.0\"},{\"name\":\"chacha20poly1305\",\"req\":\"^0.10.1\"},{\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4.45\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4.6.1\"},{\"kind\":\"dev\",\"name\":\"core_affinity\",\"req\":\"^0.8.1\"},{\"kind\":\"dev\",\"name\":\"ctrlc\",\"req\":\"^3.5.2\"},{\"name\":\"der-parser\",\"req\":\"^9.0.0\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.11\"},{\"kind\":\"dev\",\"name\":\"futures\",\"req\":\"^0.3.30\"},{\"name\":\"hmac\",\"req\":\"^0.12.1\"},{\"kind\":\"dev\",\"name\":\"local-sync\",\"req\":\"^0.1.1\"},{\"name\":\"log\",\"req\":\"^0.4.33\"},{\"features\":[\"default\",\"ecdh\",\"ecdsa\"],\"name\":\"p256\",\"req\":\"^0.13.2\"},{\"name\":\"p384\",\"req\":\"^0.13.0\"},{\"name\":\"pem\",\"optional\":true,\"req\":\"^3.0.3\"},{\"name\":\"rand\",\"req\":\"^0.10.1\"},{\"name\":\"rand_core\",\"req\":\"^0.6.4\"},{\"default_features\":false,\"features\":[\"pem\"],\"name\":\"rcgen\",\"req\":\"^0.14.8\"},{\"name\":\"ring\",\"optional\":true,\"req\":\"^0.17.14\"},{\"name\":\"rkyv\",\"req\":\"^0.8.17\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"req\":\"^0.23.27\"},{\"features\":[\"std\"],\"name\":\"sec1\",\"req\":\"^0.7\"},{\"name\":\"sha1\",\"req\":\"^0.10.6\"},{\"name\":\"sha2\",\"req\":\"^0.10.8\"},{\"default_features\":false,\"features\":[\"crypto\",\"replay\"],\"name\":\"shared\",\"package\":\"rtc-shared\",\"req\":\"^0.20.3\"},{\"name\":\"subtle\",\"req\":\"^2.5.0\"},{\"features\":[\"static_secrets\"],\"name\":\"x25519-dalek\",\"req\":\"^2.0.1\"},{\"name\":\"x509-parser\",\"req\":\"^0.16.0\"}],\"features\":{\"aws-lc-rs\":[\"dep:aws-lc-rs\",\"rustls/aws-lc-rs\",\"rcgen/aws_lc_rs\"],\"default\":[\"ring\"],\"pem\":[\"dep:pem\"],\"ring\":[\"dep:ring\",\"rustls/ring\",\"rcgen/ring\"]}}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 10a8c44279..ea89723953 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -11829,13 +11829,13 @@ dependencies = [ [[package]] name = "process-wrap" -version = "9.0.1" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1395947e69c07400ef4d43db0051d6f773c21f647ad8b97382fc01f0204c60" +checksum = "0e3f4237d0e4741eb50bc5584db701f1299c85fa31ff0274dd6445e79dc42d12" dependencies = [ "futures", "indexmap 2.14.0", - "nix 0.30.1", + "nix 0.31.3", "tokio", "tracing", "windows 0.62.2", @@ -13028,9 +13028,8 @@ dependencies = [ [[package]] name = "rmcp" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b6914fac0be956fe704a38239c3f44a9f841d1b06a5713d2f638065593f5b5" +version = "3.3.0" +source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df" dependencies = [ "async-trait", "base64 0.23.0", @@ -13064,9 +13063,8 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf1c49bd4d52014b94db0877410db273c2008f01628b0252a2e9460ad9b7fda" +version = "3.3.0" +source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df" dependencies = [ "darling 0.24.0", "proc-macro2", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 0e01c4534b..fc1becc762 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -446,7 +446,8 @@ regex-automata = { version = "0.4.13", default-features = false, features = ["st regex-lite = "0.1.8" regex-syntax = "0.8.8" reqwest = { version = "0.12", features = ["cookies"] } -rmcp = { version = "=3.2.0", default-features = false } +# rmcp-v3.3.0 includes enterprise authorization helpers (rust-sdk#1234). +rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", rev = "3e636cab26c013eca5131103c03d20237f12c4df", version = "=3.3.0", default-features = false } runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" } rustix = { version = "1.1.4", features = ["net"] } rustls = { version = "0.23.45", default-features = false, features = [ diff --git a/codex-rs/deny.toml b/codex-rs/deny.toml index 71617b4918..082ad98246 100644 --- a/codex-rs/deny.toml +++ b/codex-rs/deny.toml @@ -317,6 +317,7 @@ allow-git = [ "https://github.com/hyperium/h3", "https://github.com/juberti-oai/rust-sdks.git", "https://github.com/microsoft/mxc", + "https://github.com/modelcontextprotocol/rust-sdk", "https://github.com/openai-oss-forks/crossterm", "https://github.com/openai-oss-forks/tokio-tungstenite", "https://github.com/openai-oss-forks/tungstenite-rs", diff --git a/codex-rs/rmcp-client/Cargo.toml b/codex-rs/rmcp-client/Cargo.toml index 671bfd9ecc..d44f91328d 100644 --- a/codex-rs/rmcp-client/Cargo.toml +++ b/codex-rs/rmcp-client/Cargo.toml @@ -34,7 +34,7 @@ keyring = { workspace = true, features = ["crypto-rust"] } memchr = { workspace = true } oauth2 = "5" rmcp = { workspace = true, default-features = false, features = [ - "auth", + "auth-enterprise-managed", "base64", "client", "macros", diff --git a/codex-rs/rmcp-client/src/ema_auth_policy.rs b/codex-rs/rmcp-client/src/ema_auth_policy.rs index 42057b1f78..ddedfe1333 100644 --- a/codex-rs/rmcp-client/src/ema_auth_policy.rs +++ b/codex-rs/rmcp-client/src/ema_auth_policy.rs @@ -29,26 +29,6 @@ pub(crate) fn ema_reauthentication_required(message: &'static str) -> anyhow::Er anyhow::Error::new(EmaAuthFailure::ReauthenticationRequired).context(message) } -pub(crate) fn safe_oauth_error_code(code: Option<&str>) -> &str { - code.filter(|code| { - matches!( - *code, - "invalid_request" - | "invalid_client" - | "invalid_grant" - | "invalid_scope" - | "invalid_target" - | "unauthorized_client" - | "unsupported_grant_type" - | "access_denied" - | "temporarily_unavailable" - | "server_error" - | "insufficient_user_authentication" - ) - }) - .unwrap_or("OAuth token request rejected") -} - pub(crate) fn validate_ema_public_client_auth( advertised_methods: Option<&Value>, issuer_description: &str, diff --git a/codex-rs/rmcp-client/src/ema_claims.rs b/codex-rs/rmcp-client/src/ema_claims.rs index 886f148a32..e70490047d 100644 --- a/codex-rs/rmcp-client/src/ema_claims.rs +++ b/codex-rs/rmcp-client/src/ema_claims.rs @@ -1,7 +1,5 @@ -//! Validate token routing and signed authorization before forwarding an ID-JAG. +//! Validate OIDC identity assertion bindings before storing enterprise credentials. -use std::collections::HashSet; -use std::time::Duration; use std::time::SystemTime; use std::time::UNIX_EPOCH; @@ -13,14 +11,11 @@ use base64::engine::general_purpose::URL_SAFE_NO_PAD; use serde::Deserialize; use serde::de::DeserializeOwned; -use crate::ema_exchange::EmaAccessToken; - pub(crate) const ID_JAG_TOKEN_TYPE: &str = "urn:ietf:params:oauth:token-type:id-jag"; #[derive(Deserialize)] struct JwtHeader { alg: String, - typ: Option, } #[derive(Deserialize)] @@ -30,16 +25,7 @@ enum OAuthResource { Multiple(Vec), } -impl OAuthResource { - fn is_exact(&self, expected: &str) -> bool { - match self { - Self::Single(value) => value == expected, - Self::Multiple(values) => values.as_slice() == [expected], - } - } -} - -fn signed_jwt(token: &str) -> Result<(JwtHeader, T)> { +fn signed_jwt(token: &str) -> Result { let mut parts = token.split('.'); let (Some(header), Some(payload), Some(signature), None) = (parts.next(), parts.next(), parts.next(), parts.next()) @@ -56,7 +42,7 @@ fn signed_jwt(token: &str) -> Result<(JwtHeader, T)> { } let claims = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(payload)?) .map_err(|_| anyhow!("invalid identity assertion JWT claims"))?; - Ok((header, claims)) + Ok(claims) } #[derive(Deserialize)] @@ -73,7 +59,7 @@ pub(crate) fn oidc_identity( expected_issuer: &str, expected_audience: &str, ) -> Result { - let (_, claims): (_, OidcClaims) = signed_jwt(assertion)?; + let claims: OidcClaims = signed_jwt(assertion)?; if claims.iss != expected_issuer || claims.sub.trim().is_empty() { bail!("OIDC identity assertion issuer or subject does not match the enterprise IdP"); } @@ -108,143 +94,3 @@ pub fn validate_oidc_identity_assertion( } Ok(()) } - -#[derive(Deserialize)] -struct IdJagClaims { - iss: String, - sub: String, - aud: OAuthResource, - client_id: String, - jti: String, - exp: u64, - iat: u64, - resource: OAuthResource, - scope: Option, -} - -pub(crate) struct IdJagBinding<'a> { - pub issuer: &'a str, - pub audience: &'a str, - pub client_id: &'a str, - pub resource: &'a str, - /// Empty means the scope parameter was omitted, not an empty authorization ceiling. - pub requested_scopes: &'a HashSet<&'a str>, -} - -#[derive(Deserialize)] -pub(crate) struct IdJagResponse { - pub access_token: String, - issued_token_type: String, - token_type: String, - resource: Option, - scope: Option, - refresh_token: Option, -} - -impl IdJagResponse { - pub(crate) fn validate(&self, binding: IdJagBinding<'_>) -> Result> { - if self.issued_token_type != ID_JAG_TOKEN_TYPE - || self.token_type != "N_A" - || self.refresh_token.is_some() - { - bail!("enterprise IdP returned an unsupported ID-JAG token type or refresh token"); - } - let (header, claims): (_, IdJagClaims) = signed_jwt(&self.access_token)?; - if header.typ.as_deref() != Some("oauth-id-jag+jwt") - || claims.iss != binding.issuer - || !claims.aud.is_exact(binding.audience) - || claims.client_id != binding.client_id - || claims.sub.trim().is_empty() - || claims.jti.trim().is_empty() - { - bail!("ID-JAG type, issuer, audience, client, subject, or JWT ID is invalid"); - } - let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); - if claims.exp <= now || claims.iat > now.saturating_add(60) { - bail!("enterprise IdP returned an expired or future-issued ID-JAG"); - } - if !claims.resource.is_exact(binding.resource) - || self - .resource - .as_ref() - .is_some_and(|value| !value.is_exact(binding.resource)) - { - bail!("ID-JAG must authorize exactly the configured MCP resource"); - } - let granted = match claims.scope.as_deref() { - Some(scope) => parse_scope(scope)?, - None if binding.requested_scopes.is_empty() => HashSet::new(), - None => bail!("ID-JAG is missing the requested scope authorization"), - }; - if !binding.requested_scopes.is_empty() && !granted.is_subset(binding.requested_scopes) { - bail!("ID-JAG contains a scope outside the enterprise authorization request"); - } - match self.scope.as_deref() { - Some(scope) if parse_scope(scope)? != granted => { - bail!("enterprise IdP token response scope does not match the signed ID-JAG") - } - None if !binding.requested_scopes.is_empty() - && granted != *binding.requested_scopes => - { - bail!("enterprise IdP token response omitted its narrowed scope") - } - _ => {} - } - Ok(granted.into_iter().map(str::to_string).collect()) - } -} - -fn parse_scope(scope: &str) -> Result> { - let scopes = scope.split_ascii_whitespace().collect::>(); - if scopes.is_empty() || scopes.len() != scope.split_ascii_whitespace().count() { - bail!("enterprise authorization contains malformed or duplicate scopes"); - } - Ok(scopes) -} - -#[derive(Deserialize)] -pub(crate) struct McpAccessTokenResponse { - access_token: String, - token_type: String, - expires_in: Option, - resource: Option, - scope: Option, - refresh_token: Option, -} - -impl McpAccessTokenResponse { - pub(crate) fn validate( - self, - resource: &str, - id_jag_scopes: &HashSet, - ) -> Result { - if !self.token_type.eq_ignore_ascii_case("bearer") || self.access_token.trim().is_empty() { - bail!("MCP authorization server returned an invalid bearer token"); - } - if self.refresh_token.is_some() || self.expires_in == Some(0) { - bail!("MCP authorization server returned a refresh token or zero token lifetime"); - } - // The stable EMA response does not require the resource to be echoed; - // when present, it must agree with the resource bound in the ID-JAG. - if self - .resource - .as_ref() - .is_some_and(|returned| !returned.is_exact(resource)) - { - bail!("MCP access token must authorize exactly the configured MCP resource"); - } - // RFC 6749 defines an omitted scope as unchanged from the request. Here - // that authority is the scope carried by the validated ID-JAG. - if let Some(scope) = self.scope.as_deref() - && !parse_scope(scope)? - .iter() - .all(|scope| id_jag_scopes.contains(*scope)) - { - bail!("MCP authorization server granted a scope outside the ID-JAG authorization"); - } - Ok(EmaAccessToken { - access_token: self.access_token, - expires_in: self.expires_in.map(Duration::from_secs), - }) - } -} diff --git a/codex-rs/rmcp-client/src/ema_exchange.rs b/codex-rs/rmcp-client/src/ema_exchange.rs index 7b6131653a..243a05c94d 100644 --- a/codex-rs/rmcp-client/src/ema_exchange.rs +++ b/codex-rs/rmcp-client/src/ema_exchange.rs @@ -1,31 +1,25 @@ -//! Non-interactive ID-JAG exchange against explicitly trusted OAuth endpoints. +//! Adapt the SDK enterprise exchange to Codex HTTP routing and credential lifecycle errors. -use std::collections::HashSet; use std::sync::Arc; use std::time::Duration; use anyhow::Result; -use anyhow::anyhow; -use anyhow::bail; use codex_exec_server::HttpClient; -use rmcp::transport::auth::OAuthHttpRedirectPolicy; -use serde::Deserialize; -use serde::de::DeserializeOwned; +use oauth2::RefreshToken; +use rmcp::transport::auth::enterprise::EmaAuthorizationServer; +use rmcp::transport::auth::enterprise::EmaError; +use rmcp::transport::auth::enterprise::EmaExchangeRequest; +use rmcp::transport::auth::enterprise::EmaExchangeStage; use crate::ema_auth_policy::EmaAuthFailure; use crate::ema_auth_policy::EmaInvalidGrantSource; -use crate::ema_auth_policy::safe_oauth_error_code; -use crate::ema_auth_policy::validate_ema_oauth_endpoint; -use crate::ema_claims::ID_JAG_TOKEN_TYPE; -use crate::ema_claims::IdJagBinding; -use crate::ema_claims::IdJagResponse; -use crate::ema_claims::McpAccessTokenResponse; use crate::http_client_adapter::StreamableHttpRedirectMode; use crate::oauth_http_client::OAuthHttpClientAdapter; use crate::utils::build_default_headers; pub(crate) const TOKEN_EXCHANGE_GRANT_TYPE: &str = "urn:ietf:params:oauth:grant-type:token-exchange"; +#[cfg(test)] pub(crate) const JWT_BEARER_GRANT_TYPE: &str = "urn:ietf:params:oauth:grant-type:jwt-bearer"; /// A resource-bound bearer and its server-reported lifetime, with redacted diagnostics. @@ -63,155 +57,67 @@ pub struct EmaIdJagExchangeRequest<'a> { /// Exchanges an enterprise IdP credential for a resource-bound MCP bearer token. pub async fn exchange_id_jag(request: EmaIdJagExchangeRequest<'_>) -> Result { - for (endpoint, description) in [ - (request.resource, "enterprise MCP resource"), - ( - request.authorization_server_issuer, - "MCP authorization server issuer", - ), - ( - request.authorization_server_token_endpoint, - "MCP token endpoint", - ), - (request.idp_issuer, "enterprise IdP issuer"), - (request.idp_token_endpoint, "enterprise IdP token endpoint"), - ] { - validate_ema_oauth_endpoint(endpoint, description)?; - } - if request.authorization_server_issuer == request.idp_issuer { - bail!("enterprise IdP and MCP authorization server issuers must be different for ID-JAG"); - } - if request.mcp_client_id.trim().is_empty() || request.idp_client_id.trim().is_empty() { - bail!("enterprise authorization requires the registered IdP and MCP client IDs"); - } - if request.refresh_token.trim().is_empty() { - bail!("enterprise IdP refresh token must not be empty"); - } - let requested_scopes = request - .scopes - .iter() - .map(String::as_str) - .collect::>(); - if requested_scopes.len() != request.scopes.len() - || request - .scopes - .iter() - .any(|scope| scope.is_empty() || scope.chars().any(char::is_whitespace)) - { - bail!("enterprise MCP authorization scopes must be distinct, non-empty scope tokens"); - } - let scope = (!request.scopes.is_empty()).then(|| request.scopes.join(" ")); - let mut params = vec![ - ("grant_type", TOKEN_EXCHANGE_GRANT_TYPE), - ("requested_token_type", ID_JAG_TOKEN_TYPE), - ("audience", request.authorization_server_issuer), - ("resource", request.resource), - ("subject_token", request.refresh_token.as_str()), - ( - "subject_token_type", - "urn:ietf:params:oauth:token-type:refresh_token", - ), - ]; - if let Some(scope) = scope.as_deref() { - params.push(("scope", scope)); - } - let id_jag: IdJagResponse = post_form( - &request.idp_http_client, - request.idp_token_endpoint, - ¶ms, - request.idp_client_id, - EmaInvalidGrantSource::EnterpriseIdentity, - "enterprise IdP ID-JAG exchange", - ) - .await?; - let granted_scopes = id_jag.validate(IdJagBinding { - issuer: request.idp_issuer, - audience: request.authorization_server_issuer, - client_id: request.mcp_client_id, - resource: request.resource, - requested_scopes: &requested_scopes, - })?; - // Only the signed assertion carries authority to the Resource AS. Repeating - // the requested resource or scopes could undo enterprise policy narrowing. - let access_token: McpAccessTokenResponse = post_form( - &request.resource_http_client, - request.authorization_server_token_endpoint, - &[ - ("grant_type", JWT_BEARER_GRANT_TYPE), - ("assertion", id_jag.access_token.as_str()), - ], - request.mcp_client_id, - EmaInvalidGrantSource::ResourceAuthorization, - "MCP JWT bearer exchange", - ) - .await?; - access_token.validate(request.resource, &granted_scopes) -} - -#[derive(Deserialize)] -struct OAuthErrorResponse { - error: Option, -} - -pub(crate) async fn post_form( - http_client: &Arc, - url: &str, - params: &[(&str, &str)], - client_id: &str, - invalid_grant_source: EmaInvalidGrantSource, - operation: &str, -) -> Result { - let client = OAuthHttpClientAdapter::new_with_redirect_mode( - Arc::clone(http_client), + let idp_http = OAuthHttpClientAdapter::new_with_redirect_mode( + request.idp_http_client, build_default_headers(/*http_headers*/ None, /*env_http_headers*/ None)?, - url, + request.idp_token_endpoint, /*has_configured_headers*/ false, StreamableHttpRedirectMode::Legacy, )?; - let body = { - let mut form = url::form_urlencoded::Serializer::new(String::new()); - form.extend_pairs(params.iter().copied()); - form.append_pair("client_id", client_id); - form.finish().into_bytes() - }; - let builder = oauth2::http::Request::builder() - .method("POST") - .uri(url) - .header("content-type", "application/x-www-form-urlencoded") - .header("accept", "application/json"); - let response = client - .execute_request( - builder.body(body)?, - OAuthHttpRedirectPolicy::Stop, - Some(Duration::from_secs(30)), - ) - .await - .map_err(|error| anyhow!("{operation} request failed: {error}"))?; - if !response.status().is_success() { - let error = serde_json::from_slice::(response.body()).ok(); - // Provider-controlled text may reflect the submitted assertion or secret. - // Only known OAuth codes may reach callers. - let code = safe_oauth_error_code(error.as_ref().and_then(|error| error.error.as_deref())); - if code == "invalid_grant" { - return Err(anyhow::Error::new(EmaAuthFailure::InvalidGrant { - grant_source: invalid_grant_source, - }) - .context(format!( - "{operation} returned HTTP {}: invalid_grant", - response.status() - ))); + let resource_http = OAuthHttpClientAdapter::new_with_redirect_mode( + request.resource_http_client, + build_default_headers(/*http_headers*/ None, /*env_http_headers*/ None)?, + request.authorization_server_token_endpoint, + /*has_configured_headers*/ false, + StreamableHttpRedirectMode::Legacy, + )?; + let refresh_token = RefreshToken::new(request.refresh_token); + // Discovery has already verified both public-client registrations. The SDK owns + // token validation and the two exchanges; Codex retains routing and credential policy. + let token = EmaExchangeRequest::new( + EmaAuthorizationServer::new( + request.idp_issuer, + request.idp_token_endpoint, + request.idp_client_id, + ), + EmaAuthorizationServer::new( + request.authorization_server_issuer, + request.authorization_server_token_endpoint, + request.mcp_client_id, + ), + request.resource, + &refresh_token, + ) + .with_scopes(request.scopes.iter().cloned()) + .exchange(&idp_http, &resource_http) + .await + .map_err(|error| { + let failure = match &error { + EmaError::InvalidGrant(EmaExchangeStage::IdentityProvider) => { + Some(EmaAuthFailure::InvalidGrant { + grant_source: EmaInvalidGrantSource::EnterpriseIdentity, + }) + } + EmaError::InvalidGrant(EmaExchangeStage::ResourceAuthorizationServer) => { + Some(EmaAuthFailure::InvalidGrant { + grant_source: EmaInvalidGrantSource::ResourceAuthorization, + }) + } + EmaError::InsufficientUserAuthentication(_) => { + Some(EmaAuthFailure::InsufficientUserAuthentication) + } + // Future SDK stages must not invalidate the shared enterprise credential. + _ => None, + }; + match failure { + Some(failure) => anyhow::Error::new(failure).context(error), + None => anyhow::Error::new(error), } - if code == "insufficient_user_authentication" { - return Err( - anyhow::Error::new(EmaAuthFailure::InsufficientUserAuthentication).context( - format!("{operation} returned HTTP {}: {code}", response.status()), - ), - ); - } - bail!("{operation} returned HTTP {}: {code}", response.status()); - } - serde_json::from_slice(response.body()) - .map_err(|_| anyhow!("failed to parse {operation} response")) + })?; + Ok(EmaAccessToken { + access_token: token.access_token.secret().to_owned(), + expires_in: token.expires_in, + }) } #[cfg(test)] diff --git a/codex-rs/rmcp-client/src/ema_exchange_tests.rs b/codex-rs/rmcp-client/src/ema_exchange_tests.rs index 4671790710..1e7e1a3862 100644 --- a/codex-rs/rmcp-client/src/ema_exchange_tests.rs +++ b/codex-rs/rmcp-client/src/ema_exchange_tests.rs @@ -19,6 +19,8 @@ use wiremock::matchers::method; use wiremock::matchers::path; use super::*; +use crate::ema_auth_policy::validate_ema_oauth_endpoint; +use crate::ema_claims::ID_JAG_TOKEN_TYPE; use crate::ema_claims::validate_oidc_identity_assertion; fn http_client() -> Arc { @@ -42,7 +44,7 @@ fn unique_form_fields(body: &[u8]) -> HashMap { fn jwt(claims: &Value) -> String { format!( - "{}.{}.signature", + "{}.{}.c2lnbmF0dXJl", URL_SAFE_NO_PAD.encode(br#"{"alg":"ES256","typ":"oauth-id-jag+jwt"}"#), URL_SAFE_NO_PAD.encode(serde_json::to_vec(claims).expect("serialize claims")) ) @@ -70,6 +72,25 @@ fn token_response() -> Value { json!({"access_token":"resource-token","token_type":"Bearer","expires_in":300}) } +async fn exchange(server: &MockServer, refresh_token: &str) -> Result { + let issuer = format!("{}/idp", server.uri()); + let audience = format!("{}/as", server.uri()); + exchange_id_jag(EmaIdJagExchangeRequest { + resource: &format!("{}/mcp", server.uri()), + scopes: &[], + mcp_client_id: "mcp-client", + authorization_server_issuer: &audience, + authorization_server_token_endpoint: &format!("{audience}/token"), + idp_token_endpoint: &format!("{issuer}/token"), + idp_issuer: &issuer, + idp_client_id: "idp-client", + refresh_token: refresh_token.to_string(), + idp_http_client: http_client(), + resource_http_client: http_client(), + }) + .await +} + #[tokio::test] async fn public_client_round_trip_preserves_signed_narrowing() -> Result<()> { let requested_scopes = ["files.read".to_string(), "files.write".to_string()]; @@ -185,168 +206,73 @@ async fn public_client_round_trip_preserves_signed_narrowing() -> Result<()> { Ok(()) } -#[test] -fn signed_claims_and_resource_tokens_cannot_widen_authority() -> Result<()> { - let requested = HashSet::from(["files.read", "files.write"]); - let original = claims( - "https://idp.example", - "https://as.example", - "https://mcp.example", - ); - let binding = || IdJagBinding { - issuer: "https://idp.example", - audience: "https://as.example", - client_id: "mcp-client", - resource: "https://mcp.example", - requested_scopes: &requested, - }; - let valid: IdJagResponse = serde_json::from_value(jag_response(&original))?; - let granted = valid.validate(binding())?; - assert_eq!(granted, HashSet::from(["files.read".to_string()])); - for (requested_scopes, signed_scope, response_scope, valid) in [ - ("", Some("files.read"), Some("files.read"), true), - ("", Some("files.read"), None, true), - ("", None, None, true), - ("files.read", Some("files.read"), None, true), - ("files.read files.write", Some("files.read"), None, false), - ( - "files.read", - Some("files.read files.write"), - Some("files.read files.write"), - false, - ), - ("files.read", None, None, false), - ("", Some("files.read"), Some("files.write"), false), - ("", Some(" \t"), None, false), - ("", Some("files.read files.read"), Some("files.read"), false), - ("", Some("files.read"), Some(" \t"), false), - ("", Some("files.read"), Some("files.read files.read"), false), +#[tokio::test] +async fn sdk_validation_rejects_unsupported_authorization_before_returning_a_bearer() { + for location in [ + "signed claims", + "IdP response", + "resource response", + "signed scope", ] { - let requested_scopes = requested_scopes.split_ascii_whitespace().collect(); - let mut scoped_claims = original.clone(); - scoped_claims - .as_object_mut() - .expect("ID-JAG claims") - .remove("scope"); - if let Some(scope) = signed_scope { - scoped_claims["scope"] = json!(scope); - } - let mut response = jag_response(&scoped_claims); - response - .as_object_mut() - .expect("ID-JAG response") - .remove("scope"); - if let Some(scope) = response_scope { - response["scope"] = json!(scope); - } - let response: IdJagResponse = serde_json::from_value(response)?; - let result = response.validate(IdJagBinding { - requested_scopes: &requested_scopes, - ..binding() - }); - assert_eq!( - result.is_ok(), - valid, - "requested {requested_scopes:?}, signed {signed_scope:?}, response {response_scope:?}" + let server = MockServer::builder().start().await; + let original = claims( + &format!("{}/idp", server.uri()), + &format!("{}/as", server.uri()), + &format!("{}/mcp", server.uri()), ); - if let Ok(granted) = result { - assert_eq!( - granted, - signed_scope - .into_iter() - .map(str::to_string) - .collect::>() - ); - for (scope, valid_token) in [ - (None, true), - (Some("files.read"), signed_scope.is_some()), - (Some("files.read files.write"), false), - ] { - let mut response = token_response(); - if let Some(scope) = scope { - response["scope"] = json!(scope); + let mut jag = jag_response(&original); + let mut bearer = token_response(); + let details = json!([{"type": "payment_initiation"}]); + match location { + "signed claims" | "signed scope" => { + let mut changed = original; + if location == "signed claims" { + changed["authorization_details"] = details; + } else { + changed["scope"] = json!("files.read\tfiles.write"); } - let response: McpAccessTokenResponse = serde_json::from_value(response)?; - assert_eq!( - response.validate("https://mcp.example", &granted).is_ok(), - valid_token, - "ID-JAG {signed_scope:?}, bearer {scope:?}" - ); + jag = jag_response(&changed); } + "IdP response" => jag["authorization_details"] = details, + "resource response" => bearer["authorization_details"] = details, + _ => unreachable!(), } - } - let mut rotated = jag_response(&original); - rotated["refresh_token"] = json!("unsupported-jag-refresh-token"); - let response: IdJagResponse = serde_json::from_value(rotated)?; - assert!(response.validate(binding()).is_err()); - for header in [ - json!({"alg": "ES256", "typ": "JWT"}), - json!({"alg": "ES256"}), - json!({"alg": "none", "typ": "oauth-id-jag+jwt"}), - ] { - let mut changed = jag_response(&original); - changed["access_token"] = json!(format!( - "{}.{}.signature", - URL_SAFE_NO_PAD.encode(serde_json::to_vec(&header)?), - URL_SAFE_NO_PAD.encode(serde_json::to_vec(&original)?), - )); - let response: IdJagResponse = serde_json::from_value(changed)?; - assert!( - response.validate(binding()).is_err(), - "accepted invalid ID-JAG header {header}" - ); - } - for (field, value) in [ - ("iss", json!("https://attacker.example")), - ("aud", json!("https://attacker.example")), - ("client_id", json!("other-client")), - ("sub", json!("")), - ("jti", json!("")), - ("exp", json!(0)), - ("iat", json!(u64::MAX)), - ("scope", json!("files.admin")), - ( - "resource", - json!(["https://mcp.example", "https://other.example"]), - ), - ] { - let mut changed = original.clone(); - changed[field] = value; - let response: IdJagResponse = serde_json::from_value(jag_response(&changed))?; - assert!( - response.validate(binding()).is_err(), - "accepted changed {field}" - ); - } - for (field, value) in [ - ("scope", json!("files.read files.write")), - ("scope", json!("files.read files.read")), - ("resource", json!("https://other.example")), - ("expires_in", json!(0)), - ("refresh_token", json!("refresh")), - ("token_type", json!("N_A")), - ("access_token", json!("")), - ] { - let mut changed = token_response(); - changed[field] = value; - let response: McpAccessTokenResponse = serde_json::from_value(changed)?; - assert!( - response.validate("https://mcp.example", &granted).is_err(), - "accepted changed {field}" - ); - } - let mut explicit_binding = token_response(); - explicit_binding["resource"] = json!("https://mcp.example"); - explicit_binding["scope"] = json!("files.read"); - let response: McpAccessTokenResponse = serde_json::from_value(explicit_binding)?; - assert_eq!( - response.validate("https://mcp.example", &granted)?, - EmaAccessToken { - access_token: "resource-token".to_string(), - expires_in: Some(Duration::from_secs(300)), + let resource_requested = location == "resource response"; + for (endpoint, response, count) in [ + ("/idp/token", jag, 1), + ("/as/token", bearer, u64::from(resource_requested)), + ] { + Mock::given(method("POST")) + .and(path(endpoint)) + .respond_with(ResponseTemplate::new(200).set_body_json(response)) + .expect(count) + .mount(&server) + .await; } - ); - Ok(()) + let error = exchange(&server, "refresh-token") + .await + .expect_err("SDK must reject unsupported authorization"); + assert_eq!( + error.downcast_ref::(), + Some(&EmaError::InvalidResponse { + stage: if resource_requested { + EmaExchangeStage::ResourceAuthorizationServer + } else { + EmaExchangeStage::IdentityProvider + }, + message: if location == "signed scope" { + "malformed or duplicate scopes" + } else { + "authorization_details is not supported" + }, + }), + "{location}" + ); + assert_eq!( + server.received_requests().await.expect("requests").len(), + if resource_requested { 2 } else { 1 } + ); + } } #[test] @@ -384,72 +310,65 @@ fn identity_and_credential_destinations_are_bound() { } #[tokio::test] -async fn provider_errors_cannot_reflect_credentials() { +async fn provider_errors_preserve_credential_policy_without_reflecting_credentials() { const SENTINEL: &str = "secret-assertion-sentinel"; - for (code, expected) in [ - (SENTINEL, "OAuth token request rejected"), - ("invalid_grant", "invalid_grant"), - ( - "insufficient_user_authentication", - "insufficient_user_authentication", - ), + for (endpoint, grant_source) in [ + ("/idp/token", EmaInvalidGrantSource::EnterpriseIdentity), + ("/as/token", EmaInvalidGrantSource::ResourceAuthorization), ] { - let server = MockServer::start().await; - Mock::given(method("POST")) - .and(path("/token")) - .respond_with(ResponseTemplate::new(400).set_body_json(json!({ - "error":code,"error_description":SENTINEL, - }))) - .mount(&server) - .await; - let error = post_form::( - &http_client(), - &format!("{}/token", server.uri()), - &[("subject_token", SENTINEL)], - "test-client", - EmaInvalidGrantSource::EnterpriseIdentity, - "test token exchange", - ) - .await - .expect_err("provider error should fail"); - match code { - "invalid_grant" => assert_eq!( - error.downcast_ref::(), - Some(&EmaAuthFailure::InvalidGrant { - grant_source: EmaInvalidGrantSource::EnterpriseIdentity, - }) - ), - "insufficient_user_authentication" => assert_eq!( - error.downcast_ref::(), - Some(&EmaAuthFailure::InsufficientUserAuthentication) - ), - _ => assert_eq!(error.downcast_ref::(), None), + for code in [ + SENTINEL, + "invalid_grant", + "insufficient_user_authentication", + "malformed", + ] { + let server = MockServer::builder().start().await; + if endpoint == "/as/token" { + let jag = jag_response(&claims( + &format!("{}/idp", server.uri()), + &format!("{}/as", server.uri()), + &format!("{}/mcp", server.uri()), + )); + Mock::given(method("POST")) + .and(path("/idp/token")) + .respond_with(ResponseTemplate::new(200).set_body_json(jag)) + .expect(1) + .mount(&server) + .await; + } + let response = if code == "malformed" { + ResponseTemplate::new(200).set_body_json(json!({"expires_in": SENTINEL})) + } else { + ResponseTemplate::new(400).set_body_json(json!({ + "error":code,"error_description":SENTINEL, + })) + }; + Mock::given(method("POST")) + .and(path(endpoint)) + .respond_with(response) + .expect(1) + .mount(&server) + .await; + let error = exchange(&server, SENTINEL) + .await + .expect_err("provider error should fail"); + let expected = match code { + "invalid_grant" => Some(EmaAuthFailure::InvalidGrant { grant_source }), + "insufficient_user_authentication" => { + Some(EmaAuthFailure::InsufficientUserAuthentication) + } + _ => None, + }; + assert_eq!(error.downcast_ref::(), expected.as_ref()); + assert!( + !format!("{error:#?}").contains(SENTINEL), + "provider reflected a credential" + ); + assert_eq!( + server.received_requests().await.expect("requests").len(), + if endpoint == "/as/token" { 2 } else { 1 }, + "exchange must not retry a rejected grant" + ); } - let error = error.to_string(); - assert!(!error.contains(SENTINEL), "provider reflected a credential"); - assert!(error.ends_with(expected), "{error}"); } - let server = MockServer::start().await; - let mut malformed = token_response(); - malformed["expires_in"] = json!(SENTINEL); - Mock::given(method("POST")) - .and(path("/token")) - .respond_with(ResponseTemplate::new(200).set_body_json(malformed)) - .mount(&server) - .await; - let error = post_form::( - &http_client(), - &format!("{}/token", server.uri()), - &[], - "test-client", - EmaInvalidGrantSource::EnterpriseIdentity, - "test token exchange", - ) - .await - .err() - .expect("malformed response should fail"); - assert!( - !format!("{error:#}").contains(SENTINEL), - "parser reflected a credential" - ); }