Skip to content

refactor(protocol): model ResponseTool as tagged enum to match Responses spec and tighten MCP validation - #532

Merged
slin1237 merged 3 commits into
mainfrom
refactor/responses-tool-tagged-enum
Feb 25, 2026
Merged

slin1237 merged 3 commits into
mainfrom
refactor/responses-tool-tagged-enum

Conversation

@zhaowenzi

@zhaowenzi zhaowenzi commented Feb 24, 2026 •

Copy link
Copy Markdown
Contributor

Description

Problem

ResponseTool was previously modeled as a flattened struct with a type field plus many optional fields shared across tool kinds. This made it easy for invalid combinations (e.g. MCP-only fields on non-MCP tools) to silently pass parsing/validation, and it doesn’t scale well as OpenAI’s Responses API has (we are going to support) more tool types and tool-specific fields. Extending support would require more and more ad-hoc, per-field validation rules to prevent “field bleed”.

Solution

Refactor ResponseTool to match the OpenAI Responses API tool schema: an internally tagged enum (type-discriminated) where each tool type owns only its valid fields. This shifts correctness from “runtime validation of a permissive struct” to “type-level correctness”, reducing validation complexity and making it straightforward to add additional OpenAI tool types in the future without proliferating conditional checks.

Changes

  • Refactored ResponseTool from a flat struct to #[serde(tag = "type")] enum with tool-specific structs (FunctionTool, McpTool, WebSearchPreviewTool, CodeInterpreterTool).
  • Updated tool extraction and Harmony/OpenAI routers to work with the new representation; MCP tool-call classification uses session.has_exposed_tool() (MCP tools are exposed as function tools to the model).
  • Updated tests to construct tools using the new enum variants; added a deserialization test to ensure unknown fields on function tools are rejected.

Test Plan

Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated

Summary by CodeRabbit

Release Notes

  • Refactor
    • Restructured internal tool representation system for improved consistency and maintainability across tool types (function, MCP, web search, and code interpreter).
    • Enhanced validation for MCP tools, now enforcing required server label configuration and stricter format constraints.
    • Simplified tool selection logic to rely on session-based exposure checks rather than precomputed tool sets.

@github-actions github-actions Bot added grpc gRPC client and router changes mcp MCP related changes tests Test changes protocols Protocols crate changes model-gateway Model gateway crate changes openai OpenAI router changes labels Feb 24, 2026
@coderabbitai

coderabbitai Bot commented Feb 24, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR refactors the ResponseTool representation from a struct with a type discriminator field to a strongly-typed enum with variants (Function, Mcp, WebSearchPreview, CodeInterpreter), removing the ResponseToolType enum. The change propagates through MCP bridging, tool extraction utilities, and streaming processors, replacing type-field-based pattern matching with enum-variant matching and removing precomputed MCP tool name sets in favor of session-based exposure checks.

Changes

Cohort / File(s) Summary
Core Protocol Types
protocols/src/responses.rs
Introduced ResponseTool enum with Function, Mcp, WebSearchPreview, CodeInterpreter variants. Added FunctionTool, McpTool, WebSearchPreviewTool, CodeInterpreterTool structs. Removed ResponseToolType enum. Made server_label required in McpTool with validation. Updated validation logic to pattern-match on enum variants.
MCP Bridge
mcp/src/responses_bridge.rs
Changed ResponseTool construction from Mcp variant with optional fields to Function variant wrapping FunctionTool. Updated imports to use FunctionTool instead of ResponseToolType.
Tool Extraction & Common Utils
model_gateway/src/routers/grpc/common/responses/utils.rs, model_gateway/src/routers/mcp_utils.rs
Updated pattern matching from ResponseToolType to ResponseTool enum variants. Removed include_mcp parameter from extract_tools_from_response_tools, now extracting only function tools. Updated test harness to construct ResponseTool variants.
Harmony Streaming & Processing
model_gateway/src/routers/grpc/harmony/builder.rs, model_gateway/src/routers/grpc/harmony/responses/common.rs, model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs, model_gateway/src/routers/grpc/harmony/responses/streaming.rs, model_gateway/src/routers/grpc/harmony/streaming.rs, model_gateway/src/routers/grpc/harmony/stages/preparation.rs
Removed build_mcp_tool_names_set helper function and mcp_tool_names HashSet parameters. Replaced static MCP tool set lookup with session.has_exposed_tool() runtime checks. Updated ToolLike trait implementations to pattern-match on ResponseTool enum variants. Updated process_responses_iteration_stream, process_responses_dual_stream, process_decode_stream signatures to remove mcp_tool_names parameter.
OpenAI Router & Regular Conversions
model_gateway/src/routers/openai/responses/streaming.rs, model_gateway/src/routers/openai/responses/utils.rs, model_gateway/src/routers/grpc/regular/responses/conversions.rs
Updated imports from ResponseToolType to ResponseTool. Replaced type-field-based matching with ResponseTool::Mcp(_) enum pattern matching. Updated response_tool_to_value to destructure MCP variant and access fields from McpTool struct. Updated extract_tools_from_response_tools call to remove include_mcp argument.
Tests
model_gateway/tests/api/responses_api_test.rs, model_gateway/tests/spec/responses.rs
Updated test fixtures to construct tools via ResponseTool enum variants (e.g., ResponseTool::Function(...), ResponseTool::Mcp(...)). Wired MCP fields into McpTool struct instead of top-level ResponseTool. Updated deserialization tests to validate enum-variant behavior with deny_unknown_fields attribute. Refactored normalization and validation tests to use new enum-based representations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • key4ng
  • slin1237
  • CatherineSue

Poem

🐰 With enum variants bright and keen,
ResponseTool's a beauty to be seen!
No more type tags in scattered fields,
Each tool variant now their secrets yields—
Sessions check exposure, clean and spry,
Type safety soars, no more to spy! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main refactoring change: converting ResponseTool from a flat struct to a tagged enum to align with the Responses API spec and enhance MCP validation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/responses-tool-tagged-enum

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ses spec and tighten MCP validation

Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
@zhaowenzi
zhaowenzi force-pushed the refactor/responses-tool-tagged-enum branch from 5ec5d4d to 5843ebb Compare February 24, 2026 21:58
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @zhaowenzi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the internal representation of ResponseTool within the openai-protocol crate, transitioning it from a generic struct to a more robust and type-safe tagged enum. This change not only brings the model closer to the Responses API specification but also allows for tighter validation rules, particularly for MCP tools. The widespread impact of this refactoring is evident in the model_gateway crate, where numerous modules have been updated to accommodate the new enum-based tool handling, leading to cleaner and more explicit code for managing different tool types.

Highlights

  • Refactored ResponseTool to a Tagged Enum: The core ResponseTool structure in openai-protocol has been transformed from a struct with a type field to a Rust tagged enum, aligning it more closely with the Responses API specification.
  • Introduced Specific Tool Structs: New dedicated structs (FunctionTool, McpTool, WebSearchPreviewTool, CodeInterpreterTool) were introduced to encapsulate the specific data for each tool type, improving type safety and clarity.
  • Enhanced MCP Validation: The refactoring enables stricter validation for MCP (Multi-Cloud Platform) tools, ensuring that required fields like server_label are present and correctly formatted, and that server_label values are unique.
  • Simplified Tool Handling Logic: Various modules across model_gateway have been updated to leverage the new enum structure, simplifying tool extraction, filtering, and routing logic by directly matching on ResponseTool variants instead of checking a type field.
  • Removed ResponseToolType Enum: The redundant ResponseToolType enum has been removed as its functionality is now handled by the tagged ResponseTool enum.
Changelog
  • mcp/src/responses_bridge.rs
    • Updated ResponseTool construction to use the new ResponseTool::Function variant.
  • model_gateway/src/routers/grpc/common/responses/utils.rs
    • Removed ResponseToolType import.
    • Updated tool matching logic to use ResponseTool enum variants.
    • Simplified extract_tools_from_response_tools by removing the include_mcp parameter.
  • model_gateway/src/routers/grpc/harmony/builder.rs
    • Removed ResponseToolType import.
    • Updated ToolLike trait implementations for Tool and ResponseTool to reflect the new enum structure.
    • Adjusted mapping of ResponseTool to string types.
  • model_gateway/src/routers/grpc/harmony/responses/common.rs
    • Removed ResponseToolType import.
    • Removed the build_mcp_tool_names_set function.
  • model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs
    • Removed build_mcp_tool_names_set import.
    • Updated logic for separating MCP and function tool calls to use session.has_exposed_tool.
  • model_gateway/src/routers/grpc/harmony/responses/streaming.rs
    • Removed ResponseToolType import.
    • Removed mcp_tool_names parameter from several function calls.
    • Updated logic for separating MCP and function tool calls to use session.has_exposed_tool.
  • model_gateway/src/routers/grpc/harmony/stages/preparation.rs
    • Removed the include_mcp parameter from extract_tools_from_response_tools call.
  • model_gateway/src/routers/grpc/harmony/streaming.rs
    • Removed HashSet import.
    • Removed mcp_tool_names parameter from process_responses_iteration_stream, process_responses_dual_stream, and process_decode_stream functions.
    • Updated logic for determining response_format to rely on McpToolSession.
  • model_gateway/src/routers/grpc/regular/responses/conversions.rs
    • Removed the include_mcp parameter from extract_tools_from_response_tools call.
    • Updated comments related to MCP tool merging.
  • model_gateway/src/routers/mcp_utils.rs
    • Removed ResponseToolType import.
    • Updated collect_builtin_routing, extract_builtin_types, and ensure_request_mcp_client functions to match on ResponseTool enum variants.
    • Updated test cases to instantiate ResponseTool using new enum variants.
  • model_gateway/src/routers/openai/responses/streaming.rs
    • Removed ResponseToolType import.
    • Updated tool matching logic to use ResponseTool enum variants.
  • model_gateway/src/routers/openai/responses/utils.rs
    • Removed ResponseToolType import.
    • Updated response_tool_to_value to match on ResponseTool enum variants and serialize built-in tools directly.
  • model_gateway/tests/api/responses_api_test.rs
    • Updated test cases to instantiate ResponseTool using the new enum variants (McpTool, WebSearchPreviewTool, CodeInterpreterTool).
  • model_gateway/tests/spec/responses.rs
    • Updated test cases to instantiate ResponseTool using the new enum variants (FunctionTool, McpTool).
    • Modified validation tests for function and MCP tools to reflect deserialization failures for malformed enum variants.
    • Updated logic for validate_response_tools to directly match on ResponseTool::Mcp.
  • protocols/src/responses.rs
    • Refactored ResponseTool from a struct to a tagged enum.
    • Introduced new structs: FunctionTool, McpTool, WebSearchPreviewTool, and CodeInterpreterTool.
    • Removed the ResponseToolType enum.
    • Added #[serde(deny_unknown_fields)] to new tool structs for stricter deserialization.
    • Updated validate_tool_choice_with_tools and validate_response_tools to match on ResponseTool enum variants.
Activity
  • The pull request was authored by zhaowenzi.
  • The pull request description indicates the intent to refactor ResponseTool to a tagged enum to match the Responses API specification and tighten MCP validation.
  • The author has confirmed that cargo +nightly fmt and cargo clippy checks pass.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the ResponseTool enum to use type-based discrimination instead of a type field, introducing dedicated structs for FunctionTool, WebSearchPreviewTool, CodeInterpreterTool, and McpTool. The code changes remove ResponseToolType enum. The extract_tools_from_response_tools function is updated to extract only function tools. The review comments identify a Server-Side Request Forgery (SSRF) vulnerability in the ensure_request_mcp_client function due to the lack of validation of the user-provided server_url in the McpTool configuration, which could allow an attacker to point to internal services or cloud metadata endpoints. The validation logic for ResponseTool::Mcp only verifies the server_label format but lacks any validation for the server_url field.

Comment thread model_gateway/src/routers/mcp_utils.rs
Comment thread protocols/src/responses.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
model_gateway/src/routers/grpc/harmony/responses/streaming.rs (1)

244-268: ⚠️ Potential issue | 🟠 Major

Ensure max_tool_calls counts function tool calls when MCP is enabled.

total_calls_after only includes MCP calls, but max_tool_calls is a global limit. If the model returns MCP + function tool calls in the same iteration, the limit can be exceeded without emitting incomplete_details. Consider counting both types (or explicitly document MCP-only semantics).

🛠️ Suggested fix
-                let total_calls_after = mcp_tracking.total_calls() + mcp_tool_calls.len();
+                let total_calls_after = mcp_tracking.total_calls()
+                    + mcp_tool_calls.len()
+                    + function_tool_calls.len();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/grpc/harmony/responses/streaming.rs` around lines
244 - 268, The check for exceeding max_tool_calls only adds MCP calls; include
function tool calls as well: compute total_calls_after using
mcp_tracking.total_calls() + mcp_tool_calls.len() + function_tool_calls.len()
(or otherwise sum mcp_tool_calls and function_tool_calls) and compare that sum
to effective_limit; update the warn() fields (new_calls/total_after) to reflect
the combined new calls and resultant total, and ensure any emitted
incomplete_details path uses this combined count logic (refer to mcp_tool_calls,
function_tool_calls, total_calls_after, effective_limit,
mcp_tracking.total_calls(), and max_tool_calls).
mcp/src/responses_bridge.rs (1)

76-99: ⚠️ Potential issue | 🟡 Minor

Update the MCP tool docstring to reflect function-tool construction.

The comment still says MCP tools are represented as type: "mcp", but the implementation now returns ResponseTool::Function. If this is intentional, update the doc to avoid misleading callers; otherwise, restore the MCP variant in the builder.

📝 Suggested doc update
-/// Build Responses API MCP tools from MCP tool entries.
-///
-/// These tools are exposed in Responses requests where MCP tools are represented
-/// as `{"type": "mcp", ...}` tool entries.
+/// Build Responses API function tools from MCP tool entries.
+///
+/// These tools are exposed in Responses requests as `{"type": "function", ...}` tool entries.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mcp/src/responses_bridge.rs` around lines 76 - 99, The docstring for
build_response_tools / build_response_tools_with_names is stale: the code
constructs ResponseTool::Function (via FunctionTool / Function and using
resolved_name_for_entry, entry.tool.input_schema, etc.) but the comment still
says MCP tools are represented as `{"type": "mcp", ...}`. Fix by updating the
top-of-function comments to state that MCP tool entries are exposed as Responses
Function tools (ResponseTool::Function) with name, description, and parameters
derived from resolved_name_for_entry, entry.tool.description, and
entry.tool.input_schema; alternatively, if the original MCP variant behavior is
required, change the mapper to construct the MCP ResponseTool variant instead of
ResponseTool::Function. Ensure references to build_response_tools and
build_response_tools_with_names remain accurate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs`:
- Around line 170-176: The partition logic using
session.has_exposed_tool(&tc.function.name) can misclassify user function tools
that share names with MCP-exposed tools; add a preflight check before the
partition to detect any name collisions between the MCP-exposed tool names (from
session.exposed tools, used by session.has_exposed_tool) and the incoming
tool_calls' tc.function.name values, and if any overlaps are found reject the
request with a clear error (or apply an explicit MCP namespace rule) instead of
proceeding to let partition(...) separate them; update the code around
tool_calls, the partition call, and session.has_exposed_tool usage to enforce
this guard.

In `@model_gateway/src/routers/openai/responses/utils.rs`:
- Around line 211-233: The match arm in response_tool_to_value currently only
returns a Value for ResponseTool::Mcp when mcp.server_url.is_some(), which drops
static MCP tools that only have server_label; change the logic in
response_tool_to_value so ResponseTool::Mcp always builds and returns the object
(including type "mcp", server_label, and optional fields server_url,
server_description, require_approval, allowed_tools) regardless of whether
mcp.server_url is Some or None; keep using insert_optional_value for optional
fields and the same allowed_tools handling so static MCP entries are preserved.

---

Outside diff comments:
In `@mcp/src/responses_bridge.rs`:
- Around line 76-99: The docstring for build_response_tools /
build_response_tools_with_names is stale: the code constructs
ResponseTool::Function (via FunctionTool / Function and using
resolved_name_for_entry, entry.tool.input_schema, etc.) but the comment still
says MCP tools are represented as `{"type": "mcp", ...}`. Fix by updating the
top-of-function comments to state that MCP tool entries are exposed as Responses
Function tools (ResponseTool::Function) with name, description, and parameters
derived from resolved_name_for_entry, entry.tool.description, and
entry.tool.input_schema; alternatively, if the original MCP variant behavior is
required, change the mapper to construct the MCP ResponseTool variant instead of
ResponseTool::Function. Ensure references to build_response_tools and
build_response_tools_with_names remain accurate.

In `@model_gateway/src/routers/grpc/harmony/responses/streaming.rs`:
- Around line 244-268: The check for exceeding max_tool_calls only adds MCP
calls; include function tool calls as well: compute total_calls_after using
mcp_tracking.total_calls() + mcp_tool_calls.len() + function_tool_calls.len()
(or otherwise sum mcp_tool_calls and function_tool_calls) and compare that sum
to effective_limit; update the warn() fields (new_calls/total_after) to reflect
the combined new calls and resultant total, and ensure any emitted
incomplete_details path uses this combined count logic (refer to mcp_tool_calls,
function_tool_calls, total_calls_after, effective_limit,
mcp_tracking.total_calls(), and max_tool_calls).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 324f4d3 and 5843ebb.

📒 Files selected for processing (15)
  • mcp/src/responses_bridge.rs
  • model_gateway/src/routers/grpc/common/responses/utils.rs
  • model_gateway/src/routers/grpc/harmony/builder.rs
  • model_gateway/src/routers/grpc/harmony/responses/common.rs
  • model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs
  • model_gateway/src/routers/grpc/harmony/responses/streaming.rs
  • model_gateway/src/routers/grpc/harmony/stages/preparation.rs
  • model_gateway/src/routers/grpc/harmony/streaming.rs
  • model_gateway/src/routers/grpc/regular/responses/conversions.rs
  • model_gateway/src/routers/mcp_utils.rs
  • model_gateway/src/routers/openai/responses/streaming.rs
  • model_gateway/src/routers/openai/responses/utils.rs
  • model_gateway/tests/api/responses_api_test.rs
  • model_gateway/tests/spec/responses.rs
  • protocols/src/responses.rs

Comment thread model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs
Comment thread model_gateway/src/routers/openai/responses/utils.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
model_gateway/src/routers/openai/responses/utils.rs (1)

211-230: ⚠️ Potential issue | 🟠 Major

Restore MCP tools even without server_url.

The guard on mcp.server_url.is_some() still drops MCP tools that only provide server_label, which breaks mirroring of original request tools.

🔧 Suggested fix
-        ResponseTool::Mcp(mcp) if mcp.server_url.is_some() => {
+        ResponseTool::Mcp(mcp) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/openai/responses/utils.rs` around lines 211 - 230,
The match arm currently only handles ResponseTool::Mcp when
mcp.server_url.is_some(), which drops MCP tools that only have server_label;
change the pattern to match all ResponseTool::Mcp(mcp) (remove the
server_url.is_some() guard) so the block always builds an object and uses
insert_optional_value(&mut m, "server_url", mcp.server_url.as_ref()) to include
server_url only when present; keep the existing inserts for server_label,
server_description, require_approval, and allowed_tools as-is.
model_gateway/src/routers/grpc/harmony/responses/streaming.rs (1)

244-248: MCP/function tool name collision risk (same as non-streaming path).

Same session.has_exposed_tool partition logic as non_streaming.rs Line 175.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/grpc/harmony/responses/streaming.rs` around lines
244 - 248, The partitioning currently uses
session.has_exposed_tool(&tc.function.name) which can collide on duplicate
names; update the partition logic in the streaming path (where tool_calls is
split into mcp_tool_calls and function_tool_calls) to use a unique identifier or
provenance check instead of raw name—e.g., use tc.function.id or a
fully-qualified name/namespace field (or compare tc.function.origin/mapping)
when calling session.has_exposed_tool (or add a new session method that accepts
the function id) so streaming and non-streaming paths disambiguate tools by
identity rather than name.
model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs (1)

170-175: MCP/function tool name collision risk persists with session-based partitioning.

The partition at Line 175 uses session.has_exposed_tool(&tc.function.name) as the sole discriminator. If a user-defined function tool happens to share a name with an MCP-exposed tool, it will be misclassified as MCP and executed through the MCP path. This is a correctness/security concern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs` around
lines 170 - 175, The current partition uses only
session.has_exposed_tool(&tc.function.name) which misclassifies user tools that
share names with MCP tools; change the partition predicate in the tool_calls ->
partition(...) logic to require both that the name is exposed AND that the tool
call actually originates from the MCP (e.g., check an explicit origin/type field
on the ToolCall/Function struct such as tc.function.origin or tc.metadata
indicating MCP), falling back to comparing a unique tool id/namespace rather
than name; update references around mcp_tool_calls and function_tool_calls
accordingly and add tests for a same-name user tool vs MCP tool collision.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mcp/src/responses_bridge.rs`:
- Around line 91-99: The docstring for build_response_tools_with_names is
incorrect: the function converts MCP tool entries into Responses API
function-type tools (constructed as ResponseTool::Function with FunctionTool and
Function) rather than producing {"type": "mcp", ...} entries; update the
docstring to state that MCP tool entries are transformed into function tools
(serialize as {"type": "function", ...}), mentioning the use of
ResponseTool::Function, FunctionTool and resolved_name_for_entry to make the
behavior clear.

In `@model_gateway/src/routers/openai/responses/utils.rs`:
- Around line 231-232: ResponseTool::WebSearchPreview and
ResponseTool::CodeInterpreter currently call serde_json::to_value(tool) which
produces adjacent-tagged JSON; change both branches to manually construct an
object with an explicit "type" field (e.g. "web_search_preview" and
"code_interpreter") and then merge the tool's inner serialized fields into that
object so the output shape matches the MCP branch. Locate the
ResponseTool::WebSearchPreview and ResponseTool::CodeInterpreter arms and
replace the serde_json::to_value(tool).ok() usage with code that creates a
serde_json::Map, inserts ("type",
serde_json::Value::String("web_search_preview"/"code_interpreter")), serializes
the inner data (via serde_json::to_value on the inner struct) and extends the
map with those key/value pairs, then returns
serde_json::Value::Object(map).ok().

---

Duplicate comments:
In `@model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs`:
- Around line 170-175: The current partition uses only
session.has_exposed_tool(&tc.function.name) which misclassifies user tools that
share names with MCP tools; change the partition predicate in the tool_calls ->
partition(...) logic to require both that the name is exposed AND that the tool
call actually originates from the MCP (e.g., check an explicit origin/type field
on the ToolCall/Function struct such as tc.function.origin or tc.metadata
indicating MCP), falling back to comparing a unique tool id/namespace rather
than name; update references around mcp_tool_calls and function_tool_calls
accordingly and add tests for a same-name user tool vs MCP tool collision.

In `@model_gateway/src/routers/grpc/harmony/responses/streaming.rs`:
- Around line 244-248: The partitioning currently uses
session.has_exposed_tool(&tc.function.name) which can collide on duplicate
names; update the partition logic in the streaming path (where tool_calls is
split into mcp_tool_calls and function_tool_calls) to use a unique identifier or
provenance check instead of raw name—e.g., use tc.function.id or a
fully-qualified name/namespace field (or compare tc.function.origin/mapping)
when calling session.has_exposed_tool (or add a new session method that accepts
the function id) so streaming and non-streaming paths disambiguate tools by
identity rather than name.

In `@model_gateway/src/routers/openai/responses/utils.rs`:
- Around line 211-230: The match arm currently only handles ResponseTool::Mcp
when mcp.server_url.is_some(), which drops MCP tools that only have
server_label; change the pattern to match all ResponseTool::Mcp(mcp) (remove the
server_url.is_some() guard) so the block always builds an object and uses
insert_optional_value(&mut m, "server_url", mcp.server_url.as_ref()) to include
server_url only when present; keep the existing inserts for server_label,
server_description, require_approval, and allowed_tools as-is.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 324f4d3 and 5843ebb.

📒 Files selected for processing (15)
  • mcp/src/responses_bridge.rs
  • model_gateway/src/routers/grpc/common/responses/utils.rs
  • model_gateway/src/routers/grpc/harmony/builder.rs
  • model_gateway/src/routers/grpc/harmony/responses/common.rs
  • model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs
  • model_gateway/src/routers/grpc/harmony/responses/streaming.rs
  • model_gateway/src/routers/grpc/harmony/stages/preparation.rs
  • model_gateway/src/routers/grpc/harmony/streaming.rs
  • model_gateway/src/routers/grpc/regular/responses/conversions.rs
  • model_gateway/src/routers/mcp_utils.rs
  • model_gateway/src/routers/openai/responses/streaming.rs
  • model_gateway/src/routers/openai/responses/utils.rs
  • model_gateway/tests/api/responses_api_test.rs
  • model_gateway/tests/spec/responses.rs
  • protocols/src/responses.rs

Comment thread mcp/src/responses_bridge.rs
Comment thread model_gateway/src/routers/openai/responses/utils.rs
…ses spec and tighten MCP validation

Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
…ses spec and tighten MCP validation

Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
@zhaowenzi zhaowenzi changed the title refactor(protocol): model ResponseTool as tagged enum to match Respon… refactor(protocol): model ResponseTool as tagged enum to match Responses spec and tighten MCP validation Feb 25, 2026
@zhaowenzi
zhaowenzi marked this pull request as ready for review February 25, 2026 03:01
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@model_gateway/src/routers/grpc/harmony/streaming.rs`:
- Around line 734-741: The code does two sequential lookups via
McpToolSession::has_exposed_tool and then tool_response_format; collapse this to
a single lookup to avoid the redundant map access by adding a new method like
McpToolSession::try_tool_response_format(name: &str) -> Option<ResponseFormat>
(or inline the single lookup using exposed_name_map.get(name).map(|entry|
entry.tool_response_format().unwrap_or(ResponseFormat::Passthrough))). Update
the call site in streaming.rs to use try_tool_response_format (or the
single-lookup inline) instead of has_exposed_tool + tool_response_format to
eliminate the duplicate lookup while preserving the same fallback behavior.

In `@protocols/src/responses.rs`:
- Around line 46-50: The struct FunctionTool currently uses
#[serde(deny_unknown_fields)] together with #[serde(flatten)] on its function
field (FunctionTool and Function), which Serde doesn't support; fix by either
removing #[serde(deny_unknown_fields)] from FunctionTool (so flatten remains) or
refactor to stop flattening: make FunctionTool contain an explicit named field
pub function: Function (remove #[serde(flatten)]), and then keep
#[serde(deny_unknown_fields)] on the inner Function type to enforce
unknown-field rejection for function tools; update the FunctionTool declaration
accordingly and ensure serde attributes are only applied in a supported
combination.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 324f4d3 and 8c84c40.

📒 Files selected for processing (15)
  • mcp/src/responses_bridge.rs
  • model_gateway/src/routers/grpc/common/responses/utils.rs
  • model_gateway/src/routers/grpc/harmony/builder.rs
  • model_gateway/src/routers/grpc/harmony/responses/common.rs
  • model_gateway/src/routers/grpc/harmony/responses/non_streaming.rs
  • model_gateway/src/routers/grpc/harmony/responses/streaming.rs
  • model_gateway/src/routers/grpc/harmony/stages/preparation.rs
  • model_gateway/src/routers/grpc/harmony/streaming.rs
  • model_gateway/src/routers/grpc/regular/responses/conversions.rs
  • model_gateway/src/routers/mcp_utils.rs
  • model_gateway/src/routers/openai/responses/streaming.rs
  • model_gateway/src/routers/openai/responses/utils.rs
  • model_gateway/tests/api/responses_api_test.rs
  • model_gateway/tests/spec/responses.rs
  • protocols/src/responses.rs

Comment thread model_gateway/src/routers/grpc/harmony/streaming.rs
Comment thread protocols/src/responses.rs
@slin1237
slin1237 merged commit 1f06504 into main Feb 25, 2026
24 checks passed
@slin1237
slin1237 deleted the refactor/responses-tool-tagged-enum branch February 25, 2026 03:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes mcp MCP related changes model-gateway Model gateway crate changes openai OpenAI router changes protocols Protocols crate changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants