Skip to content

Resolve local MCP refs in Code Mode tool schemas#31901

Open
aphonpra-oai wants to merge 1 commit into
mainfrom
agent/render-mcp-local-refs
Open

Resolve local MCP refs in Code Mode tool schemas#31901
aphonpra-oai wants to merge 1 commit into
mainfrom
agent/render-mcp-local-refs

Conversation

@aphonpra-oai

Copy link
Copy Markdown

Summary

  • Resolve local JSON Pointer $ref values against the schema root when Code Mode renders TypeScript tool declarations.
  • Support both #/$defs/... and #/definitions/..., including RFC 6901 escaped path segments.
  • Preserve $ref sibling descriptions and intersect renderable sibling constraints.
  • Expand each recursive pointer at most twice per active path. This exposes one nested Boolean group while rendering the next back-edge as unknown, so generated declarations stay finite.
  • Type non-recursive MCP structuredContent.results.items refs instead of rendering Array<unknown>.

Why

MCP schemas that use local or recursive refs currently lose their useful shape in Codex Code Mode. For the SE-8141 fixture, the model sees recursive query fields and structured result items as unknown, which weakens first-call argument construction.

Related: SE-8141, CLI-3774, CONNECT-521.

Scope and limitations

This is a narrow Codex Code Mode fix. It does not change schema normalization or the Functions/Responses v3 renderer.

Recursive rendering is intentionally a finite prefix, not a full recursive TypeScript alias: references deeper than two occurrences on the same active path remain unknown. The depth bound prevents infinite output, but repeated branching can still increase prompt size versus today's renderer. Before making this ready for review, owners should confirm the bound with prompt-size measurements and the saved SE-8141 fixture.

Validation

  • just test -p codex-code-mode-protocol — 26 passed
  • just fix -p codex-code-mode-protocol
  • just fmt

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


Alex Phonpradith seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@aphonpra-oai aphonpra-oai marked this pull request as ready for review July 10, 2026 19:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf1be25414

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

} else {
root.pointer(pointer)
};
let rendered = target.map(|target| self.render(target));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Cap local $ref expansion output

When an MCP/server schema reuses a large $defs entry many times (or a DAG of refs fans out), this recursively renders the target inline for every $ref without any total-size or memoization budget; the active-path guard only stops cycles. That can turn a compact tool schema into a single code-mode exec declaration well over 1k/10k tokens, which is injected into the model context, so please add a hard cap or avoid repeated inline expansion.

AGENTS.md reference: AGENTS.md:L91-L100

Useful? React with 👍 / 👎.

.get("$ref")
.and_then(JsonValue::as_str)
.and_then(|reference| {
let pointer = reference.strip_prefix('#')?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode URI fragments before resolving refs

For schemas that use a valid URI-fragment $ref such as #/$defs/Foo%20Bar (or percent-encoded //~ characters), this keeps the raw fragment and later passes it to serde_json::Value::pointer, so the lookup misses the definition and the generated exec declaration falls back to unknown. The input-schema pruning path already accepts decoded local definition refs, so these schemas can reach code mode with their definitions preserved but still lose their types here; decode the fragment before applying JSON-pointer lookup.

Useful? React with 👍 / 👎.

.map(|variant| self.render(variant))
.collect::<Vec<_>>();
if !rendered.is_empty() {
return rendered.join(" & ");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parenthesize union refs inside allOf

When an allOf branch is a $ref to a union-producing definition (for example a oneOf or multi-value enum), self.render(variant) now returns a string like A | B and this join emits A | B & C. TypeScript parses that as A | (B & C), not (A | B) & C, so the generated code-mode declaration makes the referenced branch appear valid without the other allOf constraints. Parenthesize union operands before joining with &.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant