What do you do when the perfect tool… isn’t so perfect? You’ve found a library or an API that does exactly what you need, but its interface is a nightmare for an LLM. The argument names are cryptic, the descriptions are missing, and it exposes parameters you’d rather keep hidden.
Today, we’re thrilled to announce FastMCP 2.8, a massive release that starts to put the power of curation directly in your hands. This update is all about giving you fine-grained control to transform, filter, and shape the components your AI interacts with, and it’s the foundation for a lot of the new features we’re working on.
Give us a star on GitHub or check out the updated docs at gofastmcp.com.
Also! The waitlist is open for FastMCP Cloud, which is literally the fastest way to get started with MCP. More on that very soon.
🛠️ Tool Transformation: Curate the LLM Experience
The highlight of this release is first-class Tool Transformation. Instead of wrestling with complex prompts to make an LLM use a clunky tool, you can now adapt the tool itself to be perfectly LLM-friendly.
This feature was developed in close partnership with Bill Easton of Elastic, who has become one of FastMCP’s most prolific contributors and a key thought partner. As Bill brilliantly put it:
Tool transformation flips Prompt Engineering on its head: stop writing tool-friendly LLM prompts and start providing LLM-friendly Tools.
With a single Tool.from_tool()
call, you can now create enhanced variations of any tool—whether it’s from your own codebase, a third-party library, or an auto-generated OpenAPI server.
- Rename arguments to be more intuitive (
q
becomessearch_query
). - Rewrite descriptions to give the LLM better context.
- Hide parameters like API keys, providing default values behind the scenes.
- Wrap a tool with custom validation or post-processing logic.
from fastmcp import FastMCPfrom fastmcp.tools import Toolfrom fastmcp.tools.tool_transform import ArgTransform
mcp = FastMCP()
# An existing, generic tool from a third partyfrom some_library import generic_search
# Transform it into a domain-specific, LLM-friendly toolproduct_search = Tool.from_tool( tool=generic_search, name="find_products_by_keyword", description="Searches the product catalog for items matching a keyword.", transform_args={ "q": ArgTransform( name="keyword", description="The search term for finding products.", ), "limit": ArgTransform(hide=True, default=10) # Hide and pass along a new default })
mcp.add_tool(product_search)
This is a foundational step towards a future where we don’t just provide tools, but actively curate the LLM’s environment, paving the way for more sophisticated agentic systems.
🫥 Enabling and Disabling Components
Now that you’ve transformed a tool into a sleek, LLM-friendly powerhouse, you’ll probably want to hide the old, busted original. This release introduces a simple way to manage component visibility.
Every tool, resource, and prompt can now be programmatically enabled or disabled. You can set the initial state in the decorator or toggle it at runtime.
@mcp.tool(enabled=False)def legacy_tool(): """This tool is disabled from the start.""" # ...
# you can enable it laterlegacy_tool.enable()
# or turn it back offlegacy_tool.disable()
This gives you precise control to roll out new features, deprecate old ones, or dynamically adjust the toolset available to your clients.
🏷️ Component Control: Tags Have a Purpose!
FastMCP introduced component tags all the way back in v2.1.0, and since then users have been asking: “What are these for?” Today, we’re excited to finally have an answer:
Tag-based filtering is here, allowing you to declaratively control which components are exposed based on the tags you assign.
mcp = FastMCP( name="MyFilteredServer", # Only expose components with the "public" tag include_tags={"public"}, # But exclude any that are also tagged "beta" exclude_tags={"beta"})
@mcp.tool(tags={"public"})def stable_feature(): """This tool is public and will be exposed.""" # ...
@mcp.tool(tags={"public", "beta"})def new_feature(): """This tool is public but also beta, so it will be excluded.""" # ...
This is perfect for managing different environments (e.g., exposing internal
tools in dev but not prod) or controlling access for different user types.
🔀 A Pragmatic Shift for OpenAPI
In our commitment to providing the simplest path to production, we sometimes have to make pragmatic decisions. This release includes a minor but important breaking change to FastMCP’s default OpenAPI route maps. To improve out-of-the-box compatibility with the widest range of LLM clients, all API endpoints from an OpenAPI spec are now converted to Tools
by default.
Previously, GET
requests were mapped to either resources or resource templates as appropriate. However, the reality is that most MCP clients available today (including all major foundation model vendors… looking at you, Anthropic) only support MCP tools and essentially disregard every other feature.
While we could wait for them to adopt full-featured clients (I know a great library…), we’ve decided to make the pragmatic shift to tools-only in order to ensure that our users don’t have to do extra work.
For users who need the previous semantic behavior, it can be easily restored by providing a custom route_maps
configuration, as detailed in the OpenAPI docs.
Alongside these headline features, v2.8.0 continues the major modernization effort we began in v2.7, with a host of internal improvements and optimizations to make FastMCP more robust and performant.
FastMCP 2.8 puts more power and control in your hands than ever before. We’re excited to see how you use these new features to build even more sophisticated and robust MCP applications.
- Upgrade:
uv add fastmcp
orpip install fastmcp --upgrade
- Explore the documentation on Tool Transformation, Tag-based Filtering, and Enabling/Disabling Components.
- Check out the code and examples on GitHub.
- Sign up for FastMCP Cloud.
Happy Transforming! 🤖
Subscribe
Comments
Reply to this post on Bluesky to join the conversation.
No comments yet. Be the first to comment!