feat: initial McpClient for Rust (#822)
This PR introduces an initial `McpClient` that we will use to give Codex
itself programmatic access to foreign MCPs. This does not wire it up in
Codex itself yet, but the new `mcp-client` crate includes a `main.rs`
for basic testing for now.
Manually tested by sending a `tools/list` request to Codex's own MCP
server:
```
codex-rs$ cargo build
codex-rs$ cargo run --bin codex-mcp-client ./target/debug/codex-mcp-server
{
"tools": [
{
"description": "Run a Codex session. Accepts configuration parameters matching the Codex Config struct.",
"inputSchema": {
"properties": {
"approval-policy": {
"description": "Execution approval policy expressed as the kebab-case variant name (`unless-allow-listed`, `auto-edit`, `on-failure`, `never`).",
"enum": [
"auto-edit",
"unless-allow-listed",
"on-failure",
"never"
],
"type": "string"
},
"cwd": {
"description": "Working directory for the session. If relative, it is resolved against the server process's current working directory.",
"type": "string"
},
"disable-response-storage": {
"description": "Disable server-side response storage.",
"type": "boolean"
},
"model": {
"description": "Optional override for the model name (e.g. \"o3\", \"o4-mini\")",
"type": "string"
},
"prompt": {
"description": "The *initial user prompt* to start the Codex conversation.",
"type": "string"
},
"sandbox-permissions": {
"description": "Sandbox permissions using the same string values accepted by the CLI (e.g. \"disk-write-cwd\", \"network-full-access\").",
"items": {
"enum": [
"disk-full-read-access",
"disk-write-cwd",
"disk-write-platform-user-temp-folder",
"disk-write-platform-global-temp-folder",
"disk-full-write-access",
"network-full-access"
],
"type": "string"
},
"type": "array"
}
},
"required": [
"prompt"
],
"type": "object"
},
"name": "codex"
}
]
}
```
This commit is contained in:
43
codex-rs/mcp-client/src/main.rs
Normal file
43
codex-rs/mcp-client/src/main.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Simple command-line utility to exercise `McpClient`.
|
||||
//!
|
||||
//! Example usage:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo run -p codex-mcp-client -- `codex-mcp-server`
|
||||
//! ```
|
||||
//!
|
||||
//! Any additional arguments after the first one are forwarded to the spawned
|
||||
//! program. The utility connects, issues a `tools/list` request and prints the
|
||||
//! server's response as pretty JSON.
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use codex_mcp_client::McpClient;
|
||||
use mcp_types::ListToolsRequestParams;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Collect command-line arguments excluding the program name itself.
|
||||
let cmd_args: Vec<String> = std::env::args().skip(1).collect();
|
||||
|
||||
if cmd_args.is_empty() || cmd_args[0] == "--help" || cmd_args[0] == "-h" {
|
||||
eprintln!("Usage: mcp-client <program> [args..]\n\nExample: mcp-client codex-mcp-server");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Spawn the subprocess and connect the client.
|
||||
let client = McpClient::new_stdio_client(cmd_args.clone())
|
||||
.await
|
||||
.with_context(|| format!("failed to spawn subprocess: {:?}", cmd_args))?;
|
||||
|
||||
// Issue `tools/list` request (no params).
|
||||
let tools = client
|
||||
.list_tools(None::<ListToolsRequestParams>)
|
||||
.await
|
||||
.context("tools/list request failed")?;
|
||||
|
||||
// Print the result in a human readable form.
|
||||
println!("{}", serde_json::to_string_pretty(&tools)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user