Skip to content

[Bug]: Meta Muse (api.meta.ai) rejects MCP tool names longer than 64 characters — full catalog fails with 400 #4410

Closed

Description

@dangdh08

Client or integration

Other

ZCode connects to the proxy over OpenAI-compatible http://127.0.0.1:10100/v1 (chat completions translated to Responses).

Provider or upstream service

meta-muse (https://api.meta.ai/v1)

OpenCodex version

2.51.0 (also reproduced on 2.50.0, npm @bitkyc08/opencodex, macOS)

Endpoint or capability

/v1/chat/completions with function tools (translated to Responses); tool declarations, tool_choice, tool-call history

Current behaviour

Requesting meta-muse/muse-spark-1.3-contributor fails with HTTP 400 when the request carries MCP tool names longer than 64 characters. ZCode sends fully-namespaced MCP tool names (for example mcp__plugin_huggingface-skills_huggingface-skills__hub_repo_search, 66 characters), and the whole turn is rejected before any tool is called. The long function names are forwarded to api.meta.ai verbatim. Disabling plugins avoids the error but loses tools.

Expected behaviour

For this strict Responses endpoint, opencodex should keep the full tool catalog but send collision-safe short wire names (at most 64 characters) upstream, then restore the original names in tool calls, tool_choice and history before returning to the client — the same pattern as the existing namespace/custom/tool-search alias restores. Arguments, user text and schema property names must not be renamed. Other providers unchanged.

Minimal redacted request or reproduction

# 1. Start opencodex with the meta-muse provider (https://api.meta.ai/v1, OAuth) on port 10100.
# 2. Send a chat-completions request for meta-muse/muse-spark-1.3-contributor with one
#    placeholder function tool whose name is 66 characters long (66 x "t"), empty object schema.
#    Message: "Reply with exactly OK. Do not call tools."

curl -s http://127.0.0.1:10100/v1/chat/completions -H 'Content-Type: application/json' -d '{
  "model": "meta-muse/muse-spark-1.3-contributor", "stream": false,
  "messages": [{"role": "user", "content": "Reply with exactly OK. Do not call tools."}],
  "tools": [{"type": "function", "function": {"name": "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt", "description": "probe", "parameters": {"type": "object", "properties": {}}}}}]}'

# 3. Observe HTTP 400 (body below).
# 4. Repeat with a 64-character name: HTTP 200, reply "OK".

Real-session evidence: a failing ZCode turn carried 93 tools, 20 of them over 64 characters, for example mcp__plugin_huggingface-skills_huggingface-skills__hub_repo_search (66), mcp__plugin_huggingface-skills_huggingface-skills__hub_repo_details (67), mcp__plugin_android-emulator_android-emulator__android_install_app (66), plus more android-emulator and microsoft-docs names. Replaying the exact 93-name catalog fails with the same 400; the same catalog with only short aliases returns 200.

Actual response or error

HTTP 400
{"error":{"message":"`name` must be at most 64 characters, got 66","type":"invalid_request_error","param":null,"code":"invalid_request_error"}}

Upstream documentation

No public specification found for the 64-character tool-name limit on api.meta.ai; the limit is observed empirically (66-character name returns HTTP 400 invalid_request_error, 64-character name returns HTTP 200).

Suggested mapping or implementation notes

Same pattern as the existing routed namespace/custom/tool-search alias restores: generate deterministic collision-checked short wire names for declarations, tool selectors and history on the Meta Muse path only, and restore original names in function calls, deltas, tool_choice and continuation cache before returning to the client.

Additional context and attachments

Distinct from #3362 (that one is a rejected web_search.indexed_web_access field on opencode-go; this is a tool-name length limit on api.meta.ai). Searched existing issues for the exact message, "64-char", "rename tool" and "meta.ai 400" with no matches. Happy to test a fix against the same 93-tool catalog.

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
    bugSomething isn't working
    catalogModel catalog, slugs, visibility, routed entries
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 12, 2026
  2. github-actions commented on Sep 12, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  3. lidge-jun commented on Sep 12, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 69 / 80

    이 이슈는 Meta Muse(https://api.meta.ai/v1, openai-responses 어댑터)가 함수/도구 이름을 최대 64자로 제한해서, ZCode 같은 클라이언트가 보내는 긴 MCP 이름(예: mcp__plugin_huggingface-skills_…__hub_repo_search, 66자)이 있으면 턴이 도구 호출도 하기 전에 HTTP 400으로 죽는다는 보고다. 메시지 그대로 name must be at most 64 characters, got 66 이다. 플러그인을 끄면 짧아져서 통과하지만 도구를 잃는다. opencodex 2.50/2.51과 현재 dev 기준으로 Meta Muse 경로에 이름 단축/별칭 복원이 없다.

    재현이 깔끔하다. 66자 placeholder 이름 → 400, 64자 → 200 “OK”. 실제 세션은 93개 도구 중 20개가 64자 초과였고, 이름만 짧은 별칭으로 바꾸면 같은 카탈로그가 200이다. 스키마·인자·본문은 건드리지 않고 이름 길이만이 원인이다. #3362(opencode-go의 web_search.indexed_web_access 필드 거절)와는 다른 축이고, 검색해도 동일 증상 이슈는 없다.

    현재 dev에는 비슷한 “와이어 이름 제약 → 별칭 → 복원” 패턴이 이미 있다. 대표적으로 src/adapters/kiro-wire.ts의 kiroToolName / 레지스트리가 1–64자·허용 문자로 정규화하고 nameMap으로 원래 이름을 되돌린다. Meta Muse는 Responses/OpenAI-compat 경로라 Kiro와 파일은 다르지만, 기대 동작(“업스트림엔 ≤64 충돌 안전 짧은 이름, 클라/히스토리/tool_choice엔 원래 이름 복원, 인자·스키마 프로퍼티는 그대로, 다른 프로바이더는 불변”)은 그 패턴을 재사용하면 된다. 전역으로 모든 프로바이더에 씌우면 부작용이 크니, 이슈가 말한 대로 strict Responses(또는 meta-muse 한정)에 거는 편이 안전하다.

    라인 - 해당 없음(이슈; 수정 PR 없음)

    api.meta.ai 도구 name 제약 - 64자 초과 시 카탈로그 전체 400, 긴 MCP 이름과 정면 충돌
    opencodex Meta Muse / openai-responses 경로 - 현재 이름 단축·복원 없음(요청 이름 그대로 전달)
    src/adapters/kiro-wire.ts · kiroToolName - 이미 64자 와이어 별칭 선례가 있어 설계 참고점은 존재
    ZCode 풀 MCP 카탈로그 - 실사용에서 20/93이 한도에 걸림(합성 66자 프로브와 동일 실패)

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

    • 별칭을 meta-muse(또는 api.meta.ai baseUrl)만 적용할지, “이름 길이 한도를 광고하는 Responses 엔드포인트” 일반 정책으로 올릴지
    • 별칭 알고리즘(해시 접미 vs 접두 절단)과 tool_choice / 히스토리 왕복 복원 위치를 어댑터 vs 공통 레이어 중 어디에 둘지
    • 64자 한도를 런타임 프로브로 감지할지, 프로바이더 플래그로 고정할지

    너의 추천

    • 버그로 유지하고 help wanted 수준으로 수정 PR을 받는다. Kiro 별칭 레지스트리 패턴을 참고해 Meta Muse(또는 해당 baseUrl) outbound에만 ≤64 collision-safe wire name을 쓰고, inbound tool_call / tool_choice / 히스토리에서 원래 이름을 복원한다.
    • 회귀는 이슈의 66 vs 64 curl 프로브 + 긴 MCP 이름 다수 카탈로그 fixture면 충분하다. 다른 프로바이더 경로 테스트가 깨지지 않게 스코프를 좁힌다.

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

  4. lidge-jun commented on Sep 12, 2026

    @lidge-jun
    Owner

    Fixed on dev by #4422 (merge commit 01e7d746d22e5a12aa35950c4035a3c5dee0bae6).

    On the api.meta.ai host only, function tool identities that exceed 64 characters — or that carry
    characters outside [a-zA-Z0-9_-] — are rewritten to collision-safe wire names before the request
    leaves, and the original names are restored on the way back, so the client never sees an alias.
    The alias is a sanitized 55-character prefix plus an 8-hex SHA-256 of the original name, which makes
    it deterministic across turns; conforming names claim the collision domain first and a salt loop
    resolves anything left over.

    Your 66-character probe now goes out as a 64-character name and the turn succeeds, and the full
    93-tool catalog from your report is covered by a fixture that asserts nothing over the limit leaves.
    Rewriting covers tools[], history function_call / custom_tool_call names, tool_choice including
    allowed_tools, additional_tools, and chat-shaped tool.function.name. Arguments, message text, and
    schema property names are never touched, and no other provider path changes behavior.

    Restoration runs ahead of namespace restore and ahead of the undeclared-tool guard on the streaming
    path, the non-stream JSON path, the continuation cache, and the inspection check, which matters
    because on a continuation turn only your original names are in the declared catalog. It also covers
    response.function_call_arguments.done, where the name sits outside any item and is read directly by
    that guard. What may be restored is bounded by tool_choice, so a tool you disabled for a turn cannot
    come back as an executable call.

    Closing as fixed. This lands on dev and will ship in the next release; reopen if you still see a 400
    with a long MCP catalog.

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

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entriesproviderProvider 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