From 73c4d19093ef1be1402fbdb925a5328caf2e9c22 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Thu, 4 Dec 2025 16:41:49 -0800 Subject: [PATCH] fix: add test that verifies that codex-exec-mcp-server starts up --- codex-rs/Cargo.lock | 1 + codex-rs/exec-server/Cargo.toml | 5 +- codex-rs/exec-server/tests/all.rs | 3 + .../exec-server/tests/suite/auto_approve.rs | 88 +++++++++++++++++++ codex-rs/exec-server/tests/suite/bash | 25 ++++++ codex-rs/exec-server/tests/suite/mod.rs | 3 + 6 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 codex-rs/exec-server/tests/all.rs create mode 100644 codex-rs/exec-server/tests/suite/auto_approve.rs create mode 100755 codex-rs/exec-server/tests/suite/bash create mode 100644 codex-rs/exec-server/tests/suite/mod.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index c3b6c27bee..fb6e0260b1 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1253,6 +1253,7 @@ name = "codex-exec-server" version = "0.0.0" dependencies = [ "anyhow", + "assert_cmd", "async-trait", "clap", "codex-core", diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index 5f8032595e..84b2a9bab6 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "codex-exec-server" -version.workspace = true edition.workspace = true license.workspace = true +name = "codex-exec-server" +version.workspace = true [[bin]] name = "codex-execve-wrapper" @@ -55,5 +55,6 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } [dev-dependencies] +assert_cmd = { workspace = true } pretty_assertions = { workspace = true } tempfile = { workspace = true } diff --git a/codex-rs/exec-server/tests/all.rs b/codex-rs/exec-server/tests/all.rs new file mode 100644 index 0000000000..7e136e4cce --- /dev/null +++ b/codex-rs/exec-server/tests/all.rs @@ -0,0 +1,3 @@ +// Single integration test binary that aggregates all test modules. +// The submodules live in `tests/suite/`. +mod suite; diff --git a/codex-rs/exec-server/tests/suite/auto_approve.rs b/codex-rs/exec-server/tests/suite/auto_approve.rs new file mode 100644 index 0000000000..a7354aed3a --- /dev/null +++ b/codex-rs/exec-server/tests/suite/auto_approve.rs @@ -0,0 +1,88 @@ +use std::borrow::Cow; +use std::path::Path; +use std::process::Stdio; +use std::sync::Arc; + +use anyhow::Result; +use pretty_assertions::assert_eq; +use rmcp::ServiceExt; +use rmcp::model::Tool; +use rmcp::model::object; +use rmcp::transport::ConfigureCommandExt; +use rmcp::transport::TokioChildProcess; +use serde_json::json; +use tokio::process::Command; + +#[tokio::test(flavor = "current_thread")] +async fn auto_approve() -> Result<()> { + let mcp_executable = assert_cmd::Command::cargo_bin("codex-exec-mcp-server")?; + let execve_wrapper = assert_cmd::Command::cargo_bin("codex-execve-wrapper")?; + let bash = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("suite") + .join("bash"); + let transport = + TokioChildProcess::new(Command::new(mcp_executable.get_program()).configure(|cmd| { + cmd.arg("--bash").arg(bash); + cmd.arg("--execve").arg(execve_wrapper.get_program()); + + // Important: pipe stdio so rmcp can speak JSON-RPC over stdin/stdout + cmd.stdin(Stdio::piped()); + cmd.stdout(Stdio::piped()); + + // Optional but very helpful while debugging: + cmd.stderr(Stdio::inherit()); + }))?; + + let service = ().serve(transport).await?; + let tools = service.list_tools(Default::default()).await?.tools; + assert_eq!( + vec![Tool { + name: Cow::Borrowed("shell"), + title: None, + description: Some(Cow::Borrowed( + "Runs a shell command and returns its output. You MUST provide the workdir as an absolute path." + )), + input_schema: Arc::new(object(json!( { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": { + "command": { + "description": "The bash string to execute.", + "type": "string", + }, + "login": { + "description": "Launch Bash with -lc instead of -c: defaults to true.", + "nullable": true, + "type": "boolean", + }, + "timeout_ms": { + "description": "The timeout for the command in milliseconds.", + "format": "uint64", + "minimum": 0, + "nullable": true, + "type": "integer", + }, + "workdir": { + "description": "The working directory to execute the command in. Must be an absolute path.", + "type": "string", + }, + }, + "required": [ + "command", + "workdir", + ], + "title": "ExecParams", + "type": "object", + }))), + output_schema: None, + annotations: None, + icons: None, + meta: None + }], + tools + ); + + // TODO(mbolin): Make shell tool calls and verify they work. + + Ok(()) +} diff --git a/codex-rs/exec-server/tests/suite/bash b/codex-rs/exec-server/tests/suite/bash new file mode 100755 index 0000000000..5c9ffba09e --- /dev/null +++ b/codex-rs/exec-server/tests/suite/bash @@ -0,0 +1,25 @@ +#!/usr/bin/env dotslash + +{ + "name": "codex-bash", + "platforms": { + "macos-aarch64": { + "size": 37003612, + "hash": "blake3", + "digest": "d9cd5928c993b65c340507931c61c02bd6e9179933f8bf26a548482bb5fa53bb", + "format": "tar.gz", + "path": "package/vendor/aarch64-apple-darwin/bash/macos-15/bash", + "providers": [ + { + "url": "https://github.com/openai/codex/releases/download/rust-v0.65.0/codex-shell-tool-mcp-npm-0.65.0.tgz" + }, + { + "type": "github-release", + "repo": "openai/codex", + "tag": "rust-v0.65.0", + "name": "codex-shell-tool-mcp-npm-0.65.0.tgz" + } + ] + } + } +} diff --git a/codex-rs/exec-server/tests/suite/mod.rs b/codex-rs/exec-server/tests/suite/mod.rs new file mode 100644 index 0000000000..1008062c02 --- /dev/null +++ b/codex-rs/exec-server/tests/suite/mod.rs @@ -0,0 +1,3 @@ +// TODO(mbolin): Open this up to more OS's once the Bash DotSlash file includes other platforms. +#[cfg(all(target_os = "macos", target_arch = "aarch64"))] +mod auto_approve;