Skip to content

feat(openresponses): accept namespace tools - #13098

Open
coffeerunhobby wants to merge 1 commit into
mudler:masterfrom
coffeerunhobby:feature/responses-namespace-tools
Open

coffeerunhobby wants to merge 1 commit into
mudler:masterfrom
coffeerunhobby:feature/responses-namespace-tools

Conversation

@coffeerunhobby

Copy link
Copy Markdown
Contributor

Description

Codex and other Responses API clients send each MCP server's tools as one namespace entry,
{"type": "namespace", "name": "mcp__docs", "tools": [...]}, and route a returned call by its
(namespace, name) pair. convertORToolsToOpenAIFormat keeps only function tools, so the model
never sees an MCP tool, and Codex reports that the tools are not available.

This PR flattens each namespace member to ns_{namespace}___{name}, the form llama.cpp's server
uses (ggml-org/llama.cpp#31568). That flat name goes to the model and the grammar. A
function_call item that carries a namespace in the input, or in a replayed stored response, is
flattened the same way. Returned function_call items are split back into namespace and name
where outputs leave: in buildORResponse and in normalizeORStreamEvent. That covers
non-streaming, streaming, background, WebSocket and stored responses.

Notes for Reviewers

  • The name decodes by itself, so no per-request state is kept. The one assumption, the same as
    llama.cpp's: a namespace does not contain ___.
  • Namespace members are client tools: the client that declared them runs the calls. They are never
    matched against LocalAI's server-side MCP tools (which are injected as plain function tools under
    their own names), even if the names coincide, so a client-declared tool cannot trigger a server tool.
  • Out of scope, possible follow-ups:
    • client-executed tool_search and additional_tools (tools loaded during a turn);
    • tool_choice naming a namespaced function;
    • the 64-character function-name limit some templates have (Codex connector names can be long).
  • Swagger: only the two new fields (tools on schema.ORFunctionTool, namespace on
    schema.ORItemParam). Running swag init on current master also picks up unrelated
    locked/lockedReason drift, which is left out.

Tests: go test ./core/http/endpoints/openresponses/ ./core/schema/ (87/87 and 83/83). The new
specs cover the tool conversion, replaying a namespaced call, and a streamed call coming back with
its namespace in output_item.added, output_item.done and response.completed. They fail with
the change reverted.

Signed commits

  • Yes, I signed my commits.
  • Documentation updated (docs/content/) for user-facing changes, or not applicable

Codex and other Responses clients send each MCP server's tools as one
{"type": "namespace", "name": ..., "tools": [...]} entry and route a
returned call by its (namespace, name) pair. convertORToolsToOpenAIFormat
kept only function tools, so the model never saw an MCP tool.

Flatten each namespace member to ns_{namespace}___{name}, the form
llama.cpp's server uses, for the model and the grammar. Earlier
function_call items that carry a namespace are flattened the same way,
both in the input and when a stored response is replayed. Returned
function_call items are split back into namespace and name where
outputs leave: buildORResponse and normalizeORStreamEvent, which covers
streaming, background, WebSocket and stored responses. The name decodes
by itself, so no per-request state is needed; a namespace must not
contain "___".

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Cristi An <c4xp@msn.com>
@coffeerunhobby

Copy link
Copy Markdown
Contributor Author

I almost forgot: for context, all others are adding the same support for namespace tools :) We are just lucky we see around the corner:

This branch has not been deployed

No deployments
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