From fbdedd9a069219a71a21a56b5695c34cb92cbd42 Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Mon, 10 Nov 2025 08:59:14 -0800 Subject: [PATCH] [app-server] feat: add command to generate json schema (#6406) Add a `codex generate-json-schema` command for generating a JSON schema bundle of app-server types, analogous to the existing `codex generate-ts` command for Typescript. --- codex-rs/app-server-protocol/src/export.rs | 2 + codex-rs/app-server/README.md | 31 ++++++++----- codex-rs/cli/src/main.rs | 51 +++++++++++++++++----- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/codex-rs/app-server-protocol/src/export.rs b/codex-rs/app-server-protocol/src/export.rs index c5038a00..4db011f7 100644 --- a/codex-rs/app-server-protocol/src/export.rs +++ b/codex-rs/app-server-protocol/src/export.rs @@ -666,6 +666,8 @@ fn ts_files_in_recursive(dir: &Path) -> Result> { Ok(files) } +/// Generate an index.ts file that re-exports all generated types. +/// This allows consumers to import all types from a single file. fn generate_index_ts(out_dir: &Path) -> Result { let mut entries: Vec = Vec::new(); let mut stems: Vec = ts_files_in(out_dir)? diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index b2d6c25e..2efd52a0 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -8,10 +8,11 @@ Similar to [MCP](https://modelcontextprotocol.io/), `codex app-server` supports ## Message Schema -Currently, you can dump a TypeScript version of the schema using `codex generate-ts`. It is specific to the version of Codex you used to run `generate-ts`, so the two are guaranteed to be compatible. +Currently, you can dump a TypeScript version of the schema using `codex app-server generate-ts`, or a JSON Schema bundle via `codex app-server generate-json-schema`. Each output is specific to the version of Codex you used to run the command, so the generated artifacts are guaranteed to match that version. ``` -codex generate-ts --out DIR +codex app-server generate-ts --out DIR +codex app-server generate-json-schema --out DIR ``` ## Initialization @@ -49,15 +50,16 @@ The JSON-RPC API exposes dedicated methods for managing Codex conversations. Thr ### 1) Start or resume a thread -Start a fresh thread when you need a new Codex conversation. Optional fields mirror CLI defaults: set `model`, `modelProvider`, `cwd`, `approvalPolicy`, `sandbox`, or custom `config` values. Instructions can be set via `baseInstructions` and `developerInstructions`: +Start a fresh thread when you need a new Codex conversation. ```json { "method": "thread/start", "id": 10, "params": { + // Optionally set config settings. If not specified, will use the user's + // current config settings. "model": "gpt-5-codex", "cwd": "/Users/me/project", "approvalPolicy": "never", - "sandbox": "workspace-write", - "baseInstructions": "You're helping with refactors." + "sandbox": "workspaceWrite", } } { "id": 10, "result": { "thread": { @@ -90,7 +92,6 @@ Example: { "method": "thread/list", "id": 20, "params": { "cursor": null, "limit": 25, - "modelProviders": ["openai"] } } { "id": 20, "result": { "data": [ @@ -122,18 +123,23 @@ Turns attach user input (text or images) to a thread and trigger Codex generatio - `{"type":"image","url":"https://…png"}` - `{"type":"localImage","path":"/tmp/screenshot.png"}` -Override knobs apply to the new turn and become the defaults for subsequent turns on the same thread: +You can optionally specify config overrides on the new turn. If specified, these settings become the default for subsequent turns on the same thread. ```json { "method": "turn/start", "id": 30, "params": { "threadId": "thr_123", "input": [ { "type": "text", "text": "Run tests" } ], + // Below are optional config overrides "cwd": "/Users/me/project", - "approvalPolicy": "untrusted", - "sandboxPolicy": "workspace-write", + "approvalPolicy": "unlessTrusted", + "sandboxPolicy": { + "mode": "workspaceWrite", + "writableRoots": ["/Users/me/project"], + "networkAccess": true + }, "model": "gpt-5-codex", "effort": "medium", - "summary": "focus-on-test-failures" + "summary": "concise" } } { "id": 30, "result": { "turn": { "id": "turn_456", @@ -159,7 +165,7 @@ The server requests cancellations for running subprocesses, then emits a `turn/c ## Auth endpoints -The v2 JSON-RPC auth/account surface exposes request/response methods plus server-initiated notifications (no `id`). Use these to determine auth state, start or cancel logins, logout, and inspect ChatGPT rate limits. +The JSON-RPC auth/account surface exposes request/response methods plus server-initiated notifications (no `id`). Use these to determine auth state, start or cancel logins, logout, and inspect ChatGPT rate limits. ### Quick reference - `account/read` — fetch current account info; optionally refresh tokens. @@ -249,5 +255,6 @@ Field notes: ### Dev notes -- `codex generate-ts --out ` emits v2 types under `v2/`. +- `codex app-server generate-ts --out ` emits v2 types under `v2/`. +- `codex app-server generate-json-schema --out ` outputs `codex_app_server_protocol.schemas.json`. - See [“Authentication and authorization” in the config docs](../../docs/config.md#authentication-and-authorization) for configuration knobs. diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 633f2605..17297091 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -1,3 +1,4 @@ +use clap::Args; use clap::CommandFactory; use clap::Parser; use clap_complete::Shell; @@ -82,8 +83,8 @@ enum Subcommand { /// [experimental] Run the Codex MCP server (stdio transport). McpServer, - /// [experimental] Run the app server. - AppServer, + /// [experimental] Run the app server or related tooling. + AppServer(AppServerCommand), /// Generate shell completion scripts. Completion(CompletionCommand), @@ -99,9 +100,6 @@ enum Subcommand { /// Resume a previous interactive session (picker by default; use --last to continue the most recent). Resume(ResumeCommand), - /// [experimental] Generate TypeScript bindings for the app server protocol. - GenerateTs(GenerateTsCommand), - /// [EXPERIMENTAL] Browse tasks from Codex Cloud and apply changes locally. #[clap(name = "cloud", alias = "cloud-tasks")] Cloud(CloudTasksCli), @@ -208,6 +206,22 @@ struct LogoutCommand { } #[derive(Debug, Parser)] +struct AppServerCommand { + /// Omit to run the app server; specify a subcommand for tooling. + #[command(subcommand)] + subcommand: Option, +} + +#[derive(Debug, clap::Subcommand)] +enum AppServerSubcommand { + /// [experimental] Generate TypeScript bindings for the app server protocol. + GenerateTs(GenerateTsCommand), + + /// [experimental] Generate JSON Schema for the app server protocol. + GenerateJsonSchema(GenerateJsonSchemaCommand), +} + +#[derive(Debug, Args)] struct GenerateTsCommand { /// Output directory where .ts files will be written #[arg(short = 'o', long = "out", value_name = "DIR")] @@ -218,6 +232,13 @@ struct GenerateTsCommand { prettier: Option, } +#[derive(Debug, Args)] +struct GenerateJsonSchemaCommand { + /// Output directory where the schema bundle will be written + #[arg(short = 'o', long = "out", value_name = "DIR")] + out_dir: PathBuf, +} + #[derive(Debug, Parser)] struct StdioToUdsCommand { /// Path to the Unix domain socket to connect to. @@ -410,9 +431,20 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() prepend_config_flags(&mut mcp_cli.config_overrides, root_config_overrides.clone()); mcp_cli.run().await?; } - Some(Subcommand::AppServer) => { - codex_app_server::run_main(codex_linux_sandbox_exe, root_config_overrides).await?; - } + Some(Subcommand::AppServer(app_server_cli)) => match app_server_cli.subcommand { + None => { + codex_app_server::run_main(codex_linux_sandbox_exe, root_config_overrides).await?; + } + Some(AppServerSubcommand::GenerateTs(gen_cli)) => { + codex_app_server_protocol::generate_ts( + &gen_cli.out_dir, + gen_cli.prettier.as_deref(), + )?; + } + Some(AppServerSubcommand::GenerateJsonSchema(gen_cli)) => { + codex_app_server_protocol::generate_json(&gen_cli.out_dir)?; + } + }, Some(Subcommand::Resume(ResumeCommand { session_id, last, @@ -527,9 +559,6 @@ async fn cli_main(codex_linux_sandbox_exe: Option) -> anyhow::Result<() tokio::task::spawn_blocking(move || codex_stdio_to_uds::run(socket_path.as_path())) .await??; } - Some(Subcommand::GenerateTs(gen_cli)) => { - codex_app_server_protocol::generate_ts(&gen_cli.out_dir, gen_cli.prettier.as_deref())?; - } Some(Subcommand::Features(FeaturesCli { sub })) => match sub { FeaturesSubcommand::List => { // Respect root-level `-c` overrides plus top-level flags like `--profile`.