From 515cdd5cf5e12b20970015704871f22093689284 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 14:20:38 -0600 Subject: [PATCH 1/6] Update mcp_mixin.py to include enabled, annotations, excluded_args, serializer annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None, --- src/fastmcp/contrib/mcp_mixin/mcp_mixin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py index dd3a007b5..cb54fb69b 100644 --- a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +++ b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py @@ -23,6 +23,10 @@ def mcp_tool( name: str | None = None, description: str | None = None, tags: set[str] | None = None, + annotations: ToolAnnotations | None = None, + exclude_args: list[str] | None = None, + serializer: Callable[[Any], str] | None = None, + enabled: bool | None = None, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: """Decorator to mark a method as an MCP tool for later registration.""" @@ -31,6 +35,10 @@ def mcp_tool( "name": name or func.__name__, "description": description, "tags": tags, + "annotations": annotations, + "exclude_args": exclude_args, + "serializer": serializer, + "enabled": enabled, } call_args = {k: v for k, v in call_args.items() if v is not None} setattr(func, _MCP_REGISTRATION_TOOL_ATTR, call_args) @@ -46,6 +54,7 @@ def mcp_resource( description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, + enabled: bool | None = None, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: """Decorator to mark a method as an MCP resource for later registration.""" @@ -56,6 +65,7 @@ def mcp_resource( "description": description, "mime_type": mime_type, "tags": tags, + "enabled": enabled, } call_args = {k: v for k, v in call_args.items() if v is not None} @@ -78,6 +88,7 @@ def mcp_prompt( "name": name or func.__name__, "description": description, "tags": tags, + "enabled": enabled, } call_args = {k: v for k, v in call_args.items() if v is not None} From 9788fabe19a15f226363e946bb4127b58968bbd9 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 14:43:05 -0600 Subject: [PATCH 2/6] Update README.md --- src/fastmcp/contrib/mcp_mixin/README.md | 37 ++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/fastmcp/contrib/mcp_mixin/README.md b/src/fastmcp/contrib/mcp_mixin/README.md index 6dce9c9e3..5e0af966f 100644 --- a/src/fastmcp/contrib/mcp_mixin/README.md +++ b/src/fastmcp/contrib/mcp_mixin/README.md @@ -4,6 +4,18 @@ This module provides the `MCPMixin` base class and associated decorators (`@mcp_ It allows developers to easily define classes whose methods can be registered as tools, resources, or prompts with a `FastMCP` server instance using the `register_all()`, `register_tools()`, `register_resources()`, or `register_prompts()` methods provided by the mixin. +Includes support for +Tools: +* [enable/disable](https://gofastmcp.com/servers/tools#disabling-tools) +* [annotations](https://gofastmcp.com/servers/tools#annotations-2) +* [excluded arguments](https://gofastmcp.com/servers/tools#excluding-arguments) + +Prompts: +* [enable/disable](https://gofastmcp.com/servers/prompts#disabling-prompts) + +Resources: +* [enable/disabe](https://gofastmcp.com/servers/resources#disabling-resources) + ## Usage Inherit from `MCPMixin` and use the decorators on the methods you want to register. @@ -17,10 +29,33 @@ class MyComponent(MCPMixin): def tool_method(self): return "Tool executed!" + # example of disabled tool + @mcp_tool(name="my_tool", description="Does something cool.", enabled=False) + def disabled_tool_method(self): + # This function can't be called by client because it's disabled + return "You'll never get here!" + + @mcp_tool( + name="my_tool", description="Does something cool.", + annotations={ + "title": "Attn LLM, use this tool first!", + "readOnlyHint": False, + "destructiveHint": False, + "idempotentHint": False + } + ) + def tool_method(self): + return "Tool executed!" + @mcp_resource(uri="component://data") def resource_method(self): return {"data": "some data"} + # Disabled resource + @mcp_resource(uri="component://data", enabled=False) + def resource_method(self): + return {"data": "some data"} + mcp_server = FastMCP() component = MyComponent() @@ -36,4 +71,4 @@ component.register_all(mcp_server, prefix="my_comp") # Or 'my_tool' and 'component://data' are registered (if no prefix used) ``` -The `prefix` argument in registration methods is optional. If omitted, methods are registered with their original decorated names/URIs. Individual separators (`tools_separator`, `resources_separator`, `prompts_separator`) can also be provided to `register_all` to change the separator for specific types. \ No newline at end of file +The `prefix` argument in registration methods is optional. If omitted, methods are registered with their original decorated names/URIs. Individual separators (`tools_separator`, `resources_separator`, `prompts_separator`) can also be provided to `register_all` to change the separator for specific types. From 909194dc89a346231a4a5c76379426c71972ba7b Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 14:50:26 -0600 Subject: [PATCH 3/6] add missing import, DOH! --- src/fastmcp/contrib/mcp_mixin/mcp_mixin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py index cb54fb69b..95a2977e1 100644 --- a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +++ b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py @@ -3,6 +3,8 @@ from collections.abc import Callable from typing import TYPE_CHECKING, Any +from mcp.types import ToolAnnotations + from fastmcp.prompts.prompt import Prompt from fastmcp.resources.resource import Resource from fastmcp.tools.tool import Tool From a37df5077a14add84969d4d01058f9e1d498ad98 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 14:51:04 -0600 Subject: [PATCH 4/6] add missing parameter, DOH! --- src/fastmcp/contrib/mcp_mixin/mcp_mixin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py index 95a2977e1..e33cea538 100644 --- a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +++ b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py @@ -82,6 +82,7 @@ def mcp_prompt( name: str | None = None, description: str | None = None, tags: set[str] | None = None, + enabled: bool | None = None, ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: """Decorator to mark a method as an MCP prompt for later registration.""" From 3deddf542e5c7c94591904448fd7d020a0c39416 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 15:05:24 -0600 Subject: [PATCH 5/6] Add moree docs --- src/fastmcp/contrib/mcp_mixin/README.md | 56 +++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/src/fastmcp/contrib/mcp_mixin/README.md b/src/fastmcp/contrib/mcp_mixin/README.md index 5e0af966f..86c46651c 100644 --- a/src/fastmcp/contrib/mcp_mixin/README.md +++ b/src/fastmcp/contrib/mcp_mixin/README.md @@ -1,3 +1,5 @@ +from mcp.types import ToolAnnotations + # MCP Mixin This module provides the `MCPMixin` base class and associated decorators (`@mcp_tool`, `@mcp_resource`, `@mcp_prompt`). @@ -21,8 +23,9 @@ Resources: Inherit from `MCPMixin` and use the decorators on the methods you want to register. ```python +from mcp.types import ToolAnnotations from fastmcp import FastMCP -from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource +from fastmcp.contrib.mcp_mixin import MCPMixin, mcp_tool, mcp_resource, mcp_prompt class MyComponent(MCPMixin): @mcp_tool(name="my_tool", description="Does something cool.") @@ -35,18 +38,47 @@ class MyComponent(MCPMixin): # This function can't be called by client because it's disabled return "You'll never get here!" + # example of excluded parameter tool @mcp_tool( name="my_tool", description="Does something cool.", - annotations={ - "title": "Attn LLM, use this tool first!", - "readOnlyHint": False, - "destructiveHint": False, - "idempotentHint": False - } + enabled=False, exclude_args=['delete_everything'], + ) + def excluded_param_tool_method(self, delete_everything=False): + # MCP tool calls can't pass the "delete_everything" argument + if delete_everything: + return "Nothing to delete, I bet you're not a tool :)" + return "You might be a tool if..." + + # example tool w/annotations + @mcp_tool( + name="my_tool", description="Does something cool.", + annotations=ToolAnnotations( + title="Attn LLM, use this tool first!", + readOnlyHint=False, + destructiveHint=False, + idempotentHint=False, + ) ) def tool_method(self): return "Tool executed!" + # example tool w/everything + @mcp_tool( + name="my_tool", description="Does something cool.", + enabled=True, + exclude_args=['delete_all'], + annotations=ToolAnnotations( + title="Attn LLM, use this tool first!", + readOnlyHint=False, + destructiveHint=False, + idempotentHint=False, + ) + ) + def tool_method(self, delete_all=False): + if delete_all: + return "99 records deleted. I bet you're not a tool :)" + return "Tool executed, but you might be a tool!" + @mcp_resource(uri="component://data") def resource_method(self): return {"data": "some data"} @@ -56,6 +88,16 @@ class MyComponent(MCPMixin): def resource_method(self): return {"data": "some data"} + # prompt + @mcp_prompt(name="A prompt") + def prompt_method(self, name): + return f"Whats up {name}?" + + # disabled prompt + @mcp_prompt(name="A prompt", enabled=False) + def prompt_method(self, name): + return f"Whats up {name}?" + mcp_server = FastMCP() component = MyComponent() From cf56bdd15e76c274087fb18488c932233e4bd073 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 17 Jun 2025 16:14:20 -0600 Subject: [PATCH 6/6] allow dicts to be passed as annotations --- src/fastmcp/contrib/mcp_mixin/mcp_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py index e33cea538..6b8cf62f7 100644 --- a/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py +++ b/src/fastmcp/contrib/mcp_mixin/mcp_mixin.py @@ -25,7 +25,7 @@ def mcp_tool( name: str | None = None, description: str | None = None, tags: set[str] | None = None, - annotations: ToolAnnotations | None = None, + annotations: ToolAnnotations | dict[str, Any] | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None,