Repository navigation
Conversation
This branch has not been deployed
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.
Please describe the changes in your PR. If it is addressing an issue, please reference that as well.
Fixes #6111
Summary
MCPClient.tools_output_filtersis documented asdict[str, Callable[[Any], Any]], with each filter returning "the processed output (any type)" (src/pipecat/services/mcp_service.py:216,:225-226). In practice, a filter that returned a non-string value (e.g. a parseddict) or the empty string had its output discarded, and the model receivedSorry, could not call the mcp tooleven thoughsession.call_tool()had succeeded.Why
After the filter is applied, the result was gated by
if isinstance(response, str) and response:. Any non-string value and the empty string fell into theelse, which replaces the response witherror_msg or "Sorry, could not call the mcp tool"— anderror_msgisNonefor a successful call. That check was aimed at filter failures (the adjacent except-handler setsresponse = ""when a filter raises), but it also caught every successful call whose legitimate output was empty or non-string, making a successful tool call indistinguishable from a failed one for the model. The framework already supports non-string tool results end-to-end: the result callback takesresult: Any(src/pipecat/services/llm_service.py:105-106) and the LLM context aggregator serializes non-string results withjson.dumps(src/pipecat/processors/aggregators/llm_response_universal.py:2119).Changes
src/pipecat/services/mcp_service.py: track whether a filter ran without raising (filter_applied). On a successful call (error_msg is None), a filter that ran without error owns the response and its output is delivered as-is, whatever the type. A failed call, or a filter that raises, still reportserror_msg or "Sorry, could not call the mcp tool". Behavior for calls without a filter and for failed calls is unchanged.tests/test_mcp_service.py: tests for a non-string filter output (adict), an empty-string filter output (fully redacted), and a filter that raises still reading as the stock failure line. The first two fail onmainwithout the fix.changelog/6111.fixed.md: changelog fragment.Testing
On the unpatched tree, the two new regression tests fail:
With the fix:
uv run pytest tests/test_mcp_service.py—57 passed, 5 subtests passeduv run pytest tests/test_keenable_web_search.py—11 passed(the only other test file exercisingMCPClient)uv run pytest—7187 passed, 64 failed, 63 skipped; the 64 failures are in 5 network-dependent files unrelated to MCP and reproduce identically (same files, same count) on a pristineorigin/maincheckoutuv run ruff check—All checks passed!uv run ruff format --check—1596 files already formatteduv run towncrier build --draft --version Unreleased— the new fragment renders under Fixed