Add a getUserAgent MCP method (#3320)
This will allow the extension to pass this user agent + a suffix for its requests
This commit is contained in:
@@ -19,6 +19,7 @@ use codex_core::config::Config;
|
|||||||
use codex_core::config::ConfigOverrides;
|
use codex_core::config::ConfigOverrides;
|
||||||
use codex_core::config::ConfigToml;
|
use codex_core::config::ConfigToml;
|
||||||
use codex_core::config::load_config_as_toml;
|
use codex_core::config::load_config_as_toml;
|
||||||
|
use codex_core::default_client::get_codex_user_agent;
|
||||||
use codex_core::exec::ExecParams;
|
use codex_core::exec::ExecParams;
|
||||||
use codex_core::exec_env::create_env;
|
use codex_core::exec_env::create_env;
|
||||||
use codex_core::get_platform_sandbox;
|
use codex_core::get_platform_sandbox;
|
||||||
@@ -48,6 +49,7 @@ use codex_protocol::mcp_protocol::ExecArbitraryCommandResponse;
|
|||||||
use codex_protocol::mcp_protocol::ExecCommandApprovalParams;
|
use codex_protocol::mcp_protocol::ExecCommandApprovalParams;
|
||||||
use codex_protocol::mcp_protocol::ExecCommandApprovalResponse;
|
use codex_protocol::mcp_protocol::ExecCommandApprovalResponse;
|
||||||
use codex_protocol::mcp_protocol::ExecOneOffCommandParams;
|
use codex_protocol::mcp_protocol::ExecOneOffCommandParams;
|
||||||
|
use codex_protocol::mcp_protocol::GetUserAgentResponse;
|
||||||
use codex_protocol::mcp_protocol::GetUserSavedConfigResponse;
|
use codex_protocol::mcp_protocol::GetUserSavedConfigResponse;
|
||||||
use codex_protocol::mcp_protocol::GitDiffToRemoteResponse;
|
use codex_protocol::mcp_protocol::GitDiffToRemoteResponse;
|
||||||
use codex_protocol::mcp_protocol::InputItem as WireInputItem;
|
use codex_protocol::mcp_protocol::InputItem as WireInputItem;
|
||||||
@@ -169,6 +171,9 @@ impl CodexMessageProcessor {
|
|||||||
ClientRequest::GetUserSavedConfig { request_id } => {
|
ClientRequest::GetUserSavedConfig { request_id } => {
|
||||||
self.get_user_saved_config(request_id).await;
|
self.get_user_saved_config(request_id).await;
|
||||||
}
|
}
|
||||||
|
ClientRequest::GetUserAgent { request_id } => {
|
||||||
|
self.get_user_agent(request_id).await;
|
||||||
|
}
|
||||||
ClientRequest::ExecOneOffCommand { request_id, params } => {
|
ClientRequest::ExecOneOffCommand { request_id, params } => {
|
||||||
self.exec_one_off_command(request_id, params).await;
|
self.exec_one_off_command(request_id, params).await;
|
||||||
}
|
}
|
||||||
@@ -384,6 +389,12 @@ impl CodexMessageProcessor {
|
|||||||
self.outgoing.send_response(request_id, response).await;
|
self.outgoing.send_response(request_id, response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_user_agent(&self, request_id: RequestId) {
|
||||||
|
let user_agent = get_codex_user_agent(Some(&self.config.responses_originator_header));
|
||||||
|
let response = GetUserAgentResponse { user_agent };
|
||||||
|
self.outgoing.send_response(request_id, response).await;
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_user_saved_config(&self, request_id: RequestId) {
|
async fn get_user_saved_config(&self, request_id: RequestId) {
|
||||||
let toml_value = match load_config_as_toml(&self.config.codex_home) {
|
let toml_value = match load_config_as_toml(&self.config.codex_home) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
|
|||||||
@@ -247,6 +247,11 @@ impl McpProcess {
|
|||||||
self.send_request("getUserSavedConfig", None).await
|
self.send_request("getUserSavedConfig", None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send a `getUserAgent` JSON-RPC request.
|
||||||
|
pub async fn send_get_user_agent_request(&mut self) -> anyhow::Result<i64> {
|
||||||
|
self.send_request("getUserAgent", None).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Send a `listConversations` JSON-RPC request.
|
/// Send a `listConversations` JSON-RPC request.
|
||||||
pub async fn send_list_conversations_request(
|
pub async fn send_list_conversations_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ mod interrupt;
|
|||||||
mod list_resume;
|
mod list_resume;
|
||||||
mod login;
|
mod login;
|
||||||
mod send_message;
|
mod send_message;
|
||||||
|
mod user_agent;
|
||||||
|
|||||||
45
codex-rs/mcp-server/tests/suite/user_agent.rs
Normal file
45
codex-rs/mcp-server/tests/suite/user_agent.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use codex_core::default_client::DEFAULT_ORIGINATOR;
|
||||||
|
use codex_core::default_client::get_codex_user_agent;
|
||||||
|
use codex_protocol::mcp_protocol::GetUserAgentResponse;
|
||||||
|
use mcp_test_support::McpProcess;
|
||||||
|
use mcp_test_support::to_response;
|
||||||
|
use mcp_types::JSONRPCResponse;
|
||||||
|
use mcp_types::RequestId;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn get_user_agent_returns_current_codex_user_agent() {
|
||||||
|
let codex_home = TempDir::new().unwrap_or_else(|err| panic!("create tempdir: {err}"));
|
||||||
|
|
||||||
|
let mut mcp = McpProcess::new(codex_home.path())
|
||||||
|
.await
|
||||||
|
.expect("spawn mcp process");
|
||||||
|
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize())
|
||||||
|
.await
|
||||||
|
.expect("initialize timeout")
|
||||||
|
.expect("initialize request");
|
||||||
|
|
||||||
|
let request_id = mcp
|
||||||
|
.send_get_user_agent_request()
|
||||||
|
.await
|
||||||
|
.expect("send getUserAgent");
|
||||||
|
let response: JSONRPCResponse = timeout(
|
||||||
|
DEFAULT_READ_TIMEOUT,
|
||||||
|
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("getUserAgent timeout")
|
||||||
|
.expect("getUserAgent response");
|
||||||
|
|
||||||
|
let received: GetUserAgentResponse =
|
||||||
|
to_response(response).expect("deserialize getUserAgent response");
|
||||||
|
let expected = GetUserAgentResponse {
|
||||||
|
user_agent: get_codex_user_agent(Some(DEFAULT_ORIGINATOR)),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(received, expected);
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
|
|||||||
codex_protocol::mcp_protocol::ExecCommandApprovalParams::export_all_to(out_dir)?;
|
codex_protocol::mcp_protocol::ExecCommandApprovalParams::export_all_to(out_dir)?;
|
||||||
codex_protocol::mcp_protocol::ExecCommandApprovalResponse::export_all_to(out_dir)?;
|
codex_protocol::mcp_protocol::ExecCommandApprovalResponse::export_all_to(out_dir)?;
|
||||||
codex_protocol::mcp_protocol::GetUserSavedConfigResponse::export_all_to(out_dir)?;
|
codex_protocol::mcp_protocol::GetUserSavedConfigResponse::export_all_to(out_dir)?;
|
||||||
|
codex_protocol::mcp_protocol::GetUserAgentResponse::export_all_to(out_dir)?;
|
||||||
codex_protocol::mcp_protocol::ServerNotification::export_all_to(out_dir)?;
|
codex_protocol::mcp_protocol::ServerNotification::export_all_to(out_dir)?;
|
||||||
|
|
||||||
generate_index_ts(out_dir)?;
|
generate_index_ts(out_dir)?;
|
||||||
|
|||||||
@@ -137,6 +137,10 @@ pub enum ClientRequest {
|
|||||||
#[serde(rename = "id")]
|
#[serde(rename = "id")]
|
||||||
request_id: RequestId,
|
request_id: RequestId,
|
||||||
},
|
},
|
||||||
|
GetUserAgent {
|
||||||
|
#[serde(rename = "id")]
|
||||||
|
request_id: RequestId,
|
||||||
|
},
|
||||||
/// Execute a command (argv vector) under the server's sandbox.
|
/// Execute a command (argv vector) under the server's sandbox.
|
||||||
ExecOneOffCommand {
|
ExecOneOffCommand {
|
||||||
#[serde(rename = "id")]
|
#[serde(rename = "id")]
|
||||||
@@ -339,6 +343,12 @@ pub struct GetAuthStatusResponse {
|
|||||||
pub auth_token: Option<String>,
|
pub auth_token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct GetUserAgentResponse {
|
||||||
|
pub user_agent: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetUserSavedConfigResponse {
|
pub struct GetUserSavedConfigResponse {
|
||||||
|
|||||||
Reference in New Issue
Block a user