Skip to content

feat: warn on MCP tool response content-type drift between calls#3364

Draft
julian-risch wants to merge 1 commit into
mainfrom
feat/mcp-toolset-validate-response-shape
Draft

feat: warn on MCP tool response content-type drift between calls#3364
julian-risch wants to merge 1 commit into
mainfrom
feat/mcp-toolset-validate-response-shape

Conversation

@julian-risch
Copy link
Copy Markdown
Member

@julian-risch julian-risch commented May 28, 2026

Related Issues

Fixes https://github.com/deepset-ai/haystack-private/issues/362

Proposed Changes:

  • Add _check_response_shape in mcp_toolset.py that records the set of content block types each MCP tool returns on its first invocation and emits a warning whenever a later call introduces a previously unseen type.
  • Wire the per-toolset baseline (MCPToolset._response_shapes) through create_invoke_tool so every tool produced by an MCPToolset participates in the check.
  • Move the JSON parse to the top of invoke_tool and share it with the existing outputs_to_state extraction path; on parse failure we still return the raw string so the no-outputs_to_state contract is preserved.
  • Add a RugPull fixture server that returns TextContent on the first call and a ResourceLink on the second, plus unit tests for the helper directly and one integration-style test against the rug-pull server.

This is intentionally a detection-only signal — it does not block the response — because the MCP protocol allows servers to legitimately vary their content types between calls, and an attacker who keeps the content type stable will not trip this check. The aim is to give pipeline owners a clear, loggable signal when a tool's response shape shifts under them.

How did you test it?

  • New tests:
    • TestCheckResponseShape::test_first_call_establishes_baseline
    • TestCheckResponseShape::test_drift_emits_warning_and_extends_baseline
    • TestCheckResponseShape::test_same_shape_does_not_warn
    • TestCheckResponseShape::test_non_dict_parsed_is_ignored
    • TestCheckResponseShape::test_missing_or_malformed_content_field_is_ignored
    • TestMCPToolset::test_response_shape_drift_logs_warning

Notes for the reviewer

Checklist

Track the set of content block types each MCP tool returns and emit a
warning when a subsequent invocation introduces a previously unseen type.
This surfaces a class of server-side rug-pull where a benign tool
silently substitutes different content (e.g. a ResourceLink with a
sensitive URI) on later calls. Detection only — does not block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added integration:mcp type:documentation Improvements or additions to documentation labels May 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Coverage report (mcp)

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  integrations/mcp/src/haystack_integrations/tools/mcp
  mcp_toolset.py 396-397
Project Total  

This report was generated by python-coverage-comment-action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration:mcp type:documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant