Skip to content

RSPEED-2885: sanitize model and MCP output in all response paths#1563

Open
Lifto wants to merge 3 commits intolightspeed-core:mainfrom
Lifto:fix/rspeed-2885-sanitize-output-and-model
Open

RSPEED-2885: sanitize model and MCP output in all response paths#1563
Lifto wants to merge 3 commits intolightspeed-core:mainfrom
Lifto:fix/rspeed-2885-sanitize-output-and-model

Conversation

@Lifto
Copy link
Copy Markdown
Contributor

@Lifto Lifto commented Apr 21, 2026

RSPEED-2885: sanitize model and MCP output in all response paths

QE found that the non-streaming /v1/responses path still leaks server internals:

  • model: exposes full provider routing path (google-vertex/publishers/google/models/gemini-2.5-flash)
  • output: includes mcp_list_tools items with full server-side tool definitions

Root cause: _sanitize_response_dict() only handled instructions and tools. The streaming path had ad-hoc output filtering at a separate call site that the non-streaming path lacked. Neither path sanitized model.

Fix:

  • Move output array filtering into _sanitize_response_dict() — both paths now strip mcp_list_tools, mcp_call, mcp_approval_request items from server-deployed MCP servers
  • Add model sanitization — strips provider prefix, keeps model name (e.g. gemini-2.5-flash)
  • Remove redundant ad-hoc filtering from the streaming generator

Tested:

  • Unit tests: 63 pass (15 for _sanitize_response_dict including new coverage for output and model)
  • Integration tests: new tests for both streaming and non-streaming paths verifying sanitization end-to-end
  • Local stack: verified with curl against both streaming and non-streaming endpoints — model, instructions, and output all sanitized correctly

Summary by CodeRabbit

  • Bug Fixes

    • Removed server-deployed MCP output items from responses while preserving user-relevant MCP items.
    • Cleaned up model names by stripping provider routing prefixes for clearer model values.
  • Tests

    • Expanded unit and integration tests for response filtering, instruction substitution, and model-name handling in streaming and non-streaming flows.
    • Updated end-to-end scenarios and test helpers to use a short-model placeholder in expectations.

Lifto and others added 2 commits April 21, 2026 10:50
Red tests for _sanitize_response_dict to cover:
- mcp_list_tools/mcp_call items not filtered from output array
- model field not stripping provider prefix (google-vertex/...)

These tests document the expected behavior before the fix.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Move output array filtering into _sanitize_response_dict() so both
streaming and non-streaming paths strip server-deployed MCP items
(mcp_list_tools, mcp_call, mcp_approval_request). Strip provider
routing prefix from model field (e.g. google-vertex/.../gemini-2.5-flash
becomes gemini-2.5-flash).

Removes redundant ad-hoc output filtering from the streaming generator
that was missing from the non-streaming path, causing the leak QE found.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Walkthrough

Response sanitization now removes server-deployed MCP output items of specific types and strips provider routing prefixes from model values; duplicate streaming-path filtering was removed. Tests and e2e fixtures were updated to expect the shortened model name (MODEL_SHORT) and to cover streaming and non-streaming sanitization behavior.

Changes

Cohort / File(s) Summary
Response Sanitization Logic
src/app/endpoints/responses.py
Extended _sanitize_response_dict() to: 1) remove server-deployed MCP output items with types mcp_list_tools, mcp_call, or mcp_approval_request when server_label matches configured_mcp_labels; 2) strip provider routing prefixes from response["model"] (remove substring before last /). Removed duplicate output-filtering from the streaming response_generator() path.
Unit tests (response sanitization & streaming)
tests/unit/app/endpoints/test_responses.py
Added json import and updated moderation-refusal fixture shape. Expanded tests to cover: filtering server MCP items from output, handling missing output, stripping provider prefixes from model, combined sanitization, and integration-style non-streaming and streaming assertions (parsing SSE lines).
End-to-end test fixtures & helpers
tests/e2e/features/responses.feature, tests/e2e/features/responses_streaming.feature, tests/e2e/features/environment.py, tests/e2e/utils/utils.py
Updated e2e expectations to use {MODEL_SHORT} for returned model. Added context.default_model_short in environment setup (last path segment of default model). replace_placeholders now substitutes {MODEL_SHORT} using context.default_model_short.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and specifically describes the main change: sanitizing model field and MCP output across all response paths, which directly aligns with the core fixes implemented throughout the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

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.

Copy link
Copy Markdown
Contributor

@tisnik tisnik left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Copy Markdown
Contributor

@radofuchs radofuchs left a comment

Choose a reason for hiding this comment

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

LGTM, just one update in tests needed

Comment thread src/app/endpoints/responses.py
E2E feature files now use {MODEL_SHORT} placeholder for response body
assertions since the model field is stripped of provider prefix.
Request bodies still send {PROVIDER}/{MODEL} unchanged.

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@Lifto Lifto changed the title Fix/rspeed 2885 sanitize output and model RSPEED-2885: sanitize model and MCP output in all response paths Apr 21, 2026
@tisnik tisnik requested a review from radofuchs April 21, 2026 20:05
Copy link
Copy Markdown
Contributor

@asimurka asimurka left a comment

Choose a reason for hiding this comment

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

Why the provider stripping from model cannot be done on client side? This introduces some implicit behavior that violates the OpenResponses standard.

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.

4 participants