Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/wayflowcore/source/core/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ WayFlow 26.1.2
New features
^^^^^^^^^^^^

* **Configurable retry policies for MCP tools**

Added ``retry_policy`` to ``MCPTool`` and ``MCPToolBox`` to retry transient
missing-tool responses and MCP tool execution failures, including WayFlow
Agent Spec plugin serialization support.

For usage details, see :doc:`the MCP tools guide <howtoguides/howto_mcp>`.


WayFlow 26.1.2
Comment thread
paul-cayet marked this conversation as resolved.
--------------

New features
^^^^^^^^^^^^

* **Configurable retry policies for remote components**

Added the ``RetryPolicy`` object to configure retries, backoff,
Expand Down
30 changes: 29 additions & 1 deletion docs/wayflowcore/source/core/code_examples/howto_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ def start_mcp_server() -> str:
from wayflowcore.agent import Agent
from wayflowcore.mcp import MCPTool, MCPToolBox, SSETransport, authless_mcp_enabled
from wayflowcore.flow import Flow
from wayflowcore.retrypolicy import RetryPolicy
from wayflowcore.steps import ToolExecutionStep

mcp_server_url = f"http://localhost:8080/sse" # change to your own URL
# We will see below how to connect a specific tool to an assistant, e.g.
MCP_TOOL_NAME = "get_user_session"
MCP_TOOLBOX_TOOL_FILTER = ["get_user_session", "get_payslips"]
# And see how to build an agent that can answer questions, e.g.
USER_QUERY = "What was the payment date of the last payslip for the current user?"
# .. end-##_Imports_for_this_guide
Expand All @@ -101,6 +103,8 @@ def start_mcp_server() -> str:
mcp_server_url: str # docs-skiprow
USER_QUERY: str # docs-skiprow
(llm, mcp_server_url, USER_QUERY, MCP_TOOL_NAME) = _update_globals(["llm_small", "sse_mcp_server", "mcp_user_query", "mcp_example_tool_name"]) # docs-skiprow # type: ignore
if MCP_TOOL_NAME != "get_user_session": # docs-skiprow
MCP_TOOLBOX_TOOL_FILTER = [MCP_TOOL_NAME] # docs-skiprow

# .. start-##_Connecting_an_agent_to_the_MCP_server
mcp_client = SSETransport(url=mcp_server_url)
Expand All @@ -112,6 +116,22 @@ def start_mcp_server() -> str:
tools=[mcp_toolbox]
Comment thread
paul-cayet marked this conversation as resolved.
)
# .. end-##_Connecting_an_agent_to_the_MCP_server
# .. start-##_Configuring_toolbox_retry_policy
mcp_toolbox_with_retries = MCPToolBox(
client_transport=mcp_client,
tool_filter=MCP_TOOLBOX_TOOL_FILTER,
retry_policy=RetryPolicy(
Comment thread
paul-cayet marked this conversation as resolved.
max_attempts=3,
initial_retry_delay=0.25,
max_retry_delay=2.0,
),
)

assistant = Agent(
llm=llm,
tools=[mcp_toolbox_with_retries]
)
# .. end-##_Configuring_toolbox_retry_policy
from wayflowcore.agentspec import AgentSpecExporter # docs-skiprow
serialized_assistant = AgentSpecExporter().to_json(assistant) # docs-skiprow

Expand Down Expand Up @@ -156,9 +176,17 @@ def run_agent_in_command_line(assistant: Agent):
name=MCP_TOOL_NAME,
client_transport=mcp_client
)
# .. start-##_Configuring_direct_tool_retry_policy
with authless_mcp_enabled():
mcp_tool_with_retries = MCPTool(
name=MCP_TOOL_NAME,
client_transport=mcp_client,
retry_policy=RetryPolicy(max_attempts=3),
)
# .. end-##_Configuring_direct_tool_retry_policy

assistant = Flow.from_steps([
ToolExecutionStep(name="mcp_tool_step", tool=mcp_tool)
ToolExecutionStep(name="mcp_tool_step", tool=mcp_tool_with_retries)
])
# .. end-##_Connecting_a_flow_to_the_MCP_server
from wayflowcore.agentspec import AgentSpecExporter, AgentSpecLoader # docs-skiprow
Expand Down
32 changes: 32 additions & 0 deletions docs/wayflowcore/source/core/howtoguides/howto_mcp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,31 @@ Here you will use the toolbox (see the section on Flows to see how to use the ``
Specify the :ref:`transport <clienttransport>` to use to handle the connection to the server and create the toolbox.
You can then equip an agent with the toolbox similarly to tools.

If the MCP server composes tools from external MCP servers and can temporarily
return a partial tool list during health or remount events, declare the expected
tools with ``tool_filter`` and configure ``retry_policy``.
When an expected tool is missing from a successful ``list_tools`` response,
WayFlow retries the tool-list resolution before failing. The same policy is
propagated to generated ``MCPTool`` instances for transient tool execution
failures.

.. literalinclude:: ../code_examples/howto_mcp.py
:language: python
:start-after: # .. start-##_Configuring_toolbox_retry_policy
:end-before: # .. end-##_Configuring_toolbox_retry_policy

.. note::
Tool-list missing-tool retry only applies when WayFlow knows which tools are
expected, for example through ``tool_filter`` or a direct ``MCPTool(name=...)``.
If ``tool_filter`` is ``None``, WayFlow cannot determine whether a successful
tool-list response is incomplete.

.. note::
The MCP tool retry policy is separate from transport-level retry. It handles
successful tool-list responses that are missing expected tools and transient
tool execution failures. Configure ``retry_policy`` on the transport when you
need lower-level HTTP client retry or request timeout behavior.

.. note::
``authless_mcp_enabled()`` disables authorization for local/testing only—do not use in production.
Keep it scoped around the code that creates MCP tools or toolboxes.
Expand Down Expand Up @@ -153,6 +178,13 @@ Create the flow using the MCP tool:
Here you specify the client transport as with the MCP ToolBox, as well as the name of the specific tool
you want to use. Additionally, you can override the tool description (exposed by the MCP server) by
specifying the ``description`` parameter.
You can also pass ``retry_policy`` to retry direct tool resolution and transient
tool execution failures.

.. literalinclude:: ../code_examples/howto_mcp.py
:language: python
:start-after: # .. start-##_Configuring_direct_tool_retry_policy
:end-before: # .. end-##_Configuring_direct_tool_retry_policy

.. tip::

Expand Down
14 changes: 14 additions & 0 deletions wayflowcore/src/wayflowcore/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ class MaxNumTrialsExceededException(ValueError):
class NoSuchToolFoundOnMCPServerError(ValueError):
"""Error thrown when MCP server returns no tools with a given signature"""

def __init__(
self,
message: str,
missing_tool_names: list[str] | None = None,
expected_tool_names: list[str] | None = None,
exposed_tool_names: list[str] | None = None,
attempts: int | None = None,
) -> None:
super().__init__(message)
self.missing_tool_names = missing_tool_names or []
self.expected_tool_names = expected_tool_names or []
self.exposed_tool_names = exposed_tool_names or []
self.attempts = attempts


class DataclassFieldDeserializationError(ValueError):
"""Error thrown when the deserialization of a field of a dataclass fails"""
Expand Down
Loading