Skip to content
Merged
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
8 changes: 4 additions & 4 deletions docs/clients/prompts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async with client:
print(f"Arguments: {[arg.name for arg in prompt.arguments]}")
# Access tags and other metadata
if prompt.meta:
fastmcp_meta = prompt.meta.get('_fastmcp', {})
fastmcp_meta = prompt.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```

Expand All @@ -45,15 +45,15 @@ async with client:
analysis_prompts = [
prompt for prompt in prompts
if prompt.meta and
prompt.meta.get('_fastmcp', {}) and
'analysis' in prompt.meta.get('_fastmcp', {}).get('tags', [])
prompt.meta.get('fastmcp', {}) and
'analysis' in prompt.meta.get('fastmcp', {}).get('tags', [])
]

print(f"Found {len(analysis_prompts)} analysis prompts")
```

<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. Component versions are also included in the metadata when available (e.g., `meta.fastmcp.version`). Other MCP server implementations may not provide this metadata structure.
</Note>

## Using Prompts
Expand Down
10 changes: 5 additions & 5 deletions docs/clients/resources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async with client:
print(f"MIME Type: {resource.mimeType}")
# Access tags and other metadata
if resource.meta:
fastmcp_meta = resource.meta.get('_fastmcp', {})
fastmcp_meta = resource.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```

Expand All @@ -55,7 +55,7 @@ async with client:
print(f"Description: {template.description}")
# Access tags and other metadata
if template.meta:
fastmcp_meta = template.meta.get('_fastmcp', {})
fastmcp_meta = template.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```

Expand All @@ -73,15 +73,15 @@ async with client:
config_resources = [
resource for resource in resources
if resource.meta and
resource.meta.get('_fastmcp', {}) and
'config' in resource.meta.get('_fastmcp', {}).get('tags', [])
resource.meta.get('fastmcp', {}) and
'config' in resource.meta.get('fastmcp', {}).get('tags', [])
]

print(f"Found {len(config_resources)} config resources")
```

<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. Component versions are also included in the metadata when available (e.g., `meta.fastmcp.version`). Other MCP server implementations may not provide this metadata structure.
</Note>

## Reading Resources
Expand Down
8 changes: 4 additions & 4 deletions docs/clients/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async with client:
print(f"Parameters: {tool.inputSchema}")
# Access tags and other metadata
if tool.meta:
fastmcp_meta = tool.meta.get('_fastmcp', {})
fastmcp_meta = tool.meta.get('fastmcp', {})
print(f"Tags: {fastmcp_meta.get('tags', [])}")
```

Expand All @@ -45,15 +45,15 @@ async with client:
analysis_tools = [
tool for tool in tools
if tool.meta and
tool.meta.get('_fastmcp', {}) and
'analysis' in tool.meta.get('_fastmcp', {}).get('tags', [])
tool.meta.get('fastmcp', {}) and
'analysis' in tool.meta.get('fastmcp', {}).get('tags', [])
]

print(f"Found {len(analysis_tools)} analysis tools")
```

<Note>
The `meta` field is part of the standard MCP specification. FastMCP servers include tags and other metadata within a `_fastmcp` namespace (e.g., `meta._fastmcp.tags`) to avoid conflicts with user-defined metadata. This behavior can be controlled with the server's `include_fastmcp_meta` setting - when disabled, the `_fastmcp` namespace won't be included. Other MCP server implementations may not provide this metadata structure.
The `meta` field is part of the standard MCP specification. FastMCP servers always include tags and other metadata within a `fastmcp` namespace (e.g., `meta.fastmcp.tags`) to avoid conflicts with user-defined metadata. Component versions are also included in the metadata when available (e.g., `meta.fastmcp.version`). Other MCP server implementations may not provide this metadata structure.
</Note>

## Executing Tools
Expand Down
62 changes: 62 additions & 0 deletions docs/development/upgrade-guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,68 @@ tag: NEW

This guide provides migration instructions for breaking changes and major updates when upgrading between FastMCP versions.

## FastMCP Metadata Namespace Change

### Metadata Namespace Renamed

The FastMCP metadata namespace has been renamed from `_fastmcp` to `fastmcp` (underscore prefix removed). All metadata is now always included in component responses.

**What changed:**
- Metadata namespace: `meta._fastmcp` → `meta.fastmcp`
- The `include_fastmcp_meta` setting and parameter have been removed
- Component version is now included in metadata when available (`meta.fastmcp.version`)

**Migration steps:**

1. **Update metadata access patterns:**
<CodeGroup>
```python Before
tags = tool.meta.get("_fastmcp", {}).get("tags", [])
```

```python After
tags = tool.meta.get("fastmcp", {}).get("tags", [])
```
</CodeGroup>

2. **Remove `include_fastmcp_meta` parameter:**
<CodeGroup>
```python Before
mcp = FastMCP(include_fastmcp_meta=False)
```

```python After
# Parameter removed - metadata is always included
mcp = FastMCP()
```
</CodeGroup>

3. **Remove `include_fastmcp_meta` from component serialization:**
<CodeGroup>
```python Before
mcp_tool = tool.to_mcp_tool(include_fastmcp_meta=True)
```

```python After
mcp_tool = tool.to_mcp_tool()
```
</CodeGroup>

4. **Access component version from metadata:**
<CodeGroup>
```python Before
# Version was not available in metadata
```

```python After
version = tool.meta.get("fastmcp", {}).get("version")
if version:
print(f"Tool version: {version}")
```
</CodeGroup>

**Why this changed:** The underscore prefix was removed to make the namespace more discoverable and consistent with standard naming conventions. Making metadata always included simplifies the API and ensures consistent behavior across all FastMCP servers.

## v3.0.0

### WSTransport Removed
Expand Down
6 changes: 3 additions & 3 deletions docs/integrations/openapi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ mcp = FastMCP.from_openapi(

#### OpenAPI Tags in Client Meta

FastMCP automatically includes OpenAPI tags from your specification in the component's metadata. These tags are available to MCP clients through the `meta._fastmcp.tags` field, allowing clients to filter and organize components based on the original OpenAPI tagging:
FastMCP automatically includes OpenAPI tags from your specification in the component's metadata. These tags are available to MCP clients through the `meta.fastmcp.tags` field, allowing clients to filter and organize components based on the original OpenAPI tagging:

<CodeGroup>
```json {5} OpenAPI spec with tags
Expand All @@ -345,8 +345,8 @@ async with client:
tools = await client.list_tools()
for tool in tools:
if tool.meta:
# OpenAPI tags are now available in _fastmcp namespace!
fastmcp_meta = tool.meta.get('_fastmcp', {})
# OpenAPI tags are now available in fastmcp namespace!
fastmcp_meta = tool.meta.get('fastmcp', {})
openapi_tags = fastmcp_meta.get('tags', [])
if 'users' in openapi_tags:
print(f"Found user-related tool: {tool.name}")
Expand Down
9 changes: 0 additions & 9 deletions docs/servers/server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@ The `FastMCP` constructor accepts several arguments:
Controls how tool input parameters are validated. When `False` (default), FastMCP uses Pydantic's flexible validation that coerces compatible inputs (e.g., `"10"` → `10` for int parameters). When `True`, uses the MCP SDK's JSON Schema validation to validate inputs against the exact schema before passing them to your function, rejecting any type mismatches. The default mode improves compatibility with LLM clients while maintaining type safety. See [Input Validation Modes](/servers/tools#input-validation-modes) for details
</ParamField>

<ParamField body="include_fastmcp_meta" type="bool" default="True">
<VersionBadge version="2.11.0" />

Whether to include FastMCP metadata in component responses. When `True`, component tags and other FastMCP-specific metadata are included in the `_fastmcp` namespace within each component's `meta` field. When `False`, this metadata is omitted, resulting in cleaner integration with external systems. Can be overridden globally via `FASTMCP_INCLUDE_FASTMCP_META` environment variable
</ParamField>
</Card>
## Components

Expand Down Expand Up @@ -344,7 +339,6 @@ mcp = FastMCP(
on_duplicate_tools="error", # Handle duplicate registrations
on_duplicate_resources="warn",
on_duplicate_prompts="replace",
include_fastmcp_meta=False, # Disable FastMCP metadata for cleaner integration
)
```

Expand All @@ -359,14 +353,12 @@ import fastmcp
print(fastmcp.settings.log_level) # Default: "INFO"
print(fastmcp.settings.mask_error_details) # Default: False
print(fastmcp.settings.strict_input_validation) # Default: False
print(fastmcp.settings.include_fastmcp_meta) # Default: True
```

Common global settings include:
- **`log_level`**: Logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"), set with `FASTMCP_LOG_LEVEL`
- **`mask_error_details`**: Whether to hide detailed error information from clients, set with `FASTMCP_MASK_ERROR_DETAILS`
- **`strict_input_validation`**: Controls tool input validation mode (default: False for flexible coercion), set with `FASTMCP_STRICT_INPUT_VALIDATION`. See [Input Validation Modes](/servers/tools#input-validation-modes)
- **`include_fastmcp_meta`**: Whether to include FastMCP metadata in component responses (default: True), set with `FASTMCP_INCLUDE_FASTMCP_META`
- **`env_file`**: Path to the environment file to load settings from (default: ".env"), set with `FASTMCP_ENV_FILE`. Useful when your project uses a `.env` file with syntax incompatible with python-dotenv

### Transport-Specific Configuration
Expand Down Expand Up @@ -399,6 +391,5 @@ Global FastMCP settings can be configured via environment variables (prefixed wi
export FASTMCP_LOG_LEVEL=DEBUG
export FASTMCP_MASK_ERROR_DETAILS=True
export FASTMCP_STRICT_INPUT_VALIDATION=False
export FASTMCP_INCLUDE_FASTMCP_META=False
```

4 changes: 1 addition & 3 deletions src/fastmcp/prompts/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,6 @@ class Prompt(FastMCPComponent):

def to_mcp_prompt(
self,
*,
include_fastmcp_meta: bool | None = None,
**overrides: Any,
) -> SDKPrompt:
"""Convert the prompt to an MCP prompt."""
Expand All @@ -221,7 +219,7 @@ def to_mcp_prompt(
title=overrides.get("title", self.title),
icons=overrides.get("icons", self.icons),
_meta=overrides.get( # type: ignore[call-arg] # _meta is Pydantic alias for meta field
"_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
"_meta", self.get_meta()
),
)

Expand Down
4 changes: 1 addition & 3 deletions src/fastmcp/resources/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,6 @@ async def _read(

def to_mcp_resource(
self,
*,
include_fastmcp_meta: bool | None = None,
**overrides: Any,
) -> SDKResource:
"""Convert the resource to an SDKResource."""
Expand All @@ -372,7 +370,7 @@ def to_mcp_resource(
icons=overrides.get("icons", self.icons),
annotations=overrides.get("annotations", self.annotations),
_meta=overrides.get( # type: ignore[call-arg] # _meta is Pydantic alias for meta field
"_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
"_meta", self.get_meta()
),
)

Expand Down
4 changes: 1 addition & 3 deletions src/fastmcp/resources/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,6 @@ async def create_resource(self, uri: str, params: dict[str, Any]) -> Resource:

def to_mcp_template(
self,
*,
include_fastmcp_meta: bool | None = None,
**overrides: Any,
) -> SDKResourceTemplate:
"""Convert the resource template to an SDKResourceTemplate."""
Expand All @@ -266,7 +264,7 @@ def to_mcp_template(
icons=overrides.get("icons", self.icons),
annotations=overrides.get("annotations", self.annotations),
_meta=overrides.get( # type: ignore[call-arg] # _meta is Pydantic alias for meta field
"_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
"_meta", self.get_meta()
),
)

Expand Down
12 changes: 6 additions & 6 deletions src/fastmcp/server/providers/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from fastmcp.server.server import FastMCP
from fastmcp.server.tasks.config import TaskConfig
from fastmcp.tools.tool import Tool, ToolResult
from fastmcp.utilities.components import FastMCPComponent
from fastmcp.utilities.components import FastMCPComponent, get_fastmcp_metadata
from fastmcp.utilities.logging import get_logger

if TYPE_CHECKING:
Expand Down Expand Up @@ -104,7 +104,7 @@ def from_mcp_tool(
output_schema=mcp_tool.outputSchema,
icons=mcp_tool.icons,
meta=mcp_tool.meta,
tags=(mcp_tool.meta or {}).get("_fastmcp", {}).get("tags", []),
tags=get_fastmcp_metadata(mcp_tool.meta).get("tags", []),
)

async def run(
Expand Down Expand Up @@ -211,7 +211,7 @@ def from_mcp_resource(
mime_type=mcp_resource.mimeType or "text/plain",
icons=mcp_resource.icons,
meta=mcp_resource.meta,
tags=(mcp_resource.meta or {}).get("_fastmcp", {}).get("tags", []),
tags=get_fastmcp_metadata(mcp_resource.meta).get("tags", []),
task_config=TaskConfig(mode="forbidden"),
)

Expand Down Expand Up @@ -309,7 +309,7 @@ def from_mcp_template( # type: ignore[override]
icons=mcp_template.icons,
parameters={}, # Remote templates don't have local parameters
meta=mcp_template.meta,
tags=(mcp_template.meta or {}).get("_fastmcp", {}).get("tags", []),
tags=get_fastmcp_metadata(mcp_template.meta).get("tags", []),
task_config=TaskConfig(mode="forbidden"),
)

Expand Down Expand Up @@ -371,7 +371,7 @@ async def create_resource(
].mimeType, # Use first item's mimeType for backward compatibility
icons=self.icons,
meta=self.meta,
tags=(self.meta or {}).get("_fastmcp", {}).get("tags", []),
tags=get_fastmcp_metadata(self.meta).get("tags", []),
_cached_content=cached_content,
)

Expand Down Expand Up @@ -429,7 +429,7 @@ def from_mcp_prompt(
arguments=arguments,
icons=mcp_prompt.icons,
meta=mcp_prompt.meta,
tags=(mcp_prompt.meta or {}).get("_fastmcp", {}).get("tags", []),
tags=get_fastmcp_metadata(mcp_prompt.meta).get("tags", []),
task_config=TaskConfig(mode="forbidden"),
)

Expand Down
11 changes: 0 additions & 11 deletions src/fastmcp/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ def __init__(
tool_serializer: ToolResultSerializerType | None = None,
include_tags: Collection[str] | None = None,
exclude_tags: Collection[str] | None = None,
include_fastmcp_meta: bool | None = None,
on_duplicate: DuplicateBehavior | None = None,
strict_input_validation: bool | None = None,
tasks: bool | None = None,
Expand Down Expand Up @@ -406,12 +405,6 @@ def __init__(
sampling_handler_behavior or "fallback"
)

self.include_fastmcp_meta: bool = (
include_fastmcp_meta
if include_fastmcp_meta is not None
else fastmcp.settings.include_fastmcp_meta
)

self._handle_deprecated_settings(
log_level=log_level,
debug=debug,
Expand Down Expand Up @@ -1939,7 +1932,6 @@ async def _list_tools_mcp(self) -> list[SDKTool]:
return [
tool.to_mcp_tool(
name=tool.name,
include_fastmcp_meta=self.include_fastmcp_meta,
)
for tool in tools
]
Expand All @@ -1956,7 +1948,6 @@ async def _list_resources_mcp(self) -> list[SDKResource]:
return [
resource.to_mcp_resource(
uri=str(resource.uri),
include_fastmcp_meta=self.include_fastmcp_meta,
)
for resource in resources
]
Expand All @@ -1973,7 +1964,6 @@ async def _list_resource_templates_mcp(self) -> list[SDKResourceTemplate]:
return [
template.to_mcp_template(
uriTemplate=template.uri_template,
include_fastmcp_meta=self.include_fastmcp_meta,
)
for template in templates
]
Expand All @@ -1990,7 +1980,6 @@ async def _list_prompts_mcp(self) -> list[SDKPrompt]:
return [
prompt.to_mcp_prompt(
name=prompt.name,
include_fastmcp_meta=self.include_fastmcp_meta,
)
for prompt in prompts
]
Expand Down
Loading