fix(community-v3): inject tool context without deep-copying the tool#38
Merged
Conversation
When enriching a tool's input schema with the AgentCat `context` parameter, the FastMCP v3 middleware deep-copied the entire tool object on every `tools/list` request. FastMCP v3 tools generated from an OpenAPI specification hold a reference to their `httpx.AsyncClient`, which contains a `threading.RLock`. Deep-copying that object raised `TypeError: cannot pickle '_thread.RLock' object`, and the resulting fallback returned the tool unmodified. As a consequence, context injection was silently skipped for every affected tool and the failure was logged on each request, producing a high volume of diagnostic errors. Only the `parameters` schema (a plain dictionary) is modified during injection, so this change copies just that schema and reuses the rest of the tool by reference via `Tool.model_copy`. This avoids traversing non-copyable fields entirely, restores context injection for OpenAPI-generated tools, and leaves the server's original tool objects unmutated. The remaining copy-failure log line now records the resolved FastMCP version to aid future diagnosis. Adds regression coverage that reproduces the RLock failure with an OpenAPI-generated tool and verifies injection, non-mutation, and the version-tagged log message. Verified against FastMCP 3.0.0b1 and 3.4.4.
…absent The regression tests exercise the FastMCP v3 OpenAPI middleware path and require FastMCP v3+. Guard the module so it is skipped rather than failing collection in the no-FastMCP CI job and on the FastMCP v2 compatibility matrix: defer the fastmcp/httpx imports behind a try/except and gate the module with a skipif on the resolved FastMCP major version.
kashishhora
approved these changes
Jul 9, 2026
5 tasks
naji247
added a commit
that referenced
this pull request
Jul 10, 2026
The community test suite previously exercised only plain function tools, which deep-copy cleanly. That gap let the context-injection regression fixed in #38 ship green: FastMCP v3 tools that hold live runtime state (an httpx client on OpenAPI-generated tools, a client factory on proxy tools, a sub-server reference on mounted tools) failed to deep-copy and silently lost context injection. Add end-to-end coverage that drives the real middleware dispatch against those tool types, entirely in-process via httpx.MockTransport (no network): - tests/test_utils/community_openapi_server.py: factories for OpenAPI, proxy, and mounted FastMCP v3 servers, plus a multi-endpoint OpenAPI spec. - tests/community/test_community_v3_openapi.py: tools/list injects context on every tool, no copy failure is logged, cached tools are not mutated, tools/call strips context and captures it as intent without leaking it downstream, HTTP errors are captured, the tools/list event serializes OpenAPI tools, and get_more_tools coexists with generated tools. - tests/community/test_community_v3_runtime_state_tools.py: parametrized guard over openapi/proxy/mounted asserting context injection with no copy failures. All modules are FastMCP v3-only and skip cleanly when FastMCP is absent or on the v2 compatibility matrix. Verified on FastMCP 3.0.0b1 and 3.4.4.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_inject_context_into_toolsdeep-copied the entire tool object on everytools/listrequest in order to add the AgentCatcontextparameter to its input schema. FastMCP v3 tools generated from an OpenAPI specification hold a reference to theirhttpx.AsyncClient, which contains athreading.RLock. Deep-copying that object raisesTypeError: cannot pickle '_thread.RLock' object. The failure was caught and the tool returned unmodified, so context injection was silently skipped for every affected tool while the error was logged on each request — generating a high volume of SDK diagnostic errors.parametersschema (a plain dictionary) is mutated during injection, so we now copy just that schema and reuse the rest of the tool by reference viaTool.model_copy. This avoids traversing non-copyable fields, restores context injection for OpenAPI-generated tools, and leaves the server's original tool objects unmutated.1.0.0->1.0.1.Impact
Customers running OpenAPI-generated FastMCP v3 servers on
agentcat==1.0.0were not receiving tool-call context/intent capture, and their processes emitted a copy-failure log per tool on everytools/list. This restores the intended behavior and removes the error volume.Test plan
uv run pytest tests/community/test_community_v3_inject_context.py -v- new regression tests pass:contextis injected into the tool's parameters and marked requireduv run pytest tests/community/ tests/test_context_parameters.py -q- full community suite passes (no regressions)