openai / openai/codex

Code-mode MCP schema compaction erases nested operation unions into Array<unknown | …>

Open
#43,902 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug CLI mcp
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

What version of Codex CLI is running?

codex-cli 0.153.4. This report is scoped to that installed version; no claim of reproduction on a newer release.

What subscription do you have?

Not collected.

Which model were you using?

GPT-6 family, code-mode tool declarations (ALL_TOOLS / functions.exec).

What platform is your computer?

macOS, Apple Silicon.

Terminal / Codex doctor report

Not collected. No local configuration, credentials or private session logs are included.

What issue are you seeing?

A connected MCP tool has a fully typed input schema, but the model-visible TypeScript declaration erases every nested operation variant:

apply_image_operations(args: {
  expectedRevision: number;
  idempotencyKey: string;
  jobId: string;
  operations: Array<unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown | unknown>;
}): Promise<CallToolResult>;

The released schema contains 14 inline object variants under properties.operations.items.oneOf, each with its own type discriminator and typed fields. This is not an unresolved $ref or sibling-properties-at-root case.

Observed with @formseed2d/mcp@1.1.2. After restarting/resuming the same CLI conversation, /mcp and the actual model-visible catalog both expose all 12 server tool names. Live health reports the expected package version, process identity, aggregate input hash and per-tool hashes. Thus this report concerns lost input structure, not missing tool names or a failed server connection. The server's hash report is supporting evidence, not independent executable attestation.

What steps can reproduce the bug?

Use an MCP input schema with this shape and enough typed fields to exceed the host's normalized schema budget. This standalone Python generator provides a synthetic fixture with no application data:

import json
variants = []
for i in range(14):
    props = {"type": {"type": "string", "const": f"operation_{i}"}}
    props.update({f"field_{j}": {"type": "integer", "minimum": 0,
                               "maximum": 1023} for j in range(12)})
    variants.append({"type": "object", "properties": props,
                     "required": list(props), "additionalProperties": False})
schema = {
    "type": "object",
    "properties": {
        "expectedRevision": {"type": "integer", "minimum": 0},
        "idempotencyKey": {"type": "string"},
        "jobId": {"type": "string"},
        "operations": {"type": "array", "minItems": 1, "maxItems": 20,
                       "items": {"oneOf": variants}},
    },
    "required": ["expectedRevision", "idempotencyKey", "jobId", "operations"],
}
print(json.dumps(schema))

Register this as a disposable MCP tool's inputSchema and inspect its actual code-mode declaration. No mutation handler is needed to inspect declarations. Also test the host pipeline directly: MCP schema parsing/compaction → TypeScript rendering.

Evidence scope: the real FormSeed tool declaration above was observed live. Separately, applying the source depth-collapse pass to the exact released FormSeed schema reproduces 14 empty objects at the operation-variant positions. The standalone synthetic generator above is supplied as a proposed regression fixture; it has not been run through a complete fresh CLI session.

What is the expected behavior?

Preserve useful typed operation discriminants and fields in the model-visible declaration, or provide a supported way to retrieve the full input schema when compaction is necessary. A silent union of 14 unknown alternatives prevents callers from discovering valid operations and arguments through the tool declaration.

Additional information / source investigation

In the inspected rust-v0.153.4 source:

  • codex-rs/tools/src/mcp_tool.rs routes inputs through parse_tool_input_schema.
  • codex-rs/tools/src/json_schema/compaction.rs uses a 5,000-byte compact-normalized schema budget and a depth-3 collapse pass.
  • The pass replaces complex schemas at that depth with {}. The FormSeed schema is 8,145 bytes as raw compact JSON (distinct from the policy's normalized-byte measurement), and its inline operation objects sit at depth 3.
  • codex-rs/code-mode-protocol/src/json_schema_types.rs renders empty schemas as unknown.

This source-level mechanism reproduces the observed shape, but a complete pipeline regression test is still needed to establish the exact triggering passes and normalized size. Please retain resource/recursion limits when addressing this; the request is not to remove all schema budgets.

Related: #42283 also concerns code-mode schema information loss, but describes sibling properties beside root-level oneOf; this report concerns large nested discriminated operation unions erased by compaction.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in codex-rs/tools/src/mcp_tool.rs and follow parse_tool_input_schema into codex-rs/tools/src/json_schema/compaction.rs; then inspect rendering in codex-rs/code-mode-protocol/src/json_schema_types.rs. Run the supplied synthetic schema through the MCP schema-compaction and TypeScript-rendering pipeline, and establish a regression test showing nested operation discriminants and fields remain discoverable within resource limits.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.