Skip to content

fix(openai_mcp_tool_resolve): rewrite type:mcp tools to type:function - #488

Merged
leseb merged 2 commits into
praxis-proxy:mainfrom
leseb:leseb/fix-issue-425
Jul 23, 2026
Merged

fix(openai_mcp_tool_resolve): rewrite type:mcp tools to type:function#488
leseb merged 2 commits into
praxis-proxy:mainfrom
leseb:leseb/fix-issue-425

Conversation

@leseb

@leseb leseb commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #425.

The openai_mcp_tool_resolve filter now rewrites type: "mcp" tool entries in the Responses API request body to type: "function" before forwarding upstream, since inference backends have no concept of MCP tools.

  • Resolves MCP tool definitions via tools/list, builds a dispatch map (mcp_tool_map), and replaces MCP entries with function tools using {server_label}__{tool_name} encoding for unique dispatch
  • Rewrites tool_choice MCP references: named MCP → function, server-level MCP → allowed_tools scoped to that server, MCP selectors inside allowed_tools → expanded function refs (unresolved selectors are preserved)
  • Detects generated function name collisions from lossy sanitization/truncation while allowing client-supplied duplicates to pass through to the backend
  • Synchronizes rewritten body, tools, and tool_choice into ResponsesState for downstream filters
  • 87 unit tests and integration test coverage for the rewrite, caching, collision detection, tool_choice translation, and state sync

Test plan

  • cargo test -p praxis-ai-apis -- openai_mcp_tool_resolve — 87 tests pass
  • make lint — clippy, rustfmt, separator, and filter-doc checks pass
  • Integration test mcp_tools_rewritten_to_function_type validates end-to-end rewrite

leseb added 2 commits July 23, 2026 10:51
The openai_mcp_tool_resolve filter now rewrites MCP tool entries to
function tool entries in the request body before forwarding upstream.

This ensures:
1. The proxy always controls MCP execution - upstream never sees
   type:mcp entries
2. Models emit function_call events that the accumulator can capture
   into tool_calls for mcp_dispatch to execute
3. All security controls (SSRF validation, auth injection, approval
   policies) are enforced by the proxy

Changes:
- Change request_body_access() from ReadOnly to ReadWrite
- Add rewrite_request_body() to replace resolved MCP entries with
  function entries using the prefixed naming scheme (label__tool_name)
- Update write_tool_map() to use the rewritten body for ResponsesState
  so downstream filters see the converted tools
- Add Serialization error variant to ResolveError for body rewrite
  failures
- MCP entries without resolved tools (connector_id only, deferred,
  or no server_url) are preserved unchanged for upstream handling

Fixes praxis-proxy#425

Signed-off-by: Sébastien Han <seb@redhat.com>
Refine the type:mcp → type:function rewrite based on review feedback:

- Rewrite server-level MCP tool_choice to allowed_tools form scoped
  to that server's generated functions instead of unrestricted required
- Scope collision detection to generated names only so client-supplied
  duplicate function names pass through to the backend
- Pre-build function tools in resolve_all_entries via a consuming pass,
  eliminating duplication between per_entry and tool_map
- Preserve unresolved MCP selectors in allowed_tools tool_choice
  instead of silently dropping them
- Rewrite tool_choice MCP selectors inside allowed_tools compound form
- Document the server_label__tool_name encoding rationale

Signed-off-by: Sébastien Han <seb@redhat.com>
@leseb
leseb requested review from a team and franciscojavierarceo July 23, 2026 09:17
@praxis-bot-app

Copy link
Copy Markdown

PR too large: 1105 lines added (limit: 750, excludes Cargo files, tests, docs, examples, and benchmarks). Please split into smaller PRs. Add skip/pr-conventions label to override.

@leseb
leseb added this pull request to the merge queue Jul 23, 2026
Merged via the queue into praxis-proxy:main with commit ae5b1c2 Jul 23, 2026
29 of 30 checks passed
@leseb
leseb deleted the leseb/fix-issue-425 branch July 23, 2026 11:20
shaneutt pushed a commit that referenced this pull request Aug 28, 2026
…#488)

* fix(openai_mcp_tool_resolve): rewrite type:mcp tools to type:function

The openai_mcp_tool_resolve filter now rewrites MCP tool entries to
function tool entries in the request body before forwarding upstream.

This ensures:
1. The proxy always controls MCP execution - upstream never sees
   type:mcp entries
2. Models emit function_call events that the accumulator can capture
   into tool_calls for mcp_dispatch to execute
3. All security controls (SSRF validation, auth injection, approval
   policies) are enforced by the proxy

Changes:
- Change request_body_access() from ReadOnly to ReadWrite
- Add rewrite_request_body() to replace resolved MCP entries with
  function entries using the prefixed naming scheme (label__tool_name)
- Update write_tool_map() to use the rewritten body for ResponsesState
  so downstream filters see the converted tools
- Add Serialization error variant to ResolveError for body rewrite
  failures
- MCP entries without resolved tools (connector_id only, deferred,
  or no server_url) are preserved unchanged for upstream handling

Fixes #425

Signed-off-by: Sébastien Han <seb@redhat.com>

* fix(openai_mcp_tool_resolve): address review findings for MCP rewrite

Refine the type:mcp → type:function rewrite based on review feedback:

- Rewrite server-level MCP tool_choice to allowed_tools form scoped
  to that server's generated functions instead of unrestricted required
- Scope collision detection to generated names only so client-supplied
  duplicate function names pass through to the backend
- Pre-build function tools in resolve_all_entries via a consuming pass,
  eliminating duplication between per_entry and tool_map
- Preserve unresolved MCP selectors in allowed_tools tool_choice
  instead of silently dropping them
- Rewrite tool_choice MCP selectors inside allowed_tools compound form
- Document the server_label__tool_name encoding rationale

Signed-off-by: Sébastien Han <seb@redhat.com>

---------

Signed-off-by: Sébastien Han <seb@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openai_mcp_tool_resolve must rewrite type:mcp tools to type:function

2 participants