Skip to content

add client kwargs to proxy clients and meta to proxy tool calls#2520

Merged
jlowin merged 8 commits intoPrefectHQ:mainfrom
cegersdoerfer:client_proxy_meta
Dec 6, 2025
Merged

add client kwargs to proxy clients and meta to proxy tool calls#2520
jlowin merged 8 commits intoPrefectHQ:mainfrom
cegersdoerfer:client_proxy_meta

Conversation

@cegersdoerfer
Copy link
Copy Markdown
Contributor

Description

This PR solves 2 issues:

  1. Passing tool call metadata to servers initialized via the MCPConfig transport. The fix checks if there is any metadata in the context before the tool call is made and then injecting it into the proxy tool call.
  2. Passing client handlers and callbacks to proxy clients created when using the MCPConfig transport. Since ProxyClient accept the same arguments as regular ones, the fix passes the original client arguments to the ProxyClient instances when they are created. Admittedly my changes to solve this issue added more code than I hoped so I am hoping for some feedback to find a better solution

Contributors Checklist

Review Checklist

  • I have self-reviewed my changes
  • My Pull Request is ready for review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 2, 2025

Walkthrough

ProxyTool.run now obtains the request context, extracts metadata from context.request_context.meta when present, and passes that metadata as a meta keyword argument to self._client.call_tool_mcp. Error handling and the returned ToolResult remain unchanged. Separately, a purely formatting change was made in mcp_config.py where _to_server_and_underlying_transport is called with server_name=server_name, client_name=client_name on a single line; this does not alter behavior.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main changes: adding client kwargs to proxy clients and metadata to proxy tool calls.
Description check ✅ Passed The description comprehensively explains both issues being solved and provides context, though testing and documentation updates were not completed.
Linked Issues check ✅ Passed Changes directly address both objectives from #2519: metadata is forwarded to proxy tool calls via get_context() and meta extraction, and client kwargs are passed to proxy clients during MCPConfig transport setup.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the two objectives from #2519; the minor formatting change in mcp_config.py is incidental and does not introduce out-of-scope functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b845ef0 and 21fa3df.

📒 Files selected for processing (1)
  • src/fastmcp/server/proxy.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/fastmcp/server/proxy.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run tests: Python 3.10 on windows-latest
  • GitHub Check: Run tests with lowest-direct dependencies
  • GitHub Check: Run tests: Python 3.10 on ubuntu-latest

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.

@marvin-context-protocol marvin-context-protocol Bot added bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. client Related to the FastMCP client SDK or client-side functionality. labels Dec 2, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/fastmcp/server/proxy.py (2)

285-290: Simplify by removing unused context parameter.

The context parameter is immediately overwritten by get_context() on Line 290, making the parameter misleading. Since get_context() always retrieves the active context, the parameter serves no purpose.

Apply this diff to remove the unused parameter:

     async def run(
         self,
         arguments: dict[str, Any],
-        context: Context | None = None,
     ) -> ToolResult:
-        from fastmcp.utilities.types import find_kwarg_by_type
         """Executes the tool by making a call through the client."""
         async with self._client:
             context = get_context()

292-292: Simplify meta extraction with getattr.

The chained hasattr checks are verbose. Use getattr with a default value for cleaner code.

Apply this diff to simplify:

-            meta = dict(context.request_context.meta) if hasattr(context, 'request_context') and hasattr(context.request_context, 'meta') else None
+            request_context = getattr(context, 'request_context', None)
+            meta = dict(request_context.meta) if request_context and hasattr(request_context, 'meta') else None
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54692c3 and 11f0eb2.

📒 Files selected for processing (5)
  • src/fastmcp/client/client.py (1 hunks)
  • src/fastmcp/client/transports.py (5 hunks)
  • src/fastmcp/mcp_config.py (2 hunks)
  • src/fastmcp/server/proxy.py (1 hunks)
  • src/fastmcp/utilities/mcp_config.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.py: Python source code must be version ≥3.10 with full type annotations
Prioritize readable, understandable code - clarity over cleverness; avoid obfuscated or confusing patterns even if they're shorter
Never use bare except in code - be specific with exception types

Files:

  • src/fastmcp/utilities/mcp_config.py
  • src/fastmcp/client/client.py
  • src/fastmcp/server/proxy.py
  • src/fastmcp/client/transports.py
  • src/fastmcp/mcp_config.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T01:29:57.585Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients for testing using in-memory transport instead of HTTP transport, unless explicitly testing network features
📚 Learning: 2025-12-02T01:29:57.585Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T01:29:57.585Z
Learning: Applies to tests/**/*.py : Pass FastMCP servers directly to clients for testing using in-memory transport instead of HTTP transport, unless explicitly testing network features

Applied to files:

  • src/fastmcp/utilities/mcp_config.py
  • src/fastmcp/client/transports.py
  • src/fastmcp/mcp_config.py
🧬 Code graph analysis (5)
src/fastmcp/utilities/mcp_config.py (3)
src/fastmcp/client/transports.py (1)
  • ClientTransport (75-117)
src/fastmcp/mcp_config.py (3)
  • to_transport (120-124)
  • to_transport (157-165)
  • to_transport (208-230)
src/fastmcp/server/proxy.py (1)
  • ProxyClient (520-618)
src/fastmcp/client/client.py (1)
src/fastmcp/client/transports.py (10)
  • infer_transport (989-989)
  • infer_transport (993-993)
  • infer_transport (997-997)
  • infer_transport (1001-1001)
  • infer_transport (1004-1004)
  • infer_transport (1008-1008)
  • infer_transport (1012-1014)
  • infer_transport (1018-1022)
  • infer_transport (1026-1026)
  • infer_transport (1029-1125)
src/fastmcp/server/proxy.py (4)
src/fastmcp/server/context.py (2)
  • fastmcp (152-157)
  • request_context (181-207)
src/fastmcp/utilities/types.py (1)
  • find_kwarg_by_type (152-175)
src/fastmcp/server/dependencies.py (1)
  • get_context (34-40)
src/fastmcp/client/client.py (1)
  • call_tool_mcp (889-932)
src/fastmcp/client/transports.py (1)
src/fastmcp/mcp_config.py (1)
  • MCPConfig (244-295)
src/fastmcp/mcp_config.py (2)
src/fastmcp/client/client.py (1)
  • Client (107-1018)
src/fastmcp/client/transports.py (1)
  • ClientTransport (75-117)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (7)
src/fastmcp/client/client.py (1)

242-254: LGTM!

The client configuration parameters are properly collected and forwarded to infer_transport. This enables downstream transport and proxy creation to receive client initialization parameters, which aligns with the PR objectives.

src/fastmcp/mcp_config.py (1)

92-108: LGTM!

The addition of **client_kwargs to the signature and forwarding to the Client constructor enables client configuration to propagate through the transforming MCP server pathway, which aligns with the PR objectives.

src/fastmcp/client/transports.py (3)

940-964: LGTM!

The addition of **client_kwargs to MCPConfigTransport.__init__ and forwarding to mcp_config_to_servers_and_transports enables client configuration to propagate through the MCPConfig transport pathway.


1003-1004: LGTM!

The overload for MCPConfig with **client_kwargs provides proper type hints for the extended signature, ensuring type safety for callers.


1029-1038: LGTM!

The addition of **client_kwargs to the infer_transport signature enables client configuration to be forwarded when constructing transports.

src/fastmcp/utilities/mcp_config.py (2)

17-25: LGTM!

The addition of **client_kwargs to mcp_config_to_servers_and_transports and forwarding to mcp_server_type_to_servers_and_transports enables client configuration to propagate through the MCP config pathway.


55-55: LGTM!

The forwarding of **client_kwargs to ProxyClient enables client configuration (handlers, callbacks) to be properly initialized in non-transforming server proxies, which aligns with the PR objectives.

Comment thread src/fastmcp/client/transports.py Outdated
Comment thread src/fastmcp/server/proxy.py Outdated
Comment thread src/fastmcp/utilities/mcp_config.py
Comment thread src/fastmcp/utilities/mcp_config.py Outdated
Comment thread src/fastmcp/utilities/mcp_config.py Outdated
cegersdoerfer and others added 3 commits December 1, 2025 23:17
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: The tests are failing due to a syntax error in src/fastmcp/client/transports.py - an unclosed parenthesis caused by a duplicated line.

Root Cause: In the changes to src/fastmcp/client/transports.py, line 1114 has been accidentally duplicated with incomplete syntax:

# Line 1114 - Incorrect (duplicated with leading spaces)
         inferred_transport = MCPConfigTransport(
inferred_transport = MCPConfigTransport(
    config=cast(dict | MCPConfig, transport),
    **client_kwargs,
)

This causes a SyntaxError: '(' was never closed which prevents the entire module from being imported, causing all tests to fail immediately.

Suggested Solution:

  1. Fix the duplicate line in src/fastmcp/client/transports.py (around line 1114):

    • Remove the first duplicated line with leading spaces
    • Keep only the properly indented version
  2. Fix the duplicate docstring in src/fastmcp/server/proxy.py (line 287):

    • Remove one of the two identical docstrings: """Executes the tool by making a call through the client."""
  3. Fix the duplicate line in src/fastmcp/utilities/mcp_config.py (line 52):

    • Remove one of the two identical transport = mcp_server.to_transport() lines
  4. Remove debug print statements in src/fastmcp/utilities/mcp_config.py (lines 58-59):

    • Remove print(f"new proxy server: {server}")
    • Remove print(f"proxy server progress handler: {client._progress_handler}")
  5. Fix trailing comma in src/fastmcp/utilities/mcp_config.py (line 49):

    • The line client_name=client_name should have a comma at the end, not removed

After fixing these issues, run:

uv run prek run --all-files  # Fix any linting/formatting issues
uv run pytest                # Verify all tests pass
Detailed Analysis

Error Log Excerpt

All test jobs failed with the same error during import:

ImportError while loading conftest '/home/runner/work/fastmcp/fastmcp/tests/conftest.py'.
tests/conftest.py:8: in <module>
    from fastmcp.utilities.tests import temporary_settings
src/fastmcp/__init__.py:19: in <module>
    from fastmcp.client import Client
src/fastmcp/client/__init__.py:1: in <module>
    from .client import Client
src/fastmcp/client/client.py:47: in <module>
    from .transports import (
.venv/lib/python3.10/site-packages/beartype/claw/_importlib/_clawimpload.py:454: in source_to_code
    return super().source_to_code(  # type: ignore[call-arg]
E     File "/home/runner/work/fastmcp/fastmcp/src/fastmcp/client/transports.py", line 1114
E       inferred_transport = MCPConfigTransport(
E                                              ^
E   SyntaxError: '(' was never closed

This is a Python syntax error that occurs during module import, preventing any tests from running.

Related Files
  • src/fastmcp/client/transports.py:1114 - Contains the syntax error (duplicated line)
  • src/fastmcp/server/proxy.py:287 - Contains duplicate docstring
  • src/fastmcp/utilities/mcp_config.py:49,52,58-59 - Contains duplicate line, missing comma, and debug print statements

@strawgate
Copy link
Copy Markdown
Collaborator

MCPTransport is supposed to be a friendly "Helper" -- I think if you need more advanced capabilities then you should consider setting up the servers and transports yourself?

@cegersdoerfer
Copy link
Copy Markdown
Contributor Author

@strawgate I would agree for the second problem which the PR fixes (passing client callbacks to proxies) since it requires significantly more changes to the code and may not be needed by most users. But, for the first issue (passing metadata to proxy tool calls), it only adds minimal code contained to a single method in src/fastmcp/server/proxy.py and it seems like something anyone using an MCPConfig and tool call metadata would need and expect as the default behavior. It is also the primary reason I opened the PR at all. I'd have no problem with removing all of the changes pertaining to passing client callbacks to proxies from this PR if you agree

Copy link
Copy Markdown
Member

@jlowin jlowin left a comment

Choose a reason for hiding this comment

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

@cegersdoerfer I agree with @strawgate here -- passing the kwargs through so many layers does work but IMO is a smell that we're overloading the functionality. However the meta-passing is an excellent enhancement that we should include - if you wouldn't mind removing the kwargs from the PR, we can get this merged!

@cegersdoerfer
Copy link
Copy Markdown
Contributor Author

@jlowin done! Let me know if anything else is needed

@cegersdoerfer cegersdoerfer requested a review from jlowin December 6, 2025 19:07
Copy link
Copy Markdown
Member

@jlowin jlowin left a comment

Choose a reason for hiding this comment

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

Thank you!

@jlowin jlowin merged commit 6147c27 into PrefectHQ:main Dec 6, 2025
7 checks passed
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: Two Windows-specific tests are failing due to pytest-xdist worker crashes. These are known flaky tests unrelated to the PR changes.

Root Cause: The test failures are caused by a known Windows-specific flakiness in tests that spawn subprocess servers using PythonStdioTransport. When running under pytest-xdist with parallel execution, Windows workers occasionally crash with "node down: Not properly terminated" errors.

The failing tests:

  • test_keep_alive_false_starts_new_session_across_multiple_calls
  • test_keep_alive_starts_new_session_if_manually_closed

Both tests:

  1. Spawn Python subprocess servers using PythonStdioTransport
  2. Call tools to get the server PID
  3. Close the client/server and check that new PIDs are generated

This pattern of spawning and killing subprocesses under parallel test execution on Windows triggers race conditions in pytest-xdist worker management.

Evidence this is NOT related to the PR:

  • The PR only changes proxy tool call metadata passing (src/fastmcp/server/proxy.py) and formatting (src/fastmcp/utilities/mcp_config.py)
  • No changes to stdio transport, keep-alive logic, or test files
  • Linux tests: ✅ All passed (same code, different OS)
  • Windows tests: Only 2 out of 3014 tests failed (~0.07% failure rate)
  • This is a documented known issue (#2559) with Windows + pytest-xdist worker crashes

Suggested Solution:

This is a known flaky test issue that should be addressed separately:

  1. Short-term: Mark these specific tests as expected to be flaky on Windows:

    @pytest.mark.flaky(retries=3, delay=0.1)
    @pytest.mark.skipif(sys.platform == 'win32' and is_parallel_execution(), 
                       reason="Flaky on Windows with pytest-xdist")
  2. Medium-term: Investigate using pytest-timeout or process management improvements to handle subprocess cleanup more reliably on Windows

  3. Long-term: Consider refactoring these tests to use in-memory transports or other approaches that don't spawn real subprocesses

For this specific PR: The PR is functionally correct and all non-Windows tests pass. The failures are environmental, not code-related.

Detailed Analysis

Test Failure Logs

[gw0] node down: Not properly terminated
FAILED tests/client/test_stdio.py::TestKeepAlive::test_keep_alive_false_starts_new_session_across_multiple_calls
replacing crashed worker gw0

[gw2] node down: Not properly terminated  
FAILED tests/client/test_stdio.py::TestKeepAlive::test_keep_alive_starts_new_session_if_manually_closed
replacing crashed worker gw2

PR Changes (Minimal and Unrelated)

The PR makes only two small changes:

  1. src/fastmcp/server/proxy.py: Extract request context metadata and pass it to proxy tool calls
  2. src/fastmcp/utilities/mcp_config.py: Format adjustment (line continuation)

Neither change touches:

  • Stdio transport implementation
  • Process lifecycle management
  • keep_alive logic
  • Test infrastructure

Known Issue References

Related Files

Test file: tests/client/test_stdio.py:186-218
Failing tests: Lines 186-202 and 204-218
Reason: Tests spawn subprocesses and verify PID changes after close/reopen cycles

No code changes in:

  • src/fastmcp/client/transports.py (stdio transport)
  • tests/client/test_stdio.py (test file)
  • Any process management code

Note: This is an updated analysis for the test failure that occurred after the PR was merged to main (workflow run #19994439585).

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

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. client Related to the FastMCP client SDK or client-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Client Metadata is not passed to MCPConfig Servers

3 participants