diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index af33be4d35..5be913b8ed 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1087,8 +1087,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/modelcontextprotocol/rust-sdk?rev=4048ac40e958fb1a7d614617a2ae8d26666ee2fa#4048ac40e958fb1a7d614617a2ae8d26666ee2fa_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.5.1\"},{\"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=4048ac40e958fb1a7d614617a2ae8d26666ee2fa#4048ac40e958fb1a7d614617a2ae8d26666ee2fa_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=ed1cdab335221515706178d68495bba2aed1924f#ed1cdab335221515706178d68495bba2aed1924f_crossterm": "{\"dependencies\":[{\"default_features\":true,\"features\":[],\"name\":\"base64\",\"optional\":true,\"req\":\"0.23\"},{\"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\"},{\"default_features\":true,\"features\":[],\"name\":\"document-features\",\"optional\":true,\"req\":\"0.2.11\"},{\"default_features\":false,\"features\":[],\"name\":\"futures-core\",\"optional\":true,\"req\":\"0.3.31\"},{\"name\":\"parking_lot\",\"req\":\"0.12.1\"},{\"default_features\":true,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"1.0.103\"},{\"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.168\",\"target\":\"cfg(unix)\"},{\"default_features\":true,\"features\":[\"os-poll\"],\"name\":\"mio\",\"optional\":true,\"req\":\"1.0.1\",\"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\",\"derive-more\",\"events\",\"windows\"],\"derive-more\":[\"dep:derive_more\"],\"document-features\":[\"dep:document-features\"],\"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,\"req\":\"0.3.28\"},{\"name\":\"log\",\"req\":\"0.4.17\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"0.2.11\"},{\"default_features\":false,\"features\":[],\"name\":\"rustls\",\"optional\":true,\"req\":\"0.23.0\"},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"0.8.0\"},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"1.0\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio\",\"optional\":false,\"req\":\"1.0.0\"},{\"default_features\":true,\"features\":[],\"name\":\"tokio-native-tls\",\"optional\":true,\"req\":\"0.3.1\"},{\"default_features\":false,\"features\":[],\"name\":\"tokio-rustls\",\"optional\":true,\"req\":\"0.26.0\"},{\"default_features\":false,\"features\":[],\"name\":\"tungstenite\",\"optional\":false},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"0.26.0\"}],\"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\",\"req\":\"1.9.0\"},{\"default_features\":true,\"features\":[],\"name\":\"data-encoding\",\"optional\":true,\"req\":\"2\"},{\"default_features\":false,\"features\":[\"zlib-rs\"],\"name\":\"flate2\",\"optional\":true,\"req\":\"1.1.0\"},{\"default_features\":true,\"features\":[],\"name\":\"headers\",\"optional\":true,\"req\":\"0.4.0\"},{\"default_features\":true,\"features\":[],\"name\":\"http\",\"optional\":true,\"req\":\"1.0\"},{\"default_features\":true,\"features\":[],\"name\":\"httparse\",\"optional\":true,\"req\":\"1.3.4\"},{\"name\":\"log\",\"req\":\"0.4.8\"},{\"default_features\":true,\"features\":[],\"name\":\"native-tls-crate\",\"optional\":true,\"package\":\"native-tls\",\"req\":\"0.2.3\"},{\"name\":\"rand\",\"req\":\"0.9.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"rustls\",\"optional\":true,\"req\":\"0.23.0\"},{\"default_features\":true,\"features\":[],\"name\":\"rustls-native-certs\",\"optional\":true,\"req\":\"0.8.0\"},{\"default_features\":true,\"features\":[],\"name\":\"rustls-pki-types\",\"optional\":true,\"req\":\"1.0\"},{\"default_features\":true,\"features\":[],\"name\":\"sha1\",\"optional\":true,\"req\":\"0.10\"},{\"name\":\"thiserror\",\"req\":\"2.0.7\"},{\"default_features\":true,\"features\":[],\"name\":\"url\",\"optional\":true,\"req\":\"2.1.0\"},{\"name\":\"utf-8\",\"req\":\"0.7.5\"},{\"default_features\":true,\"features\":[],\"name\":\"webpki-roots\",\"optional\":true,\"req\":\"0.26\"}],\"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\":\"\"}", diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 10c3e869ae..945d4c0b42 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -13098,8 +13098,8 @@ dependencies = [ [[package]] name = "rmcp" -version = "3.3.0" -source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df" +version = "3.5.1" +source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=4048ac40e958fb1a7d614617a2ae8d26666ee2fa#4048ac40e958fb1a7d614617a2ae8d26666ee2fa" dependencies = [ "async-trait", "base64 0.23.0", @@ -13133,8 +13133,8 @@ dependencies = [ [[package]] name = "rmcp-macros" -version = "3.3.0" -source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3e636cab26c013eca5131103c03d20237f12c4df#3e636cab26c013eca5131103c03d20237f12c4df" +version = "3.5.1" +source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=4048ac40e958fb1a7d614617a2ae8d26666ee2fa#4048ac40e958fb1a7d614617a2ae8d26666ee2fa" dependencies = [ "darling 0.24.0", "proc-macro2", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 1dcbaf88c0..8fffe579a7 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -447,8 +447,7 @@ 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-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 } +rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", rev = "4048ac40e958fb1a7d614617a2ae8d26666ee2fa", version = "=3.5.1", 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/app-server/tests/suite/v2/app_installed.rs b/codex-rs/app-server/tests/suite/v2/app_installed.rs index 90c32b81d2..2f6ba0450f 100644 --- a/codex-rs/app-server/tests/suite/v2/app_installed.rs +++ b/codex-rs/app-server/tests/suite/v2/app_installed.rs @@ -36,7 +36,7 @@ use rmcp::model::CallToolResult; use rmcp::model::ContentBlock; use rmcp::model::ListToolsResult; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::transport::StreamableHttpServerConfig; use rmcp::transport::StreamableHttpService; @@ -461,8 +461,8 @@ struct InstalledAppsMcpServer { } impl ServerHandler for InstalledAppsMcpServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn call_tool( diff --git a/codex-rs/app-server/tests/suite/v2/app_list.rs b/codex-rs/app-server/tests/suite/v2/app_list.rs index 84cdd36c04..5805f02ae7 100644 --- a/codex-rs/app-server/tests/suite/v2/app_list.rs +++ b/codex-rs/app-server/tests/suite/v2/app_list.rs @@ -43,7 +43,7 @@ use rmcp::model::JsonObject; use rmcp::model::ListToolsResult; use rmcp::model::MetaObject; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::transport::StreamableHttpServerConfig; @@ -1497,8 +1497,8 @@ impl AppsServerControl { } impl ServerHandler for AppListMcpServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } fn list_tools( diff --git a/codex-rs/app-server/tests/suite/v2/executor_mcp.rs b/codex-rs/app-server/tests/suite/v2/executor_mcp.rs index ce5e1e314b..f2c5da814e 100644 --- a/codex-rs/app-server/tests/suite/v2/executor_mcp.rs +++ b/codex-rs/app-server/tests/suite/v2/executor_mcp.rs @@ -37,7 +37,7 @@ use rmcp::model::CallToolResult; use rmcp::model::JsonObject; use rmcp::model::ListToolsResult; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::service::RequestContext; @@ -1507,8 +1507,8 @@ startup_timeout_sec = 10 struct ExecutorHttpMcpServer; impl ServerHandler for ExecutorHttpMcpServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn list_tools( diff --git a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs index b25a2098fd..cb5f8760dc 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs @@ -105,7 +105,7 @@ use rmcp::model::ReadResourceResult; use rmcp::model::Resource; use rmcp::model::ResourceContents; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::service::RequestContext; use rmcp::service::RoleServer; use rmcp::transport::StreamableHttpServerConfig; @@ -1376,8 +1376,8 @@ struct ResourceAppsMcpServer { } impl ServerHandler for ResourceAppsMcpServer { - fn get_info(&self) -> ServerInfo { - let mut info = ServerInfo::new(ServerCapabilities::builder().enable_resources().build()) + fn get_info(&self) -> ServerConfig { + let mut info = ServerConfig::new(ServerCapabilities::builder().enable_resources().build()) .with_protocol_version(ProtocolVersion::V_2025_06_18); if self.calls.tools_enabled.load(Ordering::Relaxed) { info.capabilities.tools = Some(Default::default()); diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs index def85ba0a4..cda1f49377 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_elicitation.rs @@ -64,7 +64,7 @@ use rmcp::model::MetaObject; use rmcp::model::PrimitiveSchemaDefinition; use rmcp::model::RequestMetaObject; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::ServerRequest as McpServerRequest; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; @@ -1048,8 +1048,8 @@ impl ServerHandler for ElicitationAppsMcpServer { Ok(self.get_info()) } - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) .with_protocol_version(rmcp::model::ProtocolVersion::V_2025_06_18) } diff --git a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs index 09daa053f3..c7dc29539e 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_server_status.rs @@ -47,7 +47,7 @@ use rmcp::model::ListResourcesResult; use rmcp::model::ListToolsResult; use rmcp::model::PaginatedRequestParams; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::service::RequestContext; @@ -1229,7 +1229,7 @@ struct McpStatusServer { } impl ServerHandler for McpStatusServer { - fn get_info(&self) -> ServerInfo { + fn get_info(&self) -> ServerConfig { let mut capabilities = ServerCapabilities::builder().enable_tools().build(); capabilities.extensions = Some(BTreeMap::from([( "openai/settings".to_string(), @@ -1238,7 +1238,7 @@ impl ServerHandler for McpStatusServer { ) .expect("settings capability is a JSON object"), )])); - ServerInfo::new(capabilities).with_server_info( + ServerConfig::new(capabilities).with_server_info( Implementation::new("lookup-server", "1.0.0").with_title("Lookup Server"), ) } @@ -1274,8 +1274,8 @@ struct SlowInventoryServer { } impl ServerHandler for SlowInventoryServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new( + fn get_info(&self) -> ServerConfig { + ServerConfig::new( ServerCapabilities::builder() .enable_tools() .enable_resources() diff --git a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs index f346144a71..f95a709820 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs @@ -66,7 +66,7 @@ use rmcp::model::PrimitiveSchemaDefinition; use rmcp::model::ProtocolVersion; use rmcp::model::RequestMetaObject; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::service::RequestContext; @@ -1285,8 +1285,8 @@ impl ServerHandler for ToolAppsMcpServer { Ok(self.get_info()) } - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn list_tools( diff --git a/codex-rs/app-server/tests/suite/v2/plugin_install.rs b/codex-rs/app-server/tests/suite/v2/plugin_install.rs index 3d48cb3f4c..6c30dc727d 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_install.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_install.rs @@ -53,7 +53,7 @@ use rmcp::model::JsonObject; use rmcp::model::ListToolsResult; use rmcp::model::MetaObject; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::transport::StreamableHttpServerConfig; @@ -2632,8 +2632,8 @@ struct PluginInstallMcpServer { } impl ServerHandler for PluginInstallMcpServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } fn list_tools( diff --git a/codex-rs/app-server/tests/suite/v2/user_verification_mcp.rs b/codex-rs/app-server/tests/suite/v2/user_verification_mcp.rs index 3206dc2b5a..3b6aec4c2d 100644 --- a/codex-rs/app-server/tests/suite/v2/user_verification_mcp.rs +++ b/codex-rs/app-server/tests/suite/v2/user_verification_mcp.rs @@ -33,7 +33,7 @@ use rmcp::model::CallToolResult; use rmcp::model::ContentBlock; use rmcp::model::CustomRequest; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::ServerRequest; use rmcp::service::RequestContext; use rmcp::service::RoleServer; @@ -73,8 +73,8 @@ impl McpServerContributor for HostedVerificationServer { } impl ServerHandler for VerificationServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn call_tool( diff --git a/codex-rs/cli/tests/exec_server.rs b/codex-rs/cli/tests/exec_server.rs index 8f4f8b742e..36b122a414 100644 --- a/codex-rs/cli/tests/exec_server.rs +++ b/codex-rs/cli/tests/exec_server.rs @@ -281,7 +281,7 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr "2", ]) .stdin(Stdio::piped()) - .stderr(Stdio::piped()) + .stderr(Stdio::inherit()) .kill_on_drop(true); let mut child = command.spawn()?; @@ -290,7 +290,9 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr .take() .ok_or_else(|| anyhow::anyhow!("remote exec-server stdin was not piped"))?; - let environment_websocket = accept_parent_lifetime_websocket(&listener, TEST_TIMEOUT).await?; + let environment_websocket = accept_parent_lifetime_websocket(&listener, TEST_TIMEOUT) + .await + .context("remote executor failed to attach")?; // Remote startup must capture the version before registration, not on the first initialize. std::fs::write(&manifest, r#"{"version":"9.9.9"}"#)?; let executor_public_key = registered_parent_lifetime_executor_public_key(®istry).await?; @@ -311,7 +313,9 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr }; let client_task = tokio::spawn(async move { ExecServerClient::connect_noise_rendezvous(harness_args).await }); - let harness_websocket = accept_parent_lifetime_websocket(&listener, TEST_TIMEOUT).await?; + let harness_websocket = accept_parent_lifetime_websocket(&listener, TEST_TIMEOUT) + .await + .context("harness failed to attach")?; let relay_task = tokio::spawn(proxy_parent_lifetime_relay( environment_websocket, harness_websocket, @@ -342,9 +346,12 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr #[cfg(windows)] let argv = vec![ - "cmd.exe", - "/C", - "if defined CODEX_EXEC_SERVER_EXIT_ON_STDIN_CLOSE (exit /b 1) else ping -n 61 127.0.0.1", + "powershell.exe", + "-NoLogo", + "-NoProfile", + "-NonInteractive", + "-Command", + "if (Test-Path Env:CODEX_EXEC_SERVER_EXIT_ON_STDIN_CLOSE) { exit 1 }; Start-Sleep -Seconds 60", ]; #[cfg(not(windows))] let argv = vec![ @@ -385,15 +392,10 @@ metrics_exporter = {{ otlp-http = {{ endpoint = "{collector_url}/v1/metrics", pr drop(stdin); - let output = tokio::time::timeout(TEST_TIMEOUT, child.wait_with_output()) + let status = tokio::time::timeout(TEST_TIMEOUT, child.wait()) .await .map_err(|_| anyhow::anyhow!("remote exec-server did not exit after stdin closed"))??; - anyhow::ensure!( - output.status.success(), - "remote exec-server exited with {}; stderr: {}", - output.status, - String::from_utf8_lossy(&output.stderr) - ); + anyhow::ensure!(status.success(), "remote exec-server exited with {status}"); relay_task.abort(); let _ = relay_task.await; diff --git a/codex-rs/cli/tests/sandbox_network_proxy.rs b/codex-rs/cli/tests/sandbox_network_proxy.rs index 21747c3919..a21b5b4ee8 100644 --- a/codex-rs/cli/tests/sandbox_network_proxy.rs +++ b/codex-rs/cli/tests/sandbox_network_proxy.rs @@ -1,11 +1,14 @@ #![cfg(target_os = "linux")] use std::net::TcpListener; -use std::time::Duration; -use std::time::Instant; use anyhow::Result; use tempfile::TempDir; +use wiremock::Mock; +use wiremock::MockServer; +use wiremock::ResponseTemplate; +use wiremock::matchers::method; +use wiremock::matchers::path; const BWRAP_UNAVAILABLE_ERR: &str = "bubblewrap is unavailable"; @@ -71,50 +74,17 @@ mode = "full" Ok(()) } -#[test] -fn sandbox_with_network_proxy_allows_explicit_loopback_access() -> Result<()> { +#[tokio::test] +async fn sandbox_with_network_proxy_allows_explicit_loopback_access() -> Result<()> { let codex_home = TempDir::new()?; let listener = TcpListener::bind("127.0.0.2:0")?; let port = listener.local_addr()?.port(); - listener.set_nonblocking(true)?; - let server = std::thread::spawn(move || -> std::io::Result<()> { - let deadline = Instant::now() + Duration::from_secs(5); - loop { - match listener.accept() { - Ok((mut stream, _)) => { - // Closing with unread request bytes can reset the connection and yield a 502. - stream.set_nonblocking(false)?; - stream.set_read_timeout(Some(Duration::from_secs(5)))?; - let mut request = Vec::new(); - let mut buffer = [0_u8; 1024]; - while !request.windows(4).any(|window| window == b"\r\n\r\n") { - let bytes_read = std::io::Read::read(&mut stream, &mut buffer)?; - if bytes_read == 0 || request.len() + bytes_read > 64 * 1024 { - return Err(std::io::Error::other( - "incomplete or oversized loopback request headers", - )); - } - request.extend_from_slice(&buffer[..bytes_read]); - } - std::io::Write::write_all( - &mut stream, - b"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\n\r\n", - )?; - return Ok(()); - } - Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { - if Instant::now() >= deadline { - return Err(std::io::Error::new( - std::io::ErrorKind::TimedOut, - "timed out waiting for allowlisted loopback request", - )); - } - std::thread::sleep(Duration::from_millis(10)); - } - Err(err) => return Err(err), - } - } - }); + let server = MockServer::builder().listener(listener).start().await; + Mock::given(method("GET")) + .and(path("/")) + .respond_with(ResponseTemplate::new(204)) + .mount(&server) + .await; std::fs::write( codex_home.path().join("config.toml"), r#" @@ -138,7 +108,7 @@ allow_local_binding = false )?; let url = format!("http://127.0.0.2:{port}/"); - let output = std::process::Command::new(codex_utils_cargo_bin::cargo_bin("codex")?) + let output = tokio::process::Command::new(codex_utils_cargo_bin::cargo_bin("codex")?) .env("CODEX_HOME", codex_home.path()) .args([ "sandbox", @@ -155,7 +125,8 @@ allow_local_binding = false "4", url.as_str(), ]) - .output()?; + .output() + .await?; let stderr = String::from_utf8_lossy(&output.stderr); if stderr.contains(BWRAP_UNAVAILABLE_ERR) { @@ -170,7 +141,5 @@ allow_local_binding = false String::from_utf8_lossy(&output.stdout), stderr, ); - server.join().expect("loopback server panicked")?; - Ok(()) } diff --git a/codex-rs/codex-mcp/src/connection_manager_tests.rs b/codex-rs/codex-mcp/src/connection_manager_tests.rs index f40a689beb..d7a841bf4c 100644 --- a/codex-rs/codex-mcp/src/connection_manager_tests.rs +++ b/codex-rs/codex-mcp/src/connection_manager_tests.rs @@ -80,7 +80,7 @@ use rmcp::model::NumberOrString; use rmcp::model::PaginatedRequestParams; use rmcp::model::ProtocolVersion; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::service::RequestContext; use std::collections::HashMap; @@ -290,8 +290,8 @@ struct RefreshTestTransportFactory { } impl ServerHandler for RefreshTestTransportFactory { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn list_tools( @@ -338,8 +338,8 @@ struct MutableToolsServer { } impl ServerHandler for MutableToolsServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn list_tools( diff --git a/codex-rs/memories/write/src/startup_tests.rs b/codex-rs/memories/write/src/startup_tests.rs index 7d974674c9..1f2cb94f6b 100644 --- a/codex-rs/memories/write/src/startup_tests.rs +++ b/codex-rs/memories/write/src/startup_tests.rs @@ -143,7 +143,7 @@ async fn memories_startup_fails_consolidation_when_worker_creates_extension_syml ev_assistant_message("msg-phase2-complete", "phase2 complete"), ev_completed("resp-phase2-complete"), ]), - sse_failed("resp-phase2-failed", "server_error", "worker failed"), + sse_failed("resp-phase2-failed", "invalid_prompt", "worker failed"), ]; for response in responses { diff --git a/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs b/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs index a2ab794f5a..aba1422d90 100644 --- a/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs +++ b/codex-rs/rmcp-client/src/bin/rmcp_test_server.rs @@ -11,7 +11,7 @@ use rmcp::model::JsonObject; use rmcp::model::ListToolsResult; use rmcp::model::PaginatedRequestParams; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use serde::Deserialize; use serde_json::json; @@ -78,8 +78,8 @@ struct EchoArgs { } impl ServerHandler for TestToolServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new( + fn get_info(&self) -> ServerConfig { + ServerConfig::new( ServerCapabilities::builder() .enable_tools() .enable_tool_list_changed() diff --git a/codex-rs/rmcp-client/src/bin/test_stdio_server.rs b/codex-rs/rmcp-client/src/bin/test_stdio_server.rs index 8bf0845d1a..b3d2161efc 100644 --- a/codex-rs/rmcp-client/src/bin/test_stdio_server.rs +++ b/codex-rs/rmcp-client/src/bin/test_stdio_server.rs @@ -28,7 +28,7 @@ use rmcp::model::Resource; use rmcp::model::ResourceContents; use rmcp::model::ResourceTemplate; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use serde::Deserialize; @@ -527,7 +527,7 @@ impl ServerHandler for TestToolServer { Ok(self.get_info()) } - fn get_info(&self) -> ServerInfo { + fn get_info(&self) -> ServerConfig { let mut capabilities = ServerCapabilities::builder() .enable_tools() .enable_tool_list_changed() @@ -538,7 +538,7 @@ impl ServerHandler for TestToolServer { JsonObject::new(), )])); - let server_info = ServerInfo::new(capabilities); + let server_info = ServerConfig::new(capabilities); let server_info = match dynamic_server_process_label() { Some(process_label) => server_info .with_server_info( diff --git a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs index 9702794bd6..31af242ed1 100644 --- a/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs +++ b/codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs @@ -40,7 +40,7 @@ use rmcp::model::Resource; use rmcp::model::ResourceContents; use rmcp::model::ResourceTemplate; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::transport::StreamableHttpServerConfig; @@ -251,8 +251,8 @@ async fn main() -> Result<(), Box> { } impl ServerHandler for TestToolServer { - fn get_info(&self) -> ServerInfo { - ServerInfo::new( + fn get_info(&self) -> ServerConfig { + ServerConfig::new( ServerCapabilities::builder() .enable_tools() .enable_tool_list_changed() diff --git a/codex-rs/rmcp-client/src/elicitation_client_service.rs b/codex-rs/rmcp-client/src/elicitation_client_service.rs index ce15297007..ef2fbacd92 100644 --- a/codex-rs/rmcp-client/src/elicitation_client_service.rs +++ b/codex-rs/rmcp-client/src/elicitation_client_service.rs @@ -7,7 +7,7 @@ use std::sync::PoisonError; use codex_protocol::mcp::OPENAI_ELICITATION_EXTENSION_ID; use rmcp::RoleClient; -use rmcp::model::ClientInfo; +use rmcp::model::ClientConfig; use rmcp::model::ClientResult; use rmcp::model::CustomRequest; use rmcp::model::CustomResult; @@ -95,7 +95,7 @@ impl Drop for PendingVerification { impl ElicitationClientService { pub(crate) fn new( - client_info: ClientInfo, + client_info: ClientConfig, send_elicitation: SendElicitation, pause_state: ElicitationPauseState, ) -> Self { @@ -336,7 +336,7 @@ impl Service for ElicitationClientService { .await } - fn get_info(&self) -> ClientInfo { + fn get_info(&self) -> ClientConfig { >::get_info(&self.handler) } } diff --git a/codex-rs/rmcp-client/src/logging_client_handler.rs b/codex-rs/rmcp-client/src/logging_client_handler.rs index 98887d7490..f123021d21 100644 --- a/codex-rs/rmcp-client/src/logging_client_handler.rs +++ b/codex-rs/rmcp-client/src/logging_client_handler.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use rmcp::ClientHandler; use rmcp::RoleClient; use rmcp::model::CancelledNotificationParam; -use rmcp::model::ClientInfo; +use rmcp::model::ClientConfig; use rmcp::model::ElicitRequestParams; use rmcp::model::ElicitResult; #[allow(deprecated)] @@ -24,12 +24,12 @@ use crate::rmcp_client::SendElicitation; #[derive(Clone)] pub(crate) struct LoggingClientHandler { - client_info: ClientInfo, + client_info: ClientConfig, send_elicitation: Arc, } impl LoggingClientHandler { - pub(crate) fn new(client_info: ClientInfo, send_elicitation: SendElicitation) -> Self { + pub(crate) fn new(client_info: ClientConfig, send_elicitation: SendElicitation) -> Self { Self { client_info, send_elicitation: Arc::new(send_elicitation), @@ -91,7 +91,7 @@ impl ClientHandler for LoggingClientHandler { info!("MCP server prompt list changed"); } - fn get_info(&self) -> ClientInfo { + fn get_info(&self) -> ClientConfig { self.client_info.clone() } diff --git a/codex-rs/rmcp-client/src/tool_input_tests.rs b/codex-rs/rmcp-client/src/tool_input_tests.rs index ebbdc962f6..306de871e0 100644 --- a/codex-rs/rmcp-client/src/tool_input_tests.rs +++ b/codex-rs/rmcp-client/src/tool_input_tests.rs @@ -6,7 +6,7 @@ use crate::tool_input::call_tool; use codex_protocol::mcp::OPENAI_ELICITATION_EXTENSION_ID; use rmcp::RoleServer; use rmcp::model::CallToolRequestParams; -use rmcp::model::ClientInfo; +use rmcp::model::ClientConfig; use rmcp::model::ClientJsonRpcMessage; use rmcp::model::CustomResult; use rmcp::model::ServerJsonRpcMessage; @@ -28,7 +28,7 @@ async fn connection_closure_releases_pending_inputs_and_timeout_pause() -> anyho let pause_state = ElicitationPauseState::new(); let mut paused = pause_state.subscribe(); let (route_tx, mut route_rx) = mpsc::unbounded_channel(); - let mut info = ClientInfo::default(); + let mut info = ClientConfig::default(); info.capabilities.extensions = Some( [( OPENAI_ELICITATION_EXTENSION_ID.into(), diff --git a/codex-rs/rmcp-client/src/user_verification_cancellation_tests.rs b/codex-rs/rmcp-client/src/user_verification_cancellation_tests.rs index 6d66c0416d..7e330f36d7 100644 --- a/codex-rs/rmcp-client/src/user_verification_cancellation_tests.rs +++ b/codex-rs/rmcp-client/src/user_verification_cancellation_tests.rs @@ -13,7 +13,7 @@ use rmcp::ServerHandler; use rmcp::ServiceExt; use rmcp::model::CancelledNotification; use rmcp::model::CancelledNotificationParam; -use rmcp::model::ClientInfo; +use rmcp::model::ClientConfig; use rmcp::model::ClientJsonRpcMessage; use rmcp::model::CustomRequest; use rmcp::model::ElicitationAction; @@ -23,6 +23,7 @@ use rmcp::model::ServerJsonRpcMessage; use rmcp::model::ServerNotification; use rmcp::model::ServerRequest; use rmcp::service::PeerRequestOptions; +use rmcp::service::QuitReason; use rmcp::service::RunningService; use rmcp::service::ServerInitializeError; use rmcp::service::serve_directly; @@ -82,7 +83,7 @@ async fn recovered_connections_accept_elicitations_with_previously_cancelled_ids .await?; client .initialize( - ClientInfo::default().with_protocol_version(ProtocolVersion::V_2025_06_18), + ClientConfig::default().with_protocol_version(ProtocolVersion::V_2025_06_18), Some(Duration::from_secs(/*secs*/ 5)), Box::new(|_, _| { Box::pin(async { @@ -171,7 +172,7 @@ async fn ordinary_elicitations_release_pending_responses_on_cancellation() -> an let mut paused = pause_state.subscribe(); let (route_tx, mut route_rx) = mpsc::unbounded_channel(); let service = ElicitationClientService::new( - ClientInfo::default(), + ClientConfig::default(), Box::new(move |_, _| { let (response_tx, response_rx) = oneshot::channel(); route_tx.send(response_tx).expect("observe elicitation"); @@ -211,14 +212,6 @@ async fn ordinary_elicitations_release_pending_responses_on_cancellation() -> an paused.wait_for(|paused| !*paused), ) .await??; - let response = timeout(Duration::from_secs(/*secs*/ 5), server.receive()) - .await? - .expect("cancelled elicitation returned a response"); - assert_eq!( - serde_json::to_value(response)?, - json!({"jsonrpc": "2.0", "id": 1, "result": {"action": "cancel"}}), - ); - server .send(ServerJsonRpcMessage::request(request, RequestId::Number(2))) .await?; @@ -239,12 +232,88 @@ async fn ordinary_elicitations_release_pending_responses_on_cancellation() -> an Ok(()) } +#[tokio::test(start_paused = true)] +async fn transport_eof_releases_pending_elicitations_without_dropping_client() -> anyhow::Result<()> +{ + for (method, params) in [ + ( + "elicitation/create", + json!({ + "mode": "form", + "message": "Confirm", + "requestedSchema": {"type": "object", "properties": {}}, + }), + ), + ( + "openai/elicitation/create", + json!({ + "mode": "openai/userVerification", + "title": "Approve", + "description": "", + "challenge": "AQID", + }), + ), + ] { + let pause_state = ElicitationPauseState::new(); + let mut paused = pause_state.subscribe(); + let (route_tx, mut route_rx) = mpsc::unbounded_channel(); + let mut client_info = ClientConfig::default(); + client_info.capabilities.extensions = Some( + [( + OPENAI_ELICITATION_EXTENSION_ID.to_string(), + serde_json::Map::from_iter([("userVerification".to_string(), json!({}))]), + )] + .into_iter() + .collect(), + ); + let service = ElicitationClientService::new( + client_info, + Box::new(move |_, _| { + let (response_tx, response_rx) = oneshot::channel(); + route_tx + .send(response_tx) + .expect("observe pending elicitation"); + Box::pin(async move { Ok(response_rx.await?) }) + }), + pause_state, + ); + let (client_transport, server_transport) = tokio::io::duplex(/*max_buf_size*/ 4096); + let client = serve_directly(service, client_transport, /*peer_info*/ None); + let mut server = IntoTransport::::into_transport(server_transport); + // A server can request approval without an outgoing tools/call to detect EOF. + server + .send(serde_json::from_value::(json!({ + "jsonrpc": "2.0", "id": 1, "method": method, "params": params, + }))?) + .await?; + let mut response_tx = timeout(Duration::from_secs(/*secs*/ 5), route_rx.recv()) + .await? + .expect("elicitation reached the UI"); + assert!(*paused.borrow()); + + drop(server); + + // RMCP allows five seconds to drain responses after EOF. Retain the client + // throughout cleanup: dropping it would cancel the handler and hide the bug. + timeout(Duration::from_secs(/*secs*/ 7), response_tx.closed()).await?; + timeout( + Duration::from_secs(/*secs*/ 1), + paused.wait_for(|paused| !*paused), + ) + .await??; + // Only consume the retained client after observing handler cleanup. + let reason = timeout(Duration::from_secs(/*secs*/ 1), client.waiting()).await??; + assert!(matches!(reason, QuitReason::Closed)); + } + Ok(()) +} + #[tokio::test] async fn user_verification_service_cancellation_drops_pending_response() -> anyhow::Result<()> { let pause_state = ElicitationPauseState::new(); let mut paused = pause_state.subscribe(); let (route_tx, mut route_rx) = mpsc::unbounded_channel(); - let mut client_info = ClientInfo::default(); + let mut client_info = ClientConfig::default(); client_info.capabilities.extensions = Some( [( OPENAI_ELICITATION_EXTENSION_ID.to_string(), @@ -303,7 +372,7 @@ async fn cancelling_one_verification_leaves_the_mcp_connection_and_other_request let pause_state = ElicitationPauseState::new(); let mut paused = pause_state.subscribe(); let (route_tx, mut route_rx) = mpsc::unbounded_channel(); - let mut client_info = ClientInfo::default(); + let mut client_info = ClientConfig::default(); client_info.capabilities.extensions = Some( [( OPENAI_ELICITATION_EXTENSION_ID.to_string(), @@ -362,17 +431,6 @@ async fn cancelling_one_verification_leaves_the_mcp_connection_and_other_request timeout(Duration::from_secs(/*secs*/ 5), first.closed()).await?; assert!(!second.is_closed()); assert!(*paused.borrow()); - let cancel = timeout(Duration::from_secs(/*secs*/ 5), server.receive()) - .await? - .expect("cancelled verification still sends a response"); - let ClientJsonRpcMessage::Response(cancel) = cancel else { - anyhow::bail!("expected a cancellation response"); - }; - assert_eq!(cancel.id, first_id); - assert_eq!( - serde_json::to_value(cancel.result)?, - json!({"action": "cancel", "_meta": {"openai/userVerificationReason": "interrupted"}}) - ); for request_id in [first_id, RequestId::Number(999)] { server diff --git a/codex-rs/rmcp-client/src/user_verification_dispatch_tests.rs b/codex-rs/rmcp-client/src/user_verification_dispatch_tests.rs index dda5a58e18..51e534aeed 100644 --- a/codex-rs/rmcp-client/src/user_verification_dispatch_tests.rs +++ b/codex-rs/rmcp-client/src/user_verification_dispatch_tests.rs @@ -11,7 +11,7 @@ use std::time::Duration; use tokio::time::timeout; fn service() -> ElicitationClientService { - let mut info = ClientInfo::default(); + let mut info = ClientConfig::default(); info.capabilities.extensions = Some( [( OPENAI_ELICITATION_EXTENSION_ID.into(), diff --git a/codex-rs/tui/src/app/tests/session_lifecycle_requests.rs b/codex-rs/tui/src/app/tests/session_lifecycle_requests.rs index 3e7975f219..5b82dc6d3a 100644 --- a/codex-rs/tui/src/app/tests/session_lifecycle_requests.rs +++ b/codex-rs/tui/src/app/tests/session_lifecycle_requests.rs @@ -39,11 +39,26 @@ use tokio::net::TcpListener; use tokio::sync::oneshot; use tokio::task::JoinHandle; use tokio_tungstenite::accept_async; +use tokio_tungstenite::tungstenite::Error as WebSocketError; use tokio_tungstenite::tungstenite::Message; +use tokio_tungstenite::tungstenite::error::ProtocolError; pub(super) type RecordedRequests = Arc>>; pub(super) type RecordingAppServer = (AppServerSession, RecordedRequests, JoinHandle>); +fn is_recording_client_disconnect(error: &WebSocketError) -> bool { + match error { + WebSocketError::Protocol(ProtocolError::ResetWithoutClosingHandshake) => true, + WebSocketError::Io(error) => matches!( + error.kind(), + std::io::ErrorKind::BrokenPipe + | std::io::ErrorKind::ConnectionReset + | std::io::ErrorKind::ConnectionAborted + ), + _ => false, + } +} + #[tokio::test] async fn daybreak_command_persists_and_confirms_each_selection() -> Result<()> { use codex_protocol::openai_models::ModelAccessPrograms; @@ -495,20 +510,19 @@ pub(super) async fn start_recording_app_server_with_realtime_speech( if let codex_app_server_client::InProcessServerEvent::ServerNotification(notification) = event && matches!(*notification, ServerNotification::ThreadSettingsUpdated(_)) { - websocket.send(Message::Text(serde_json::to_string(¬ification)?.into())).await?; + let result = websocket.send(Message::Text(serde_json::to_string(¬ification)?.into())).await; + if result.as_ref().is_err_and(is_recording_client_disconnect) { + break; + } + result?; } continue; } }; let Some(frame) = frame else { break }; - // The client can close with an unread settings notification during shutdown. - match &frame { - Err(tokio_tungstenite::tungstenite::Error::Protocol( - tokio_tungstenite::tungstenite::error::ProtocolError::ResetWithoutClosingHandshake, - )) => break, - Err(tokio_tungstenite::tungstenite::Error::Io(error)) - if matches!(error.kind(), std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted) => break, - _ => {} + // The client can close with requests or notifications pending during shutdown. + if frame.as_ref().is_err_and(is_recording_client_disconnect) { + break; } let Message::Text(text) = frame? else { continue; @@ -516,7 +530,7 @@ pub(super) async fn start_recording_app_server_with_realtime_speech( let message = serde_json::from_str::(&text)?; match message { JSONRPCMessage::Request(request) if request.method == "initialize" => { - websocket + let result = websocket .send(Message::Text( serde_json::to_string(&JSONRPCMessage::Response(JSONRPCResponse { id: request.id, @@ -527,7 +541,11 @@ pub(super) async fn start_recording_app_server_with_realtime_speech( }))? .into(), )) - .await?; + .await; + if result.as_ref().is_err_and(is_recording_client_disconnect) { + break; + } + result?; } JSONRPCMessage::Request(request) => { request_sink @@ -803,9 +821,13 @@ pub(super) async fn start_recording_app_server_with_realtime_speech( } } }; - websocket + let result = websocket .send(Message::Text(serde_json::to_string(&response)?.into())) - .await?; + .await; + if result.as_ref().is_err_and(is_recording_client_disconnect) { + break; + } + result?; } JSONRPCMessage::Notification(notification) if notification.method == "initialized" => {} diff --git a/codex-rs/tui/src/dynamic_tools_mcp.rs b/codex-rs/tui/src/dynamic_tools_mcp.rs index 67fed87b4c..71914649a4 100644 --- a/codex-rs/tui/src/dynamic_tools_mcp.rs +++ b/codex-rs/tui/src/dynamic_tools_mcp.rs @@ -31,7 +31,7 @@ use rmcp::model::JsonObject; use rmcp::model::ListToolsResult; use rmcp::model::PaginatedRequestParams; use rmcp::model::ServerCapabilities; -use rmcp::model::ServerInfo; +use rmcp::model::ServerConfig; use rmcp::model::Tool; use rmcp::model::ToolAnnotations; use rmcp::service::RequestContext; @@ -246,8 +246,8 @@ struct DynamicToolMcpHandler { } impl ServerHandler for DynamicToolMcpHandler { - fn get_info(&self) -> ServerInfo { - ServerInfo::new(ServerCapabilities::builder().enable_tools().build()) + fn get_info(&self) -> ServerConfig { + ServerConfig::new(ServerCapabilities::builder().enable_tools().build()) } async fn list_tools(