Skip to content

Conversation

strawgate
Copy link
Contributor

@strawgate strawgate commented Sep 3, 2025

Fixes: #2406

Introduces a FastMCPToolset which can take a FastMCP Client, a FastMCP Server, a config for an MCP Server, or an MCP JSON config.

This enables running FastMCP Servers in-memory, running Stdio servers via FastMCP, connecting to remote MCP Servers, transforming tools, etc.

Todo:

  • Add tests
  • Update docs
  • Coverage

@strawgate strawgate changed the title Add FastMCP Toolset Add FastMCPToolset Sep 3, 2025
@strawgate strawgate force-pushed the fastmcp-toolset branch 3 times, most recently from f68660d to 54367c8 Compare September 4, 2025 22:13
@strawgate
Copy link
Contributor Author

@DouweM this is ready for review when you have a chance

@DouweM DouweM self-assigned this Sep 8, 2025
if isinstance(part, ImageContent | AudioContent):
return messages.BinaryContent(data=base64.b64decode(part.data), media_type=part.mimeType)

msg = f'Unsupported/Unknown content block type: {type(part)}' # pragma: no cover
Copy link
Collaborator

Choose a reason for hiding this comment

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

What are the other types?

In mcp.py, we have to specifically account for embedded resources or resource links (which we're possibly doing incorrectly: #2288)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably just those two, will take a look

@strawgate
Copy link
Contributor Author

strawgate commented Sep 12, 2025

Basically I had implemented the content handling here, gotten annoyed at how weird it was, and then went and fixed it in FastMCP --

Once the next version of FastMCP comes out I was going to simplify the content handling here and return structured content if it's available and only return content if it's not (which I'm sure is what the MCP code was doing).

Will incorporate the feedback tomorrow and work on simpler examples

Copy link

This PR is stale, and will be closed in 3 days if no reply is received.

@github-actions github-actions bot added the Stale label Sep 20, 2025
@DouweM DouweM removed the Stale label Sep 20, 2025
@strawgate
Copy link
Contributor Author

Ok sorry for the delay, will pick this back up today/tomorrow

Copy link

This PR is stale, and will be closed in 3 days if no reply is received.

@github-actions github-actions bot added the Stale label Sep 28, 2025
@github-actions github-actions bot removed the Stale label Sep 29, 2025
@DouweM DouweM added this to the October 2025 milestone Sep 30, 2025
@strawgate
Copy link
Contributor Author

strawgate commented Oct 1, 2025

PR is updated for most of the feedback, I still need to take a look at resources -- I think if it's a dataclass i lose the nice behavior of being able to provide transports or a client. Let me know if you have any thoughts

right now in the pr its a data class with an init which kinda defeats the purpose

alternatively, I could split it into two toolsets one for transport and one for Client

"coverage[toml]>=7.10.3",
"dirty-equals>=0.9.0",
"duckduckgo-search>=7.0.0",
"fastmcp>=2.12.0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we need it here if it's already coming in through pydantic-ai-slim[...,fastmcp,...] above

if client:
self.client = client
else:
self.client = Client[Any](transport=transport)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm guessing this will raise an error if the dict[str,Any] or str doesn't look like it should?

*,
client: Client[Any] | None = None,
max_retries: int = 2,
tool_error_behavior: Literal['model_retry', 'error'] = 'error',
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd prefer for the default to be model_retry for consistency with MCPServer.


See the [MCP Client](./mcp/client.md) documentation for how to use MCP servers with Pydantic AI.

### FastMCP Tools {#fastmcp-tools}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note to self: before merging this, see if it makes sense to move this to a separate doc that can be listed in the "MCP" section in the sidebar.

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.

Contributing FastMCP Toolset
2 participants