Skip to content

初始化握手与ClientInfo

追踪 initialize 如何提交客户端能力、写入连接状态并开启后续 App Server 请求。

基于rust-v0.150.0
CodexRustAppServerInitializeClientInfo

初始化握手与ClientInfo ​

本文承接Connection核心状态和Experimental API边界,面向已经理解 connection state、RPC gate 和 capability filter 的读者。本文回答 initialize 如何验证 ClientInfo、保存 experimental/MCP/opt-out 能力、生成响应并让 outbound writer 进入 ready 状态;不展开具体业务 method 的参数。

1. 握手主线 ​

initialize 不是普通请求:它决定连接后续能否调用 API、哪些实验字段可用、哪些通知被过滤,以及 server 如何识别客户端。initialized notification 完成协议层的客户端确认。

2. ClientInfo字段 ​

源码位置:codex-rs/app-server-protocol/src/protocol/v1.rs :: ClientInfo、InitializeParams

rust
pub struct ClientInfo {
    pub name: String,
    pub title: Option<String>,
    pub version: String,
}

pub struct InitializeParams {
    pub client_info: ClientInfo,
    pub capabilities: Option<InitializeCapabilities>,
}

name 和 version 用于 originator、user-agent 和 analytics;capabilities 则决定 connection behavior。ClientInfo 不是 Thread 或账户身份。

3. 能力写入 ​

源码位置:codex-rs/app-server/src/request_processors/initialize_processor.rs :: initialize

rust
let capabilities = params.capabilities.unwrap_or_default();
let experimental_api_enabled = capabilities.experimental_api;
let request_attestation = capabilities.request_attestation;
let extensions = capabilities.extensions.as_ref();
let client_mcp_extensions = codex_mcp::client_mcp_extensions(
    extensions,
    capabilities.mcp_server_openai_form_elicitation,
);
let opt_out_notification_methods = capabilities
    .opt_out_notification_methods
    .unwrap_or_default();

能力从请求一次性提取,随后写入 InitializedConnectionSessionState。后续请求通过 connection accessor 读取,不会每次重新解释 initialize JSON。

4. 验证与提交 ​

源码位置:codex-rs/app-server/src/request_processors/initialize_processor.rs :: initialize

rust
if session.initialized() {
    return Err(invalid_request("Already initialized"));
}
if HeaderValue::from_str(&name).is_err() {
    return Err(invalid_request(format!(
        "Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value."
    )));
}
if session
    .initialize(InitializedConnectionSessionState {
        experimental_api_enabled,
        opted_out_notification_methods: opt_out_notification_methods.into_iter().collect(),
        app_server_client_name: name.clone(),
        client_version: version,
        request_attestation,
        client_mcp_extensions,
    })
    .is_err()
{
    return Err(invalid_request("Already initialized"));
}

重复 initialize 和非法 header 在状态提交前失败;OnceLock 的二次 set 仍作为保护。验证通过后先提交 session,再设置 originator、analytics、residency 和 user-agent。

源码位置:codex-rs/app-server/src/request_processors/initialize_processor.rs :: send_initialize_notifications_to_connection、send_initialize_notifications

rust
pub(crate) async fn send_initialize_notifications_to_connection(
    &self,
    connection_id: ConnectionId,
) {
    for notification in self.config_warnings.iter().cloned() {
        self.outgoing
            .send_server_notification_to_connections(
                &[connection_id],
                ServerNotification::ConfigWarning(notification),
            )
            .await;
    }
}

pub(crate) async fn send_initialize_notifications(&self) {
    for notification in self.config_warnings.iter().cloned() {
        self.outgoing
            .send_server_notification(ServerNotification::ConfigWarning(notification))
            .await;
    }
}

网络连接和 in-process 连接的 warning 发送入口不同:前者按 connection id 定向发送,后者可以使用共享 sender 广播到当前连接。初始化 response 与 warning 的先后由外层 transport 决定,不能只依赖 initialize processor 的返回值推断客户端已收到全部启动信息。

5. Response与Ready ​

initialize response 携带 user-agent、codex home 和 platform 信息。发送 response 后,in-process 路径可立即设置 outbound initialized;网络路径则由外层在发送 connection-scoped notifications 后完成 ready 切换。

6. Originator副作用 ​

真实客户端初始化会设置 process-global originator 和 user-agent suffix;codex_app_server_daemon、codex-backend 等 non-originating client 不修改这组全局身份。这个规则避免后台连接覆盖实际用户客户端的 analytics 来源。

7. 失败与断连 ​

initialize 失败不会提交 connection capabilities;后续普通 request 会因未初始化被拒绝。初始化成功后断连,outbound state 和 cleanup 仍按 connection owner 回收;全局 originator 不因单个连接断开自动恢复旧值。

8. 源码验证 ​

initialize processor 测试验证重复 initialize、非法 ClientInfo.name、capability 保存和 response 字段;message processor 测试验证未初始化请求被拒绝;in-process start 测试验证函数返回前已完成 handshake。它们证明连接状态和时序,不证明真实客户端会正确发送 initialized notification。

源码位置:

  • codex-rs/app-server/src/request_processors/initialize_processor.rs :: initialize
  • codex-rs/app-server/src/message_processor_tracing_tests.rs :: Initialize
  • codex-rs/app-server/src/in_process.rs :: start
text
cd codex-rs
cargo test -p codex-app-server initialize
cargo test -p codex-app-server initialized
cargo test -p codex-app-server in_process

9. 握手排查 ​

遇到所有普通请求都返回未初始化,先检查 initialize response 和 initialized notification;实验接口被拒绝时检查 capability;通知缺失时检查 opt-out 与 outbound ready;originator 异常时检查 client name 是否属于 non-originating 列表。