fastmcp 2.1.2__py3-none-any.whl → 2.2.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/settings.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
- from enum import Enum
4
3
  from typing import TYPE_CHECKING, Literal
5
4
 
6
5
  from pydantic import Field
@@ -11,12 +10,7 @@ if TYPE_CHECKING:
11
10
 
12
11
  LOG_LEVEL = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
13
12
 
14
-
15
- class DuplicateBehavior(Enum):
16
- WARN = "warn"
17
- ERROR = "error"
18
- REPLACE = "replace"
19
- IGNORE = "ignore"
13
+ DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
20
14
 
21
15
 
22
16
  class Settings(BaseSettings):
@@ -48,26 +42,29 @@ class ServerSettings(BaseSettings):
48
42
  log_level: LOG_LEVEL = Field(default_factory=lambda: Settings().log_level)
49
43
 
50
44
  # HTTP settings
51
- host: str = "0.0.0.0"
45
+ host: str = "127.0.0.1"
52
46
  port: int = 8000
53
47
  sse_path: str = "/sse"
54
48
  message_path: str = "/messages/"
55
49
  debug: bool = False
56
50
 
57
51
  # resource settings
58
- on_duplicate_resources: DuplicateBehavior = DuplicateBehavior.WARN
52
+ on_duplicate_resources: DuplicateBehavior = "warn"
59
53
 
60
54
  # tool settings
61
- on_duplicate_tools: DuplicateBehavior = DuplicateBehavior.WARN
55
+ on_duplicate_tools: DuplicateBehavior = "warn"
62
56
 
63
57
  # prompt settings
64
- on_duplicate_prompts: DuplicateBehavior = DuplicateBehavior.WARN
58
+ on_duplicate_prompts: DuplicateBehavior = "warn"
65
59
 
66
60
  dependencies: list[str] = Field(
67
61
  default_factory=list,
68
62
  description="List of dependencies to install in the server environment",
69
63
  )
70
64
 
65
+ # cache settings (for checking mounted servers)
66
+ cache_expiration_seconds: float = 0
67
+
71
68
 
72
69
  class ClientSettings(BaseSettings):
73
70
  """FastMCP client settings."""
fastmcp/tools/tool.py CHANGED
@@ -1,14 +1,18 @@
1
- from __future__ import annotations as _annotations
1
+ from __future__ import annotations
2
2
 
3
3
  import inspect
4
+ import json
4
5
  from collections.abc import Callable
5
6
  from typing import TYPE_CHECKING, Annotated, Any
6
7
 
8
+ import pydantic_core
9
+ from mcp.types import EmbeddedResource, ImageContent, TextContent
10
+ from mcp.types import Tool as MCPTool
7
11
  from pydantic import BaseModel, BeforeValidator, Field
8
12
 
9
13
  from fastmcp.exceptions import ToolError
10
14
  from fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
11
- from fastmcp.utilities.types import _convert_set_defaults
15
+ from fastmcp.utilities.types import Image, _convert_set_defaults
12
16
 
13
17
  if TYPE_CHECKING:
14
18
  from mcp.server.session import ServerSessionT
@@ -57,7 +61,7 @@ class Tool(BaseModel):
57
61
  is_async = inspect.iscoroutinefunction(fn)
58
62
 
59
63
  if context_kwarg is None:
60
- if isinstance(fn, classmethod):
64
+ if inspect.ismethod(fn) and hasattr(fn, "__func__"):
61
65
  sig = inspect.signature(fn.__func__)
62
66
  else:
63
67
  sig = inspect.signature(fn)
@@ -66,14 +70,16 @@ class Tool(BaseModel):
66
70
  context_kwarg = param_name
67
71
  break
68
72
 
73
+ # Use callable typing to ensure fn is treated as a callable despite being a classmethod
74
+ fn_callable: Callable[..., Any] = fn
69
75
  func_arg_metadata = func_metadata(
70
- fn,
76
+ fn_callable,
71
77
  skip_names=[context_kwarg] if context_kwarg is not None else [],
72
78
  )
73
79
  parameters = func_arg_metadata.arg_model.model_json_schema()
74
80
 
75
81
  return cls(
76
- fn=fn,
82
+ fn=fn_callable,
77
83
  name=func_name,
78
84
  description=func_doc,
79
85
  parameters=parameters,
@@ -87,10 +93,10 @@ class Tool(BaseModel):
87
93
  self,
88
94
  arguments: dict[str, Any],
89
95
  context: Context[ServerSessionT, LifespanContextT] | None = None,
90
- ) -> Any:
96
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
91
97
  """Run the tool with arguments."""
92
98
  try:
93
- return await self.fn_metadata.call_fn_with_arg_validation(
99
+ result = await self.fn_metadata.call_fn_with_arg_validation(
94
100
  self.fn,
95
101
  self.is_async,
96
102
  arguments,
@@ -98,10 +104,64 @@ class Tool(BaseModel):
98
104
  if self.context_kwarg is not None
99
105
  else None,
100
106
  )
107
+ return _convert_to_content(result)
101
108
  except Exception as e:
102
109
  raise ToolError(f"Error executing tool {self.name}: {e}") from e
103
110
 
111
+ def to_mcp_tool(self, **overrides: Any) -> MCPTool:
112
+ kwargs = {
113
+ "name": self.name,
114
+ "description": self.description,
115
+ "inputSchema": self.parameters,
116
+ }
117
+ return MCPTool(**kwargs | overrides)
118
+
104
119
  def __eq__(self, other: object) -> bool:
105
120
  if not isinstance(other, Tool):
106
121
  return False
107
122
  return self.model_dump() == other.model_dump()
123
+
124
+
125
+ def _convert_to_content(
126
+ result: Any,
127
+ _process_as_single_item: bool = False,
128
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
129
+ """Convert a result to a sequence of content objects."""
130
+ if result is None:
131
+ return []
132
+
133
+ if isinstance(result, TextContent | ImageContent | EmbeddedResource):
134
+ return [result]
135
+
136
+ if isinstance(result, Image):
137
+ return [result.to_image_content()]
138
+
139
+ if isinstance(result, list | tuple) and not _process_as_single_item:
140
+ # if the result is a list, then it could either be a list of MCP types,
141
+ # or a "regular" list that the tool is returning, or a mix of both.
142
+ #
143
+ # so we extract all the MCP types / images and convert them as individual content elements,
144
+ # and aggregate the rest as a single content element
145
+
146
+ mcp_types = []
147
+ other_content = []
148
+
149
+ for item in result:
150
+ if isinstance(item, TextContent | ImageContent | EmbeddedResource | Image):
151
+ mcp_types.append(_convert_to_content(item)[0])
152
+ else:
153
+ other_content.append(item)
154
+ if other_content:
155
+ other_content = _convert_to_content(
156
+ other_content, _process_as_single_item=True
157
+ )
158
+
159
+ return other_content + mcp_types
160
+
161
+ if not isinstance(result, str):
162
+ try:
163
+ result = json.dumps(pydantic_core.to_jsonable_python(result))
164
+ except Exception:
165
+ result = str(result)
166
+
167
+ return [TextContent(type="text", text=result)]
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
- import copy
4
3
  from collections.abc import Callable
5
4
  from typing import TYPE_CHECKING, Any
6
5
 
7
6
  from mcp.shared.context import LifespanContextT
7
+ from mcp.types import EmbeddedResource, ImageContent, TextContent
8
8
 
9
- from fastmcp.exceptions import ToolError
9
+ from fastmcp.exceptions import NotFoundError
10
10
  from fastmcp.settings import DuplicateBehavior
11
11
  from fastmcp.tools.tool import Tool
12
12
  from fastmcp.utilities.logging import get_logger
@@ -22,17 +22,38 @@ logger = get_logger(__name__)
22
22
  class ToolManager:
23
23
  """Manages FastMCP tools."""
24
24
 
25
- def __init__(self, duplicate_behavior: DuplicateBehavior = DuplicateBehavior.WARN):
25
+ def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
26
26
  self._tools: dict[str, Tool] = {}
27
+
28
+ # Default to "warn" if None is provided
29
+ if duplicate_behavior is None:
30
+ duplicate_behavior = "warn"
31
+
32
+ if duplicate_behavior not in DuplicateBehavior.__args__:
33
+ raise ValueError(
34
+ f"Invalid duplicate_behavior: {duplicate_behavior}. "
35
+ f"Must be one of: {', '.join(DuplicateBehavior.__args__)}"
36
+ )
37
+
27
38
  self.duplicate_behavior = duplicate_behavior
28
39
 
29
- def get_tool(self, name: str) -> Tool | None:
30
- """Get tool by name."""
31
- return self._tools.get(name)
40
+ def has_tool(self, key: str) -> bool:
41
+ """Check if a tool exists."""
42
+ return key in self._tools
43
+
44
+ def get_tool(self, key: str) -> Tool:
45
+ """Get tool by key."""
46
+ if key in self._tools:
47
+ return self._tools[key]
48
+ raise NotFoundError(f"Unknown tool: {key}")
49
+
50
+ def get_tools(self) -> dict[str, Tool]:
51
+ """Get all registered tools, indexed by registered key."""
52
+ return self._tools
32
53
 
33
54
  def list_tools(self) -> list[Tool]:
34
55
  """List all registered tools."""
35
- return list(self._tools.values())
56
+ return list(self.get_tools().values())
36
57
 
37
58
  def add_tool_from_fn(
38
59
  self,
@@ -45,55 +66,33 @@ class ToolManager:
45
66
  tool = Tool.from_function(fn, name=name, description=description, tags=tags)
46
67
  return self.add_tool(tool)
47
68
 
48
- def add_tool(self, tool: Tool) -> Tool:
69
+ def add_tool(self, tool: Tool, key: str | None = None) -> Tool:
49
70
  """Register a tool with the server."""
50
- existing = self._tools.get(tool.name)
71
+ key = key or tool.name
72
+ existing = self._tools.get(key)
51
73
  if existing:
52
- if self.duplicate_behavior == DuplicateBehavior.WARN:
53
- logger.warning(f"Tool already exists: {tool.name}")
54
- self._tools[tool.name] = tool
55
- elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
56
- self._tools[tool.name] = tool
57
- elif self.duplicate_behavior == DuplicateBehavior.ERROR:
58
- raise ValueError(f"Tool already exists: {tool.name}")
59
- elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
60
- pass
61
- self._tools[tool.name] = tool
74
+ if self.duplicate_behavior == "warn":
75
+ logger.warning(f"Tool already exists: {key}")
76
+ self._tools[key] = tool
77
+ elif self.duplicate_behavior == "replace":
78
+ self._tools[key] = tool
79
+ elif self.duplicate_behavior == "error":
80
+ raise ValueError(f"Tool already exists: {key}")
81
+ elif self.duplicate_behavior == "ignore":
82
+ return existing
83
+ else:
84
+ self._tools[key] = tool
62
85
  return tool
63
86
 
64
87
  async def call_tool(
65
88
  self,
66
- name: str,
89
+ key: str,
67
90
  arguments: dict[str, Any],
68
91
  context: Context[ServerSessionT, LifespanContextT] | None = None,
69
- ) -> Any:
92
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
70
93
  """Call a tool by name with arguments."""
71
- tool = self.get_tool(name)
94
+ tool = self.get_tool(key)
72
95
  if not tool:
73
- raise ToolError(f"Unknown tool: {name}")
96
+ raise NotFoundError(f"Unknown tool: {key}")
74
97
 
75
98
  return await tool.run(arguments, context=context)
76
-
77
- def import_tools(
78
- self, tool_manager: ToolManager, prefix: str | None = None
79
- ) -> None:
80
- """
81
- Import all tools from another ToolManager with prefixed names.
82
-
83
- Args:
84
- tool_manager: Another ToolManager instance to import tools from
85
- prefix: Prefix to add to tool names, including the delimiter.
86
- The resulting tool name will be in the format "{prefix}{original_name}"
87
- if prefix is provided, otherwise the original name is used.
88
- For example, with prefix "weather/" and tool "forecast",
89
- the imported tool would be available as "weather/forecast"
90
- """
91
- for name, tool in tool_manager._tools.items():
92
- prefixed_name = f"{prefix}{name}" if prefix else name
93
-
94
- new_tool = copy.copy(tool)
95
- new_tool.name = prefixed_name
96
-
97
- # Store the copied tool
98
- self.add_tool(new_tool)
99
- logger.debug(f'Imported tool "{name}" as "{prefixed_name}"')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.1.2
3
+ Version: 2.2.1
4
4
  Summary: The fast, Pythonic way to build MCP servers.
5
5
  Project-URL: Homepage, https://gofastmcp.com
6
6
  Project-URL: Repository, https://github.com/jlowin/fastmcp
@@ -17,10 +17,11 @@ Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
18
  Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.10
20
- Requires-Dist: dotenv>=0.9.9
21
- Requires-Dist: fastapi>=0.115.12
20
+ Requires-Dist: exceptiongroup>=1.2.2
21
+ Requires-Dist: httpx>=0.28.1
22
22
  Requires-Dist: mcp<2.0.0,>=1.6.0
23
23
  Requires-Dist: openapi-pydantic>=0.5.1
24
+ Requires-Dist: python-dotenv>=1.1.0
24
25
  Requires-Dist: rich>=13.9.4
25
26
  Requires-Dist: typer>=0.15.2
26
27
  Requires-Dist: websockets>=15.0.1
@@ -94,6 +95,7 @@ FastMCP handles the complex protocol details and server management, letting you
94
95
  - [Proxy Servers](#proxy-servers)
95
96
  - [Composing MCP Servers](#composing-mcp-servers)
96
97
  - [OpenAPI \& FastAPI Generation](#openapi--fastapi-generation)
98
+ - [Handling `stderr`](#handling-stderr)
97
99
  - [Running Your Server](#running-your-server)
98
100
  - [Development Mode (Recommended for Building \& Testing)](#development-mode-recommended-for-building--testing)
99
101
  - [Claude Desktop Integration (For Regular Use)](#claude-desktop-integration-for-regular-use)
@@ -121,7 +123,7 @@ FastMCP provides a high-level, Pythonic interface for building and interacting w
121
123
 
122
124
  ## Why FastMCP?
123
125
 
124
- The MCP protocol is powerful but implementing it involves a lot of boilerplate - server setup, protocol handlers, content types, error management. FastMCP handles all the complex protocol details and server management, so you can focus on building great tools. Its designed to be high-level and Pythonic; in most cases, decorating a function is all you need.
126
+ The MCP protocol is powerful but implementing it involves a lot of boilerplate - server setup, protocol handlers, content types, error management. FastMCP handles all the complex protocol details and server management, so you can focus on building great tools. It's designed to be high-level and Pythonic; in most cases, decorating a function is all you need.
125
127
 
126
128
  FastMCP aims to be:
127
129
 
@@ -580,13 +582,13 @@ proxy_client = Client(
580
582
  )
581
583
 
582
584
  # Create a proxy server that connects to the client and exposes its capabilities
583
- proxy = FastMCP.as_proxy(proxy_client, name="Stdio-to-SSE Proxy")
585
+ proxy = FastMCP.from_client(proxy_client, name="Stdio-to-SSE Proxy")
584
586
 
585
587
  if __name__ == "__main__":
586
588
  proxy.run(transport='sse')
587
589
  ```
588
590
 
589
- `FastMCP.as_proxy` is an `async` classmethod. It connects to the target, discovers its capabilities, and dynamically builds the proxy server instance.
591
+ `FastMCP.from_client` is a class method that connects to the target, discovers its capabilities, and dynamically builds the proxy server instance.
590
592
 
591
593
 
592
594
 
@@ -694,6 +696,12 @@ mcp_server = FastMCP.from_openapi(openapi_spec, client=http_client)
694
696
  if __name__ == "__main__":
695
697
  mcp_server.run()
696
698
  ```
699
+
700
+ ### Handling `stderr`
701
+ The MCP spec allows for the server to write anything it wants to `stderr`, and it
702
+ doesn't specify the format in any way. FastMCP will forward the server's `stderr`
703
+ to the client's `stderr`.
704
+
697
705
  ## Running Your Server
698
706
 
699
707
  Choose the method that best suits your needs:
@@ -1,40 +1,40 @@
1
1
  fastmcp/__init__.py,sha256=2bwhjiyLJisyobp1O9tVYMjriHZAx_f4bIKJYOL-Rpk,399
2
- fastmcp/exceptions.py,sha256=9_KfANmugNdfZTCSZrohSeVmFmILZX5zz1LXI3wr_pw,508
2
+ fastmcp/exceptions.py,sha256=QKVHbftoZp4YZQ2NxA-t1SjztqspFdX95YTFOAmr5EE,640
3
3
  fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- fastmcp/settings.py,sha256=r5LT5yAZmwS1Lppro_fzdrCUJ0otnAmgtjsNgDdQIvs,1980
4
+ fastmcp/settings.py,sha256=VCjc-3011pKRYjt2h9rZ68XhVEekbpyLyVUREVBTSrg,1955
5
5
  fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
6
6
  fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
7
- fastmcp/cli/cli.py,sha256=M3BAkCjtnoGbyorUgaiqmCVSQg7S7_uEXerY-pqnOhk,13997
7
+ fastmcp/cli/cli.py,sha256=vdnJi_zz4ZYoPxp9xlJbh6RlGogaFY3icaOPzO_xsLE,14874
8
8
  fastmcp/client/__init__.py,sha256=BXO9NUhntZ5GnUACfaRCzDJ5IzxqFJs8qKG-CRMSco4,490
9
9
  fastmcp/client/base.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
- fastmcp/client/client.py,sha256=OMJ1FzrlcRRYKQg3DFxiQpaNDo4uYK3Acs23rxPyE-g,7941
10
+ fastmcp/client/client.py,sha256=1WxEaBqyAvYGhS6y_Xzei173Ufjd4rZ_REfbNFNWJnE,8033
11
11
  fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
12
12
  fastmcp/client/sampling.py,sha256=WdRhIZbWv54rXYI8lWHv0thXmGCloZYPFpwJK9El_sQ,1613
13
- fastmcp/client/transports.py,sha256=WVChsDV1UF0I3reiefsT3dipIh-P_K262TXpucwH-YY,14602
13
+ fastmcp/client/transports.py,sha256=lEE1PbqbntbFFKlVbrJtOpWzZa6N0_d1UXwixLgcpg0,15474
14
14
  fastmcp/prompts/__init__.py,sha256=LtPAv2JKIu54AwUd3iwv-HUd4DPcwgEqy6itEd3BH_E,194
15
- fastmcp/prompts/prompt.py,sha256=TCjIn0RyYsKDKjBYMuzSvmZPzigw4Y05EOvM0u_WBuk,5992
16
- fastmcp/prompts/prompt_manager.py,sha256=fX8Ckha0byC2VEgwRoM8_w8orNAkcdxEZRCd6oRtvo4,3533
15
+ fastmcp/prompts/prompt.py,sha256=mQ6iRnt7J8oKBUhlgPDYXnIzwwDNWCk4heTqCmv1sco,6622
16
+ fastmcp/prompts/prompt_manager.py,sha256=tMob9a-igjuzf6oTPLPGidFpJdg5JaPJVlYgyNkiCbE,2901
17
17
  fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
18
- fastmcp/resources/resource.py,sha256=qTAj6v_NrOhI_Hd1Mc6VfsmgSNFAxsBdCJTF9W-nU_s,1755
19
- fastmcp/resources/resource_manager.py,sha256=Ei1QthTUgjvSXIN-qXQeBd7aQiBNNsv0SY_lMtV9tfw,10997
20
- fastmcp/resources/template.py,sha256=_4JaMeYqtQYs1IO-lgBehaHJqp16G1pQ1Qe1f9YYoNg,3455
18
+ fastmcp/resources/resource.py,sha256=5FN2a7dpNwf7FSEYTNvQvkTxtodu1OPxSlJL-U-8yrM,2413
19
+ fastmcp/resources/resource_manager.py,sha256=_0itubfjYvfkA_wXKa4DQN5YpE7ejXhsE1hdt7m8XwU,9072
20
+ fastmcp/resources/template.py,sha256=Xed7mmCNHUPG2lR9YOZ2MJ1jLiHP_Cp8Osms0b69ExM,5761
21
21
  fastmcp/resources/types.py,sha256=tigil7z-SUJMakGXzDLIGSqTepPrAsRpwqwtBA4yoUY,6168
22
22
  fastmcp/server/__init__.py,sha256=pdkghG11VLMZiluQ-4_rl2JK1LMWmV003m9dDRUN8W4,92
23
23
  fastmcp/server/context.py,sha256=s1885AZRipKB3VltfaO3VEtMxGefKs8fdZByj-4tbNI,7120
24
- fastmcp/server/openapi.py,sha256=J7HrAlRziaB2a6pwB0wStbjRJ1E5Lf818yMqD762s5U,22693
25
- fastmcp/server/proxy.py,sha256=gYcoQFDIBraqWMOpWSsZLqefKjL_v0v74juLW1SU1AU,8058
26
- fastmcp/server/server.py,sha256=ryN7o7G1gNFE1NsAuZVc3WpcmsBtcKOo-mXACN5NCoc,28814
24
+ fastmcp/server/openapi.py,sha256=DVdUfs-rbBF_CIlxrI6HJ5aYbzuyDqGLAhT1TeyxwFc,22424
25
+ fastmcp/server/proxy.py,sha256=in-ZGwd7I8h7fITKMyHXaJRqODudn7MXsG0hVv9M0rA,8580
26
+ fastmcp/server/server.py,sha256=PFhnwa24diSKCz8KO39q43yuSHSbqYrzgnSspc-SPfg,31721
27
27
  fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
28
- fastmcp/tools/tool.py,sha256=pD3xOtryldu6lYsM5cOHnS05tJq9EwmQaoNUCZzLcoY,3598
29
- fastmcp/tools/tool_manager.py,sha256=p-L1KK8ecwP2psoyrcaSwHmeUKQZf-2pLPv6S_qgIcM,3525
28
+ fastmcp/tools/tool.py,sha256=FGihp_hzKLj4hK7EdHNUwe8o3NMzCngw4ftMmL_X4XI,5797
29
+ fastmcp/tools/tool_manager.py,sha256=hClv7fwj0cQSSwW0i-Swt7xiVqR4T9LVmr1Tp704nW4,3283
30
30
  fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
31
31
  fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
32
32
  fastmcp/utilities/func_metadata.py,sha256=uh-u3gAjLD4kCcGf0ZkZZwBTTl-84JuANZTnDqP5ztI,7841
33
33
  fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,1159
34
34
  fastmcp/utilities/openapi.py,sha256=PrH3usbTblaVC6jIH1UGiPEfgB2sSCLj33zA5dH7o_s,45193
35
35
  fastmcp/utilities/types.py,sha256=m2rPYMzO-ZFvvZ46N-1-Xqyw693K7yq9Z2xR4pVELyk,2091
36
- fastmcp-2.1.2.dist-info/METADATA,sha256=I3ePFWEbVbqKFh3UG6bnScng4t5WEhpbq3YNR-VET34,27467
37
- fastmcp-2.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- fastmcp-2.1.2.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
39
- fastmcp-2.1.2.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
40
- fastmcp-2.1.2.dist-info/RECORD,,
36
+ fastmcp-2.2.1.dist-info/METADATA,sha256=DSlO410ImcFq2nxjE-BfQaqzXLkkThKE9yLe-pTKA1k,27760
37
+ fastmcp-2.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ fastmcp-2.2.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
39
+ fastmcp-2.2.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
40
+ fastmcp-2.2.1.dist-info/RECORD,,