Skip to content

fix(mcp): deep-copy cached tools before returning them - #4525

Merged
seratch merged 2 commits into
openai:mainfrom
hsusul:fix/mcp-snapshot-cached-tool-objects
Aug 20, 2026
Merged

seratch merged 2 commits into
openai:mainfrom
hsusul:fix/mcp-snapshot-cached-tool-objects

Conversation

@hsusul

@hsusul hsusul commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This pull request finishes cache isolation for MCP tool listings.

#4424 returned a new list from list_tools() / cached_tools, but the Tool objects and nested input_schema dicts remained shared with the server cache. Mutating a returned tool's schema could clear required fields and bypass _validate_required_parameters on later call_tool() invocations.

Returned tools are now deep-copied so callers cannot corrupt the cache or required-parameter checks. Callable tool_filter functions receive detached copies before invocation, so a filter that mutates nested schemas cannot rewrite _tools_list. No-filter and static-filter results stay isolated by the same return-path snapshot.

Test plan

  • uv run pytest -q tests/mcp/test_caching.py tests/mcp/test_tool_filtering.py
  • make format && make lint && make typecheck
  • Confirmed mutating a returned tool's input_schema["required"] still fails call-time validation
  • Confirmed a dynamic filter that clears required cannot change later listings or call_tool() validation
  • Confirmed cached_tools nested schema mutation does not leak into later snapshots

Issue number

N/A

Checks

  • I've added new tests, if relevant
  • I've run .agents/skills/code-change-verification/scripts/run.sh
  • I've confirmed all verification steps pass, except pre-existing tests/sandbox/test_run_cwd.py::test_python_skill_uses_absolute_root_from_nested_workdir on this host
  • If using Codex, I've run /review before submitting this PR

list_tools and cached_tools only shallow-copied the tools list, so callers
could mutate nested input schemas and bypass required-parameter validation.

@seratch seratch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the contribution. The deep-copy direction is correct, but the ownership boundary is still incomplete. list_tools() passes cached MCPTool objects into a callable tool_filter before _snapshot_tools() runs, so a filter that mutates input_schema["required"] can still corrupt _tools_list and bypass later call-time validation.

Please ensure callable filters receive detached tool objects before invocation, while no-filter/static-filter results and cached_tools remain deeply isolated. Add a regression test where a dynamic filter mutates a nested schema and verify that subsequent listings and call_tool() still use the original required fields. Please also extend the cached_tools snapshot test to mutate a returned tool or nested schema, rather than only the outer list.

With those focused changes and green CI, this should be ready for another review.

Callable tool_filter still received live cache objects, so mutating nested
schemas could bypass call-time validation. Inspect detached copies and keep
returned listings and cached_tools isolated from the cache.
@seratch seratch added this to the 0.22.x milestone Aug 20, 2026
@seratch
seratch enabled auto-merge (squash) August 20, 2026 06:11
@seratch
seratch merged commit eb3a5d5 into openai:main Aug 20, 2026
17 checks passed
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.

2 participants