fastmcp 2.8.0__py3-none-any.whl → 2.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastmcp/server/server.py CHANGED
@@ -25,10 +25,7 @@ from mcp.server.lowlevel.server import Server as MCPServer
25
25
  from mcp.server.stdio import stdio_server
26
26
  from mcp.types import (
27
27
  AnyFunction,
28
- EmbeddedResource,
29
28
  GetPromptResult,
30
- ImageContent,
31
- TextContent,
32
29
  ToolAnnotations,
33
30
  )
34
31
  from mcp.types import Prompt as MCPPrompt
@@ -62,6 +59,7 @@ from fastmcp.utilities.cache import TimedCache
62
59
  from fastmcp.utilities.components import FastMCPComponent
63
60
  from fastmcp.utilities.logging import get_logger
64
61
  from fastmcp.utilities.mcp_config import MCPConfig
62
+ from fastmcp.utilities.types import MCPContent
65
63
 
66
64
  if TYPE_CHECKING:
67
65
  from fastmcp.client import Client
@@ -242,16 +240,28 @@ class FastMCP(Generic[LifespanResultT]):
242
240
  ]:
243
241
  if arg is not None:
244
242
  # Deprecated in 2.8.0
245
- warnings.warn(
246
- f"Providing `{name}` when creating a server is deprecated. Provide it when calling `run` or as a global setting instead.",
247
- DeprecationWarning,
248
- stacklevel=2,
249
- )
243
+ if fastmcp.settings.deprecation_warnings:
244
+ warnings.warn(
245
+ f"Providing `{name}` when creating a server is deprecated. Provide it when calling `run` or as a global setting instead.",
246
+ DeprecationWarning,
247
+ stacklevel=2,
248
+ )
250
249
  deprecated_settings[name] = arg
251
250
 
252
251
  combined_settings = fastmcp.settings.model_dump() | deprecated_settings
253
252
  self._deprecated_settings = Settings(**combined_settings)
254
253
 
254
+ @property
255
+ def settings(self) -> Settings:
256
+ # Deprecated in 2.8.0
257
+ if fastmcp.settings.deprecation_warnings:
258
+ warnings.warn(
259
+ "Accessing `.settings` on a FastMCP instance is deprecated. Use the global `fastmcp.settings` instead.",
260
+ DeprecationWarning,
261
+ stacklevel=2,
262
+ )
263
+ return self._deprecated_settings
264
+
255
265
  @property
256
266
  def name(self) -> str:
257
267
  return self._mcp_server.name
@@ -502,7 +512,7 @@ class FastMCP(Generic[LifespanResultT]):
502
512
 
503
513
  async def _mcp_call_tool(
504
514
  self, key: str, arguments: dict[str, Any]
505
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
515
+ ) -> list[MCPContent]:
506
516
  """
507
517
  Handle MCP 'callTool' requests.
508
518
 
@@ -528,9 +538,7 @@ class FastMCP(Generic[LifespanResultT]):
528
538
  # standardize NotFound message
529
539
  raise NotFoundError(f"Unknown tool: {key}")
530
540
 
531
- async def _call_tool(
532
- self, key: str, arguments: dict[str, Any]
533
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
541
+ async def _call_tool(self, key: str, arguments: dict[str, Any]) -> list[MCPContent]:
534
542
  """
535
543
  Call a tool with raw MCP arguments. FastMCP subclasses should override
536
544
  this method, not _mcp_call_tool.
@@ -856,11 +864,12 @@ class FastMCP(Generic[LifespanResultT]):
856
864
  tags: Optional set of tags for categorizing the resource
857
865
  """
858
866
  # deprecated since 2.7.0
859
- warnings.warn(
860
- "The add_resource_fn method is deprecated. Use the resource decorator instead.",
861
- DeprecationWarning,
862
- stacklevel=2,
863
- )
867
+ if fastmcp.settings.deprecation_warnings:
868
+ warnings.warn(
869
+ "The add_resource_fn method is deprecated. Use the resource decorator instead.",
870
+ DeprecationWarning,
871
+ stacklevel=2,
872
+ )
864
873
  self._resource_manager.add_resource_or_template_from_fn(
865
874
  fn=fn,
866
875
  uri=uri,
@@ -1218,19 +1227,19 @@ class FastMCP(Generic[LifespanResultT]):
1218
1227
  port: int | None = None,
1219
1228
  log_level: str | None = None,
1220
1229
  path: str | None = None,
1221
- message_path: str | None = None,
1222
1230
  uvicorn_config: dict[str, Any] | None = None,
1223
1231
  ) -> None:
1224
1232
  """Run the server using SSE transport."""
1225
1233
 
1226
1234
  # Deprecated since 2.3.2
1227
- warnings.warn(
1228
- "The run_sse_async method is deprecated (as of 2.3.2). Use run_http_async for a "
1229
- "modern (non-SSE) alternative, or create an SSE app with "
1230
- "`fastmcp.server.http.create_sse_app` and run it directly.",
1231
- DeprecationWarning,
1232
- stacklevel=2,
1233
- )
1235
+ if fastmcp.settings.deprecation_warnings:
1236
+ warnings.warn(
1237
+ "The run_sse_async method is deprecated (as of 2.3.2). Use run_http_async for a "
1238
+ "modern (non-SSE) alternative, or create an SSE app with "
1239
+ "`fastmcp.server.http.create_sse_app` and run it directly.",
1240
+ DeprecationWarning,
1241
+ stacklevel=2,
1242
+ )
1234
1243
  await self.run_http_async(
1235
1244
  transport="sse",
1236
1245
  host=host,
@@ -1255,12 +1264,13 @@ class FastMCP(Generic[LifespanResultT]):
1255
1264
  middleware: A list of middleware to apply to the app
1256
1265
  """
1257
1266
  # Deprecated since 2.3.2
1258
- warnings.warn(
1259
- "The sse_app method is deprecated (as of 2.3.2). Use http_app as a modern (non-SSE) "
1260
- "alternative, or call `fastmcp.server.http.create_sse_app` directly.",
1261
- DeprecationWarning,
1262
- stacklevel=2,
1263
- )
1267
+ if fastmcp.settings.deprecation_warnings:
1268
+ warnings.warn(
1269
+ "The sse_app method is deprecated (as of 2.3.2). Use http_app as a modern (non-SSE) "
1270
+ "alternative, or call `fastmcp.server.http.create_sse_app` directly.",
1271
+ DeprecationWarning,
1272
+ stacklevel=2,
1273
+ )
1264
1274
  return create_sse_app(
1265
1275
  server=self,
1266
1276
  message_path=message_path or self._deprecated_settings.message_path,
@@ -1283,11 +1293,12 @@ class FastMCP(Generic[LifespanResultT]):
1283
1293
  middleware: A list of middleware to apply to the app
1284
1294
  """
1285
1295
  # Deprecated since 2.3.2
1286
- warnings.warn(
1287
- "The streamable_http_app method is deprecated (as of 2.3.2). Use http_app() instead.",
1288
- DeprecationWarning,
1289
- stacklevel=2,
1290
- )
1296
+ if fastmcp.settings.deprecation_warnings:
1297
+ warnings.warn(
1298
+ "The streamable_http_app method is deprecated (as of 2.3.2). Use http_app() instead.",
1299
+ DeprecationWarning,
1300
+ stacklevel=2,
1301
+ )
1291
1302
  return self.http_app(path=path, middleware=middleware)
1292
1303
 
1293
1304
  def http_app(
@@ -1316,8 +1327,16 @@ class FastMCP(Generic[LifespanResultT]):
1316
1327
  or self._deprecated_settings.streamable_http_path,
1317
1328
  event_store=None,
1318
1329
  auth=self.auth,
1319
- json_response=self._deprecated_settings.json_response,
1320
- stateless_http=self._deprecated_settings.stateless_http,
1330
+ json_response=(
1331
+ json_response
1332
+ if json_response is not None
1333
+ else self._deprecated_settings.json_response
1334
+ ),
1335
+ stateless_http=(
1336
+ stateless_http
1337
+ if stateless_http is not None
1338
+ else self._deprecated_settings.stateless_http
1339
+ ),
1321
1340
  debug=self._deprecated_settings.debug,
1322
1341
  middleware=middleware,
1323
1342
  )
@@ -1340,12 +1359,13 @@ class FastMCP(Generic[LifespanResultT]):
1340
1359
  uvicorn_config: dict[str, Any] | None = None,
1341
1360
  ) -> None:
1342
1361
  # Deprecated since 2.3.2
1343
- warnings.warn(
1344
- "The run_streamable_http_async method is deprecated (as of 2.3.2). "
1345
- "Use run_http_async instead.",
1346
- DeprecationWarning,
1347
- stacklevel=2,
1348
- )
1362
+ if fastmcp.settings.deprecation_warnings:
1363
+ warnings.warn(
1364
+ "The run_streamable_http_async method is deprecated (as of 2.3.2). "
1365
+ "Use run_http_async instead.",
1366
+ DeprecationWarning,
1367
+ stacklevel=2,
1368
+ )
1349
1369
  await self.run_http_async(
1350
1370
  transport="streamable-http",
1351
1371
  host=host,
@@ -1413,30 +1433,33 @@ class FastMCP(Generic[LifespanResultT]):
1413
1433
 
1414
1434
  if tool_separator is not None:
1415
1435
  # Deprecated since 2.4.0
1416
- warnings.warn(
1417
- "The tool_separator parameter is deprecated and will be removed in a future version. "
1418
- "Tools are now prefixed using 'prefix_toolname' format.",
1419
- DeprecationWarning,
1420
- stacklevel=2,
1421
- )
1436
+ if fastmcp.settings.deprecation_warnings:
1437
+ warnings.warn(
1438
+ "The tool_separator parameter is deprecated and will be removed in a future version. "
1439
+ "Tools are now prefixed using 'prefix_toolname' format.",
1440
+ DeprecationWarning,
1441
+ stacklevel=2,
1442
+ )
1422
1443
 
1423
1444
  if resource_separator is not None:
1424
1445
  # Deprecated since 2.4.0
1425
- warnings.warn(
1426
- "The resource_separator parameter is deprecated and ignored. "
1427
- "Resource prefixes are now added using the protocol://prefix/path format.",
1428
- DeprecationWarning,
1429
- stacklevel=2,
1430
- )
1446
+ if fastmcp.settings.deprecation_warnings:
1447
+ warnings.warn(
1448
+ "The resource_separator parameter is deprecated and ignored. "
1449
+ "Resource prefixes are now added using the protocol://prefix/path format.",
1450
+ DeprecationWarning,
1451
+ stacklevel=2,
1452
+ )
1431
1453
 
1432
1454
  if prompt_separator is not None:
1433
1455
  # Deprecated since 2.4.0
1434
- warnings.warn(
1435
- "The prompt_separator parameter is deprecated and will be removed in a future version. "
1436
- "Prompts are now prefixed using 'prefix_promptname' format.",
1437
- DeprecationWarning,
1438
- stacklevel=2,
1439
- )
1456
+ if fastmcp.settings.deprecation_warnings:
1457
+ warnings.warn(
1458
+ "The prompt_separator parameter is deprecated and will be removed in a future version. "
1459
+ "Prompts are now prefixed using 'prefix_promptname' format.",
1460
+ DeprecationWarning,
1461
+ stacklevel=2,
1462
+ )
1440
1463
 
1441
1464
  # if as_proxy is not specified and the server has a custom lifespan,
1442
1465
  # we should treat it as a proxy
@@ -1498,30 +1521,33 @@ class FastMCP(Generic[LifespanResultT]):
1498
1521
  """
1499
1522
  if tool_separator is not None:
1500
1523
  # Deprecated since 2.4.0
1501
- warnings.warn(
1502
- "The tool_separator parameter is deprecated and will be removed in a future version. "
1503
- "Tools are now prefixed using 'prefix_toolname' format.",
1504
- DeprecationWarning,
1505
- stacklevel=2,
1506
- )
1524
+ if fastmcp.settings.deprecation_warnings:
1525
+ warnings.warn(
1526
+ "The tool_separator parameter is deprecated and will be removed in a future version. "
1527
+ "Tools are now prefixed using 'prefix_toolname' format.",
1528
+ DeprecationWarning,
1529
+ stacklevel=2,
1530
+ )
1507
1531
 
1508
1532
  if resource_separator is not None:
1509
1533
  # Deprecated since 2.4.0
1510
- warnings.warn(
1511
- "The resource_separator parameter is deprecated and ignored. "
1512
- "Resource prefixes are now added using the protocol://prefix/path format.",
1513
- DeprecationWarning,
1514
- stacklevel=2,
1515
- )
1534
+ if fastmcp.settings.deprecation_warnings:
1535
+ warnings.warn(
1536
+ "The resource_separator parameter is deprecated and ignored. "
1537
+ "Resource prefixes are now added using the protocol://prefix/path format.",
1538
+ DeprecationWarning,
1539
+ stacklevel=2,
1540
+ )
1516
1541
 
1517
1542
  if prompt_separator is not None:
1518
1543
  # Deprecated since 2.4.0
1519
- warnings.warn(
1520
- "The prompt_separator parameter is deprecated and will be removed in a future version. "
1521
- "Prompts are now prefixed using 'prefix_promptname' format.",
1522
- DeprecationWarning,
1523
- stacklevel=2,
1524
- )
1544
+ if fastmcp.settings.deprecation_warnings:
1545
+ warnings.warn(
1546
+ "The prompt_separator parameter is deprecated and will be removed in a future version. "
1547
+ "Prompts are now prefixed using 'prefix_promptname' format.",
1548
+ DeprecationWarning,
1549
+ stacklevel=2,
1550
+ )
1525
1551
 
1526
1552
  # Import tools from the mounted server
1527
1553
  tool_prefix = f"{prefix}_"
@@ -1657,11 +1683,12 @@ class FastMCP(Generic[LifespanResultT]):
1657
1683
  Create a FastMCP proxy server from a FastMCP client.
1658
1684
  """
1659
1685
  # Deprecated since 2.3.5
1660
- warnings.warn(
1661
- "FastMCP.from_client() is deprecated; use FastMCP.as_proxy() instead.",
1662
- DeprecationWarning,
1663
- stacklevel=2,
1664
- )
1686
+ if fastmcp.settings.deprecation_warnings:
1687
+ warnings.warn(
1688
+ "FastMCP.from_client() is deprecated; use FastMCP.as_proxy() instead.",
1689
+ DeprecationWarning,
1690
+ stacklevel=2,
1691
+ )
1665
1692
 
1666
1693
  return cls.as_proxy(client, **settings)
1667
1694
 
fastmcp/settings.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
3
  import inspect
4
- import warnings
5
4
  from pathlib import Path
6
5
  from typing import Annotated, Any, Literal
7
6
 
@@ -15,6 +14,10 @@ from pydantic_settings import (
15
14
  )
16
15
  from typing_extensions import Self
17
16
 
17
+ from fastmcp.utilities.logging import get_logger
18
+
19
+ logger = get_logger(__name__)
20
+
18
21
  LOG_LEVEL = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
19
22
 
20
23
  DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
@@ -39,10 +42,8 @@ class ExtendedEnvSettingsSource(EnvSettingsSource):
39
42
  if env_val is not None:
40
43
  if prefix == "FASTMCP_SERVER_":
41
44
  # Deprecated in 2.8.0
42
- warnings.warn(
45
+ logger.warning(
43
46
  "Using `FASTMCP_SERVER_` environment variables is deprecated. Use `FASTMCP_` instead.",
44
- DeprecationWarning,
45
- stacklevel=2,
46
47
  )
47
48
  return env_val, field_key, value_is_complex
48
49
 
@@ -89,10 +90,8 @@ class Settings(BaseSettings):
89
90
  which accessed fastmcp.settings.settings
90
91
  """
91
92
  # Deprecated in 2.8.0
92
- warnings.warn(
93
+ logger.warning(
93
94
  "Using fastmcp.settings.settings is deprecated. Use fastmcp.settings instead.",
94
- DeprecationWarning,
95
- stacklevel=2,
96
95
  )
97
96
  return self
98
97
 
@@ -111,6 +110,20 @@ class Settings(BaseSettings):
111
110
  ),
112
111
  ] = True
113
112
 
113
+ deprecation_warnings: Annotated[
114
+ bool,
115
+ Field(
116
+ description=inspect.cleandoc(
117
+ """
118
+ Whether to show deprecation warnings. You can completely reset
119
+ Python's warning behavior by running `warnings.resetwarnings()`.
120
+ Note this will NOT apply to deprecation warnings from the
121
+ settings class itself.
122
+ """,
123
+ )
124
+ ),
125
+ ] = True
126
+
114
127
  client_raise_first_exceptiongroup_error: Annotated[
115
128
  bool,
116
129
  Field(
fastmcp/tools/tool.py CHANGED
@@ -7,7 +7,7 @@ from dataclasses import dataclass
7
7
  from typing import TYPE_CHECKING, Any
8
8
 
9
9
  import pydantic_core
10
- from mcp.types import EmbeddedResource, ImageContent, TextContent, ToolAnnotations
10
+ from mcp.types import TextContent, ToolAnnotations
11
11
  from mcp.types import Tool as MCPTool
12
12
  from pydantic import Field
13
13
 
@@ -17,7 +17,9 @@ from fastmcp.utilities.components import FastMCPComponent
17
17
  from fastmcp.utilities.json_schema import compress_schema
18
18
  from fastmcp.utilities.logging import get_logger
19
19
  from fastmcp.utilities.types import (
20
+ Audio,
20
21
  Image,
22
+ MCPContent,
21
23
  find_kwarg_by_type,
22
24
  get_cached_typeadapter,
23
25
  )
@@ -75,9 +77,7 @@ class Tool(FastMCPComponent):
75
77
  enabled=enabled,
76
78
  )
77
79
 
78
- async def run(
79
- self, arguments: dict[str, Any]
80
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
80
+ async def run(self, arguments: dict[str, Any]) -> list[MCPContent]:
81
81
  """Run the tool with arguments."""
82
82
  raise NotImplementedError("Subclasses must implement run()")
83
83
 
@@ -142,9 +142,7 @@ class FunctionTool(Tool):
142
142
  enabled=enabled if enabled is not None else True,
143
143
  )
144
144
 
145
- async def run(
146
- self, arguments: dict[str, Any]
147
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
145
+ async def run(self, arguments: dict[str, Any]) -> list[MCPContent]:
148
146
  """Run the tool with arguments."""
149
147
  from fastmcp.server.context import Context
150
148
 
@@ -265,17 +263,20 @@ def _convert_to_content(
265
263
  result: Any,
266
264
  serializer: Callable[[Any], str] | None = None,
267
265
  _process_as_single_item: bool = False,
268
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
266
+ ) -> list[MCPContent]:
269
267
  """Convert a result to a sequence of content objects."""
270
268
  if result is None:
271
269
  return []
272
270
 
273
- if isinstance(result, TextContent | ImageContent | EmbeddedResource):
271
+ if isinstance(result, MCPContent):
274
272
  return [result]
275
273
 
276
274
  if isinstance(result, Image):
277
275
  return [result.to_image_content()]
278
276
 
277
+ elif isinstance(result, Audio):
278
+ return [result.to_audio_content()]
279
+
279
280
  if isinstance(result, list | tuple) and not _process_as_single_item:
280
281
  # if the result is a list, then it could either be a list of MCP types,
281
282
  # or a "regular" list that the tool is returning, or a mix of both.
@@ -287,7 +288,7 @@ def _convert_to_content(
287
288
  other_content = []
288
289
 
289
290
  for item in result:
290
- if isinstance(item, TextContent | ImageContent | EmbeddedResource | Image):
291
+ if isinstance(item, MCPContent | Image | Audio):
291
292
  mcp_types.append(_convert_to_content(item)[0])
292
293
  else:
293
294
  other_content.append(item)
@@ -4,13 +4,14 @@ import warnings
4
4
  from collections.abc import Callable
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- from mcp.types import EmbeddedResource, ImageContent, TextContent, ToolAnnotations
7
+ from mcp.types import ToolAnnotations
8
8
 
9
9
  from fastmcp import settings
10
10
  from fastmcp.exceptions import NotFoundError, ToolError
11
11
  from fastmcp.settings import DuplicateBehavior
12
12
  from fastmcp.tools.tool import Tool
13
13
  from fastmcp.utilities.logging import get_logger
14
+ from fastmcp.utilities.types import MCPContent
14
15
 
15
16
  if TYPE_CHECKING:
16
17
  pass
@@ -71,11 +72,12 @@ class ToolManager:
71
72
  ) -> Tool:
72
73
  """Add a tool to the server."""
73
74
  # deprecated in 2.7.0
74
- warnings.warn(
75
- "ToolManager.add_tool_from_fn() is deprecated. Use Tool.from_function() and call add_tool() instead.",
76
- DeprecationWarning,
77
- stacklevel=2,
78
- )
75
+ if settings.deprecation_warnings:
76
+ warnings.warn(
77
+ "ToolManager.add_tool_from_fn() is deprecated. Use Tool.from_function() and call add_tool() instead.",
78
+ DeprecationWarning,
79
+ stacklevel=2,
80
+ )
79
81
  tool = Tool.from_function(
80
82
  fn,
81
83
  name=name,
@@ -119,9 +121,7 @@ class ToolManager:
119
121
  else:
120
122
  raise NotFoundError(f"Unknown tool: {key}")
121
123
 
122
- async def call_tool(
123
- self, key: str, arguments: dict[str, Any]
124
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
124
+ async def call_tool(self, key: str, arguments: dict[str, Any]) -> list[MCPContent]:
125
125
  """Call a tool by name with arguments."""
126
126
  tool = self.get_tool(key)
127
127
  if not tool:
@@ -7,12 +7,12 @@ from dataclasses import dataclass
7
7
  from types import EllipsisType
8
8
  from typing import Any, Literal
9
9
 
10
- from mcp.types import EmbeddedResource, ImageContent, TextContent, ToolAnnotations
10
+ from mcp.types import ToolAnnotations
11
11
  from pydantic import ConfigDict
12
12
 
13
13
  from fastmcp.tools.tool import ParsedFunction, Tool
14
14
  from fastmcp.utilities.logging import get_logger
15
- from fastmcp.utilities.types import get_cached_typeadapter
15
+ from fastmcp.utilities.types import MCPContent, get_cached_typeadapter
16
16
 
17
17
  logger = get_logger(__name__)
18
18
 
@@ -97,6 +97,7 @@ class ArgTransform:
97
97
  type: New type for the argument. Use ... for no change.
98
98
  hide: If True, hide this argument from clients but pass a constant value to parent.
99
99
  required: If True, make argument required (remove default). Use ... for no change.
100
+ examples: Examples for the argument. Use ... for no change.
100
101
 
101
102
  Examples:
102
103
  # Rename argument 'old_name' to 'new_name'
@@ -137,6 +138,7 @@ class ArgTransform:
137
138
  type: Any | EllipsisType = NotSet
138
139
  hide: bool = False
139
140
  required: Literal[True] | EllipsisType = NotSet
141
+ examples: Any | EllipsisType = NotSet
140
142
 
141
143
  def __post_init__(self):
142
144
  """Validate that only one of default or default_factory is provided."""
@@ -200,9 +202,7 @@ class TransformedTool(Tool):
200
202
  forwarding_fn: Callable[..., Any] # Always present, handles arg transformation
201
203
  transform_args: dict[str, ArgTransform]
202
204
 
203
- async def run(
204
- self, arguments: dict[str, Any]
205
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
205
+ async def run(self, arguments: dict[str, Any]) -> list[MCPContent]:
206
206
  """Run the tool with context set for forward() functions.
207
207
 
208
208
  This method executes the tool's function while setting up the context
@@ -584,6 +584,10 @@ class TransformedTool(Tool):
584
584
  # Update the schema with the type information from TypeAdapter
585
585
  new_schema.update(type_schema)
586
586
 
587
+ # Handle examples transformation
588
+ if transform.examples is not NotSet:
589
+ new_schema["examples"] = transform.examples
590
+
587
591
  return new_name, new_schema, is_required
588
592
 
589
593
  @staticmethod
@@ -6,13 +6,21 @@ from collections.abc import Callable
6
6
  from functools import lru_cache
7
7
  from pathlib import Path
8
8
  from types import UnionType
9
- from typing import Annotated, TypeVar, Union, get_args, get_origin
10
-
11
- from mcp.types import ImageContent
9
+ from typing import Annotated, TypeAlias, TypeVar, Union, get_args, get_origin
10
+
11
+ from mcp.types import (
12
+ Annotations,
13
+ AudioContent,
14
+ EmbeddedResource,
15
+ ImageContent,
16
+ TextContent,
17
+ )
12
18
  from pydantic import BaseModel, ConfigDict, TypeAdapter
13
19
 
14
20
  T = TypeVar("T")
15
21
 
22
+ MCPContent: TypeAlias = TextContent | ImageContent | AudioContent | EmbeddedResource
23
+
16
24
 
17
25
  class FastMCPBaseModel(BaseModel):
18
26
  """Base model for FastMCP models."""
@@ -88,6 +96,7 @@ class Image:
88
96
  path: str | Path | None = None,
89
97
  data: bytes | None = None,
90
98
  format: str | None = None,
99
+ annotations: Annotations | None = None,
91
100
  ):
92
101
  if path is None and data is None:
93
102
  raise ValueError("Either path or data must be provided")
@@ -98,6 +107,7 @@ class Image:
98
107
  self.data = data
99
108
  self._format = format
100
109
  self._mime_type = self._get_mime_type()
110
+ self.annotations = annotations
101
111
 
102
112
  def _get_mime_type(self) -> str:
103
113
  """Get MIME type from format or guess from file extension."""
@@ -115,7 +125,11 @@ class Image:
115
125
  }.get(suffix, "application/octet-stream")
116
126
  return "image/png" # default for raw binary data
117
127
 
118
- def to_image_content(self) -> ImageContent:
128
+ def to_image_content(
129
+ self,
130
+ mime_type: str | None = None,
131
+ annotations: Annotations | None = None,
132
+ ) -> ImageContent:
119
133
  """Convert to MCP ImageContent."""
120
134
  if self.path:
121
135
  with open(self.path, "rb") as f:
@@ -125,4 +139,67 @@ class Image:
125
139
  else:
126
140
  raise ValueError("No image data available")
127
141
 
128
- return ImageContent(type="image", data=data, mimeType=self._mime_type)
142
+ return ImageContent(
143
+ type="image",
144
+ data=data,
145
+ mimeType=mime_type or self._mime_type,
146
+ annotations=annotations or self.annotations,
147
+ )
148
+
149
+
150
+ class Audio:
151
+ """Helper class for returning audio from tools."""
152
+
153
+ def __init__(
154
+ self,
155
+ path: str | Path | None = None,
156
+ data: bytes | None = None,
157
+ format: str | None = None,
158
+ annotations: Annotations | None = None,
159
+ ):
160
+ if path is None and data is None:
161
+ raise ValueError("Either path or data must be provided")
162
+ if path is not None and data is not None:
163
+ raise ValueError("Only one of path or data can be provided")
164
+
165
+ self.path = Path(path) if path else None
166
+ self.data = data
167
+ self._format = format
168
+ self._mime_type = self._get_mime_type()
169
+ self.annotations = annotations
170
+
171
+ def _get_mime_type(self) -> str:
172
+ """Get MIME type from format or guess from file extension."""
173
+ if self._format:
174
+ return f"audio/{self._format.lower()}"
175
+
176
+ if self.path:
177
+ suffix = self.path.suffix.lower()
178
+ return {
179
+ ".wav": "audio/wav",
180
+ ".mp3": "audio/mpeg",
181
+ ".ogg": "audio/ogg",
182
+ ".m4a": "audio/mp4",
183
+ ".flac": "audio/flac",
184
+ }.get(suffix, "application/octet-stream")
185
+ return "audio/wav" # default for raw binary data
186
+
187
+ def to_audio_content(
188
+ self,
189
+ mime_type: str | None = None,
190
+ annotations: Annotations | None = None,
191
+ ) -> AudioContent:
192
+ if self.path:
193
+ with open(self.path, "rb") as f:
194
+ data = base64.b64encode(f.read()).decode()
195
+ elif self.data is not None:
196
+ data = base64.b64encode(self.data).decode()
197
+ else:
198
+ raise ValueError("No audio data available")
199
+
200
+ return AudioContent(
201
+ type="audio",
202
+ data=data,
203
+ mimeType=mime_type or self._mime_type,
204
+ annotations=annotations or self.annotations,
205
+ )