Files
codex/codex-rs/exec-server/tests/suite/list_tools.rs
Michael Bolin 3c3d3d1adc fix: add integration tests for codex-exec-mcp-server with execpolicy (#7617)
This PR introduces integration tests that run
[codex-shell-tool-mcp](https://www.npmjs.com/package/@openai/codex-shell-tool-mcp)
as a user would. Note that this requires running our fork of Bash, so we
introduce a [DotSlash](https://dotslash-cli.com/) file for `bash` so
that we can run the integration tests on multiple platforms without
having to check the binaries into the repository. (As noted in the
DotSlash file, it is slightly more heavyweight than necessary, which may
be worth addressing as disk space in CI is limited:
https://github.com/openai/codex/pull/7678.)

To start, this PR adds two tests:

- `list_tools()` makes the `list_tools` request to the MCP server and
verifies we get the expected response
- `accept_elicitation_for_prompt_rule()` defines a `prefix_rule()` with
`decision="prompt"` and verifies the elicitation flow works as expected

Though the `accept_elicitation_for_prompt_rule()` test **only works on
Linux**, as this PR reveals that there are currently issues when running
the Bash fork in a read-only sandbox on Linux. This will have to be
fixed in a follow-up PR.

Incidentally, getting this test run to correctly on macOS also requires
a recent fix we made to `brew` that hasn't hit a mainline release yet,
so getting CI green in this PR required
https://github.com/openai/codex/pull/7680.
2025-12-07 06:39:38 +00:00

77 lines
2.6 KiB
Rust

#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::borrow::Cow;
use std::fs;
use std::sync::Arc;
use anyhow::Result;
use exec_server_test_support::create_transport;
use pretty_assertions::assert_eq;
use rmcp::ServiceExt;
use rmcp::model::Tool;
use rmcp::model::object;
use serde_json::json;
use tempfile::TempDir;
/// Verify the list_tools call to the MCP server returns the expected response.
#[tokio::test(flavor = "current_thread")]
async fn list_tools() -> Result<()> {
let codex_home = TempDir::new()?;
let policy_dir = codex_home.path().join("policy");
fs::create_dir_all(&policy_dir)?;
fs::write(
policy_dir.join("default.codexpolicy"),
r#"prefix_rule(pattern=["ls"], decision="prompt")"#,
)?;
let transport = create_transport(codex_home.path())?;
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
);
Ok(())
}