Skip to content

Claude Code optional tool parameters become strict on OpenAI Responses routes #3922

Description

@barbusina

Client or integration

Claude Code

Provider or upstream service

OpenAI Responses API / ChatGPT Codex

OpenCodex version

2.47.0 / commit 8bc9e4e

Endpoint or capability

/v1/messages → /v1/responses function tool translation

Current behaviour

When Claude Code sends a custom tool definition without an explicit strict field, OpenCodex translates it into a Responses function tool while also omitting strict.

This changes the request semantics because the Responses API treats an omitted strict value as strict mode. As a result, parameters that are optional in the original Anthropic input_schema can be treated as required by the upstream Responses provider.

For example, a tool may require only prompt while defining isolation and options as optional. After translation, the upstream provider applies strict schema handling even though the client did not request it.

Expected behaviour

Translated function tools should preserve the semantics of the original Anthropic tool definition:

  • If strict is omitted, OpenCodex should emit strict: false.
  • An explicit strict: false should remain false.
  • An explicit strict: true should remain true.
  • The supplied JSON Schema should be forwarded without rewriting its
    properties, required, or nested schemas.
  • Hosted tools such as web search should remain unaffected.
  • Native Anthropic passthrough should remain unaffected.

Minimal redacted request or reproduction

curl http://127.0.0.1:10100/v1/messages \
  -H 'content-type: application/json' \
  -H 'x-api-key: REDACTED' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "openai/gpt-5.4",
    "max_tokens": 32,
    "messages": [
      {
        "role": "user",
        "content": "Run a local agent."
      }
    ],
    "tool_choice": {
      "type": "tool",
      "name": "Agent"
    },
    "tools": [
      {
        "name": "Agent",
        "description": "Run an agent",
        "input_schema": {
          "type": "object",
          "properties": {
            "prompt": {
              "type": "string"
            },
            "isolation": {
              "type": "string",
              "enum": ["worktree", "remote"]
            },
            "options": {
              "type": "object",
              "properties": {
                "enabled": {
                  "type": "boolean"
                }
              }
            }
          },
          "required": ["prompt"],
          "additionalProperties": false
        }
      }
    ]
  }'

Actual response or error

The forwarded `/v1/responses` function tool omits the `strict` field:

{
  "type": "function",
  "name": "Agent",
  "parameters": {
    "type": "object",
    "properties": {
      "prompt": { "type": "string" },
      "isolation": {
        "type": "string",
        "enum": ["worktree", "remote"]
      },
      "options": {
        "type": "object",
        "properties": {
          "enabled": { "type": "boolean" }
        }
      }
    },
    "required": ["prompt"],
    "additionalProperties": false
  }
}

The Responses upstream consequently applies its default strict semantics, although the originating Anthropic request did not enable strict tool use. This breaks tool calls in which the optional arguments are omitted.

Upstream documentation

Anthropic tool definitions and optional parameters:
https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools

Anthropic strict tool use is enabled explicitly with strict: true:
https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use

OpenAI Responses API reference:
https://platform.openai.com/docs/api-reference/responses/create

Suggested mapping or implementation notes

During Anthropic Messages → Responses translation, emit:
strict: typeof tool.strict === "boolean" ? tool.strict : false

The value must then survive the internal parser and the Responses adapter unchanged. Regression coverage should exercise omitted, false, and true values through the complete outbound request-building path and verify that the original JSON Schema is unchanged.

Additional context and attachments

The fix has been verified against the reported failure. It restores optional tool arguments while preserving explicit strict-mode requests, hosted web search, and native Anthropic passthrough.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    on Sep 7, 2026
  2. lidge-jun commented on Sep 7, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 68 / 80

    이 이슈는 Claude Code(Anthropic Messages)가 보낸 커스텀 툴 정의에 strict 필드가 없을 때, OpenCodex가 /v1/messages → /v1/responses로 옮기면서 function tool에도 strict를 아예 안 넣는 문제입니다. 지금 dev HEAD 09f669a75의 src/claude/inbound-content-options.ts toolsToResponses는 type: "function", name, (있으면) description, parameters: input_schema만 넣고 strict는 복사하지 않습니다. Anthropic 쪽에서는 strict: true를 명시해야 strict tool use가 켜지지만, OpenAI Responses API는 function tool의 strict를 생략하면 strict 취급하는 쪽으로 동작합니다. 그래서 원본 input_schema에서 required: ["prompt"]만 두고 isolation/options를 선택으로 둔 툴이, 업스트림에서는 선택 인자를 빠뜨리면 깨집니다. 이슈에 적힌 curl 재현과 “생략이면 strict: false를 내보내라”는 매핑은 현재 코드와 정확히 맞습니다. hosted web_search 분기와 native Anthropic passthrough는 이 번역 경로 밖이라 같이 건드리면 안 됩니다. src/responses/parser-tools.ts는 이미 들어오는 strict boolean을 보존하는 쪽이므로, 고치려면 번역 입구에서 기본값을 넣고 그 값이 어댑터까지 살아남는지만 확인하면 됩니다. 지금 dev는 #3925로 2.48.0 open-dev이고 Claude/Responses 스택(#3877 thinking order, #3830 reasoning envelope, #3808 compatibility gate 등)이 깔려 있어, 이 구멍은 그 위에 남는 호환 버그입니다. types/config 분할과는 무관합니다.

    라인 src/claude/inbound-content-options.ts toolsToResponses - function 툴 push 때 strict를 넣지 않는다. 여기가 이슈의 직접 원인이다.
    라인 (제안) toolsToResponses strict 기본값 - typeof raw.strict === "boolean" ? raw.strict : false처럼 생략→false, 명시 true/false 보존이 이슈 제안과 맞다. 스키마 properties/required/중첩은 그대로 전달해야 한다.
    경로 src/responses/parser-tools.ts - 이미 t.strict !== undefined면 보존한다. 번역층에서 false를 명시해 주면 이후 경로가 삼키지 않는지 회귀로 확인하면 된다.
    경로 hosted web_search / native Anthropic - type.startsWith("web_search") 분기와 Anthropic 직접 경로는 이 기본값 삽입 대상이 아니다. 회귀 범위에서 빼지 말고 “영향 없음”을 증명해야 한다.
    경로 테스트 - omitted / false / true를 Messages→Responses outbound 전체 경로로 고정하고, 원본 JSON Schema 불변을 assert하는 회귀가 필요하다.

    메인테이너의 판단이 필요한 지점

    • Responses 업스트림이 정말로 “생략 = strict”인지(문서·실측) 한 번 더 확인할지, 이슈 작성자 실측만으로 바로 고칠지
    • strict: false를 항상 명시하는 것이 일부 게이트웨이/프록시에서 거부되는지(거부하면 제공자별 예외가 필요한지)
    • 수정 PR을 help-wanted로 둘지, Claude Messages 번역 담당 기여자에게 바로 붙일지

    너의 추천
    수락. toolsToResponses에서 생략 시 strict: false를 명시하고, omitted/false/true + 스키마 불변 + web_search/native 무영향을 회귀로 고정한 작은 PR을 받는다. 중복 이슈로 보이면 이 티켓에 묶고, 임시 워크어라운드(클라이언트에 strict: false를 직접 넣기)는 코멘트로만 안내하면 된다.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 7, 2026

    @lidge-jun
    Owner

    Fixed on dev by #3942, squash-merged as a13041740.

    toolsToResponses now emits strict from the source Anthropic tool exactly as you described: an explicit true or false is preserved, an omitted one becomes an explicit false, and the input_schema is forwarded unchanged, including properties, required and nested schemas. A non-boolean value cannot opt the tool into strict mode, since that is not a valid Anthropic opt-in.

    Hosted web_search leaves the translator before the function-tool branch and gains no strict field, and native Anthropic passthrough never reaches translation, so both are unaffected as you asked.

    The regression exercises omitted, false and true through the complete outbound path and asserts on the body a real Responses adapter serializes, not on the translator's return value — reading the translator's own object back would have passed even if the field were dropped downstream.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions