fastmcp 2.1.1__py3-none-any.whl → 2.2.0__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,15 +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
- from typing_extensions import Self
9
12
 
10
13
  from fastmcp.exceptions import ToolError
11
14
  from fastmcp.utilities.func_metadata import FuncMetadata, func_metadata
12
- from fastmcp.utilities.types import _convert_set_defaults
15
+ from fastmcp.utilities.types import Image, _convert_set_defaults
13
16
 
14
17
  if TYPE_CHECKING:
15
18
  from mcp.server.session import ServerSessionT
@@ -58,7 +61,7 @@ class Tool(BaseModel):
58
61
  is_async = inspect.iscoroutinefunction(fn)
59
62
 
60
63
  if context_kwarg is None:
61
- if isinstance(fn, classmethod):
64
+ if inspect.ismethod(fn) and hasattr(fn, "__func__"):
62
65
  sig = inspect.signature(fn.__func__)
63
66
  else:
64
67
  sig = inspect.signature(fn)
@@ -67,14 +70,16 @@ class Tool(BaseModel):
67
70
  context_kwarg = param_name
68
71
  break
69
72
 
73
+ # Use callable typing to ensure fn is treated as a callable despite being a classmethod
74
+ fn_callable: Callable[..., Any] = fn
70
75
  func_arg_metadata = func_metadata(
71
- fn,
76
+ fn_callable,
72
77
  skip_names=[context_kwarg] if context_kwarg is not None else [],
73
78
  )
74
79
  parameters = func_arg_metadata.arg_model.model_json_schema()
75
80
 
76
81
  return cls(
77
- fn=fn,
82
+ fn=fn_callable,
78
83
  name=func_name,
79
84
  description=func_doc,
80
85
  parameters=parameters,
@@ -88,10 +93,10 @@ class Tool(BaseModel):
88
93
  self,
89
94
  arguments: dict[str, Any],
90
95
  context: Context[ServerSessionT, LifespanContextT] | None = None,
91
- ) -> Any:
96
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
92
97
  """Run the tool with arguments."""
93
98
  try:
94
- return await self.fn_metadata.call_fn_with_arg_validation(
99
+ result = await self.fn_metadata.call_fn_with_arg_validation(
95
100
  self.fn,
96
101
  self.is_async,
97
102
  arguments,
@@ -99,17 +104,64 @@ class Tool(BaseModel):
99
104
  if self.context_kwarg is not None
100
105
  else None,
101
106
  )
107
+ return _convert_to_content(result)
102
108
  except Exception as e:
103
109
  raise ToolError(f"Error executing tool {self.name}: {e}") from e
104
110
 
105
- def copy(self, updates: dict[str, Any] | None = None) -> Self:
106
- """Copy the tool with optional updates."""
107
- data = self.model_dump()
108
- if updates:
109
- data.update(updates)
110
- return type(self)(**data)
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)
111
118
 
112
119
  def __eq__(self, other: object) -> bool:
113
120
  if not isinstance(other, Tool):
114
121
  return False
115
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)]
@@ -4,8 +4,9 @@ from collections.abc import Callable
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from mcp.shared.context import LifespanContextT
7
+ from mcp.types import EmbeddedResource, ImageContent, TextContent
7
8
 
8
- from fastmcp.exceptions import ToolError
9
+ from fastmcp.exceptions import NotFoundError
9
10
  from fastmcp.settings import DuplicateBehavior
10
11
  from fastmcp.tools.tool import Tool
11
12
  from fastmcp.utilities.logging import get_logger
@@ -21,17 +22,38 @@ logger = get_logger(__name__)
21
22
  class ToolManager:
22
23
  """Manages FastMCP tools."""
23
24
 
24
- def __init__(self, duplicate_behavior: DuplicateBehavior = DuplicateBehavior.WARN):
25
+ def __init__(self, duplicate_behavior: DuplicateBehavior | None = None):
25
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
+
26
38
  self.duplicate_behavior = duplicate_behavior
27
39
 
28
- def get_tool(self, name: str) -> Tool | None:
29
- """Get tool by name."""
30
- 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
31
53
 
32
54
  def list_tools(self) -> list[Tool]:
33
55
  """List all registered tools."""
34
- return list(self._tools.values())
56
+ return list(self.get_tools().values())
35
57
 
36
58
  def add_tool_from_fn(
37
59
  self,
@@ -44,53 +66,33 @@ class ToolManager:
44
66
  tool = Tool.from_function(fn, name=name, description=description, tags=tags)
45
67
  return self.add_tool(tool)
46
68
 
47
- def add_tool(self, tool: Tool) -> Tool:
69
+ def add_tool(self, tool: Tool, key: str | None = None) -> Tool:
48
70
  """Register a tool with the server."""
49
- existing = self._tools.get(tool.name)
71
+ key = key or tool.name
72
+ existing = self._tools.get(key)
50
73
  if existing:
51
- if self.duplicate_behavior == DuplicateBehavior.WARN:
52
- logger.warning(f"Tool already exists: {tool.name}")
53
- self._tools[tool.name] = tool
54
- elif self.duplicate_behavior == DuplicateBehavior.REPLACE:
55
- self._tools[tool.name] = tool
56
- elif self.duplicate_behavior == DuplicateBehavior.ERROR:
57
- raise ValueError(f"Tool already exists: {tool.name}")
58
- elif self.duplicate_behavior == DuplicateBehavior.IGNORE:
59
- pass
60
- 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
61
85
  return tool
62
86
 
63
87
  async def call_tool(
64
88
  self,
65
- name: str,
89
+ key: str,
66
90
  arguments: dict[str, Any],
67
91
  context: Context[ServerSessionT, LifespanContextT] | None = None,
68
- ) -> Any:
92
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
69
93
  """Call a tool by name with arguments."""
70
- tool = self.get_tool(name)
94
+ tool = self.get_tool(key)
71
95
  if not tool:
72
- raise ToolError(f"Unknown tool: {name}")
96
+ raise NotFoundError(f"Unknown tool: {key}")
73
97
 
74
98
  return await tool.run(arguments, context=context)
75
-
76
- def import_tools(
77
- self, tool_manager: ToolManager, prefix: str | None = None
78
- ) -> None:
79
- """
80
- Import all tools from another ToolManager with prefixed names.
81
-
82
- Args:
83
- tool_manager: Another ToolManager instance to import tools from
84
- prefix: Prefix to add to tool names, including the delimiter.
85
- The resulting tool name will be in the format "{prefix}{original_name}"
86
- if prefix is provided, otherwise the original name is used.
87
- For example, with prefix "weather/" and tool "forecast",
88
- the imported tool would be available as "weather/forecast"
89
- """
90
- for name, tool in tool_manager._tools.items():
91
- prefixed_name = f"{prefix}{name}" if prefix else name
92
-
93
- new_tool = tool.copy(updates=dict(name=prefixed_name))
94
- # Store the copied tool
95
- self.add_tool(new_tool)
96
- logger.debug(f'Imported tool "{name}" as "{prefixed_name}"')
@@ -20,15 +20,23 @@ def get_logger(name: str) -> logging.Logger:
20
20
 
21
21
 
22
22
  def configure_logging(
23
- level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO",
23
+ level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
24
24
  ) -> None:
25
25
  """Configure logging for FastMCP.
26
26
 
27
27
  Args:
28
28
  level: the log level to use
29
29
  """
30
- logging.basicConfig(
31
- level=level,
32
- format="%(message)s",
33
- handlers=[RichHandler(console=Console(stderr=True), rich_tracebacks=True)],
34
- )
30
+ # Only configure the FastMCP logger namespace
31
+ handler = RichHandler(console=Console(stderr=True), rich_tracebacks=True)
32
+ formatter = logging.Formatter("%(message)s")
33
+ handler.setFormatter(formatter)
34
+
35
+ fastmcp_logger = logging.getLogger("FastMCP")
36
+ fastmcp_logger.setLevel(level)
37
+
38
+ # Remove any existing handlers to avoid duplicates on reconfiguration
39
+ for hdlr in fastmcp_logger.handlers[:]:
40
+ fastmcp_logger.removeHandler(hdlr)
41
+
42
+ fastmcp_logger.addHandler(handler)
@@ -150,93 +150,6 @@ def _resolve_ref(
150
150
  return item
151
151
 
152
152
 
153
- def _extract_schema_as_dict(
154
- schema_obj: Schema | Reference, openapi: OpenAPI
155
- ) -> JsonSchema:
156
- """Resolves a schema/reference and returns it as a dictionary."""
157
- resolved_schema = _resolve_ref(schema_obj, openapi)
158
- if isinstance(resolved_schema, Schema):
159
- # Using exclude_none=True might be better than exclude_unset sometimes
160
- return resolved_schema.model_dump(mode="json", by_alias=True, exclude_none=True)
161
- elif isinstance(resolved_schema, dict):
162
- logger.warning(
163
- "Resolved schema reference resulted in a dict, not a Schema model."
164
- )
165
- return resolved_schema
166
- else:
167
- ref_str = getattr(schema_obj, "ref", "unknown")
168
- logger.warning(
169
- f"Expected Schema after resolving ref '{ref_str}', got {type(resolved_schema)}. Returning empty dict."
170
- )
171
- return {}
172
-
173
-
174
- def _convert_to_parameter_location(param_in: str) -> ParameterLocation:
175
- """Convert string parameter location to our ParameterLocation type."""
176
- if param_in == "path":
177
- return "path"
178
- elif param_in == "query":
179
- return "query"
180
- elif param_in == "header":
181
- return "header"
182
- elif param_in == "cookie":
183
- return "cookie"
184
- else:
185
- logger.warning(f"Unknown parameter location: {param_in}, defaulting to 'query'")
186
- return "query"
187
-
188
-
189
- def _extract_responses(
190
- operation_responses: dict[str, Response | Reference] | None,
191
- openapi: OpenAPI,
192
- ) -> dict[str, ResponseInfo]:
193
- """Extracts and resolves response information for an operation."""
194
- extracted_responses: dict[str, ResponseInfo] = {}
195
- if not operation_responses:
196
- return extracted_responses
197
-
198
- for status_code, resp_or_ref in operation_responses.items():
199
- try:
200
- response = cast(Response, _resolve_ref(resp_or_ref, openapi))
201
- if not isinstance(response, Response):
202
- ref_str = getattr(resp_or_ref, "ref", "unknown")
203
- logger.warning(
204
- f"Expected Response after resolving ref '{ref_str}' for status code {status_code}, got {type(response)}. Skipping."
205
- )
206
- continue
207
-
208
- content_schemas: dict[str, JsonSchema] = {}
209
- if response.content:
210
- for media_type_str, media_type_obj in response.content.items():
211
- if (
212
- isinstance(media_type_obj, MediaType)
213
- and media_type_obj.media_type_schema
214
- ):
215
- try:
216
- schema_dict = _extract_schema_as_dict(
217
- media_type_obj.media_type_schema, openapi
218
- )
219
- content_schemas[media_type_str] = schema_dict
220
- except ValueError as schema_err:
221
- logger.error(
222
- f"Failed to extract schema for media type '{media_type_str}' in response {status_code}: {schema_err}"
223
- )
224
-
225
- resp_info = ResponseInfo(
226
- description=response.description, content_schema=content_schemas
227
- )
228
- extracted_responses[str(status_code)] = resp_info
229
-
230
- except (ValidationError, ValueError, AttributeError) as e:
231
- ref_name = getattr(resp_or_ref, "ref", "unknown")
232
- logger.error(
233
- f"Failed to extract response for status code {status_code} (ref: '{ref_name}'): {e}",
234
- exc_info=False,
235
- )
236
-
237
- return extracted_responses
238
-
239
-
240
153
  # --- Main Parsing Function ---
241
154
  # (No changes needed in the main loop logic, only in the helpers it calls)
242
155
  def parse_openapi_to_http_routes(openapi_dict: dict[str, Any]) -> list[HTTPRoute]:
@@ -1,13 +1,25 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: The fast, Pythonic way to build MCP servers.
5
+ Project-URL: Homepage, https://gofastmcp.com
6
+ Project-URL: Repository, https://github.com/jlowin/fastmcp
7
+ Project-URL: Documentation, https://gofastmcp.com
5
8
  Author: Jeremiah Lowin
6
- License: Apache-2.0
9
+ License-Expression: Apache-2.0
7
10
  License-File: LICENSE
11
+ Keywords: agent,fastmcp,llm,mcp,mcp client,mcp server,model context protocol
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Typing :: Typed
8
19
  Requires-Python: >=3.10
9
20
  Requires-Dist: dotenv>=0.9.9
10
- Requires-Dist: fastapi>=0.115.12
21
+ Requires-Dist: exceptiongroup>=1.2.2
22
+ Requires-Dist: httpx>=0.28.1
11
23
  Requires-Dist: mcp<2.0.0,>=1.6.0
12
24
  Requires-Dist: openapi-pydantic>=0.5.1
13
25
  Requires-Dist: rich>=13.9.4
@@ -26,6 +38,7 @@ Description-Content-Type: text/markdown
26
38
  [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
27
39
  [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
28
40
 
41
+ <a href="https://trendshift.io/repositories/13266" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13266" alt="jlowin%2Ffastmcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
29
42
  </div>
30
43
 
31
44
  The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a new, standardized way to provide context and tools to your LLMs, and FastMCP makes building MCP servers and clients simple and intuitive. Create tools, expose resources, define prompts, and connect components with clean, Pythonic code.
@@ -109,7 +122,7 @@ FastMCP provides a high-level, Pythonic interface for building and interacting w
109
122
 
110
123
  ## Why FastMCP?
111
124
 
112
- 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.
125
+ 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.
113
126
 
114
127
  FastMCP aims to be:
115
128
 
@@ -568,13 +581,13 @@ proxy_client = Client(
568
581
  )
569
582
 
570
583
  # Create a proxy server that connects to the client and exposes its capabilities
571
- proxy = FastMCP.as_proxy(proxy_client, name="Stdio-to-SSE Proxy")
584
+ proxy = FastMCP.from_client(proxy_client, name="Stdio-to-SSE Proxy")
572
585
 
573
586
  if __name__ == "__main__":
574
587
  proxy.run(transport='sse')
575
588
  ```
576
589
 
577
- `FastMCP.as_proxy` is an `async` classmethod. It connects to the target, discovers its capabilities, and dynamically builds the proxy server instance.
590
+ `FastMCP.from_client` is a class method that connects to the target, discovers its capabilities, and dynamically builds the proxy server instance.
578
591
 
579
592
 
580
593
 
@@ -787,4 +800,4 @@ We use `ruff` via `pre-commit`.
787
800
 
788
801
  Please open an issue or discussion for questions or suggestions!
789
802
 
790
- </details>
803
+ </details>
@@ -0,0 +1,40 @@
1
+ fastmcp/__init__.py,sha256=2bwhjiyLJisyobp1O9tVYMjriHZAx_f4bIKJYOL-Rpk,399
2
+ fastmcp/exceptions.py,sha256=QKVHbftoZp4YZQ2NxA-t1SjztqspFdX95YTFOAmr5EE,640
3
+ fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ fastmcp/settings.py,sha256=VCjc-3011pKRYjt2h9rZ68XhVEekbpyLyVUREVBTSrg,1955
5
+ fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
6
+ fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
7
+ fastmcp/cli/cli.py,sha256=vdnJi_zz4ZYoPxp9xlJbh6RlGogaFY3icaOPzO_xsLE,14874
8
+ fastmcp/client/__init__.py,sha256=BXO9NUhntZ5GnUACfaRCzDJ5IzxqFJs8qKG-CRMSco4,490
9
+ fastmcp/client/base.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
+ fastmcp/client/client.py,sha256=1WxEaBqyAvYGhS6y_Xzei173Ufjd4rZ_REfbNFNWJnE,8033
11
+ fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
12
+ fastmcp/client/sampling.py,sha256=WdRhIZbWv54rXYI8lWHv0thXmGCloZYPFpwJK9El_sQ,1613
13
+ fastmcp/client/transports.py,sha256=lEE1PbqbntbFFKlVbrJtOpWzZa6N0_d1UXwixLgcpg0,15474
14
+ fastmcp/prompts/__init__.py,sha256=LtPAv2JKIu54AwUd3iwv-HUd4DPcwgEqy6itEd3BH_E,194
15
+ fastmcp/prompts/prompt.py,sha256=mQ6iRnt7J8oKBUhlgPDYXnIzwwDNWCk4heTqCmv1sco,6622
16
+ fastmcp/prompts/prompt_manager.py,sha256=tMob9a-igjuzf6oTPLPGidFpJdg5JaPJVlYgyNkiCbE,2901
17
+ fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
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
+ fastmcp/resources/types.py,sha256=tigil7z-SUJMakGXzDLIGSqTepPrAsRpwqwtBA4yoUY,6168
22
+ fastmcp/server/__init__.py,sha256=pdkghG11VLMZiluQ-4_rl2JK1LMWmV003m9dDRUN8W4,92
23
+ fastmcp/server/context.py,sha256=s1885AZRipKB3VltfaO3VEtMxGefKs8fdZByj-4tbNI,7120
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=izKw5m1nuabaFg4DeC26DYqytEq1GkqUMiuMoNVTUYI,31681
27
+ fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
28
+ fastmcp/tools/tool.py,sha256=FGihp_hzKLj4hK7EdHNUwe8o3NMzCngw4ftMmL_X4XI,5797
29
+ fastmcp/tools/tool_manager.py,sha256=hClv7fwj0cQSSwW0i-Swt7xiVqR4T9LVmr1Tp704nW4,3283
30
+ fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
31
+ fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
32
+ fastmcp/utilities/func_metadata.py,sha256=uh-u3gAjLD4kCcGf0ZkZZwBTTl-84JuANZTnDqP5ztI,7841
33
+ fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,1159
34
+ fastmcp/utilities/openapi.py,sha256=PrH3usbTblaVC6jIH1UGiPEfgB2sSCLj33zA5dH7o_s,45193
35
+ fastmcp/utilities/types.py,sha256=m2rPYMzO-ZFvvZ46N-1-Xqyw693K7yq9Z2xR4pVELyk,2091
36
+ fastmcp-2.2.0.dist-info/METADATA,sha256=MheLzFNF_LxSDXigYZ4MS5kswED3sFguTOACOe0RjUs,27497
37
+ fastmcp-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ fastmcp-2.2.0.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
39
+ fastmcp-2.2.0.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
40
+ fastmcp-2.2.0.dist-info/RECORD,,
@@ -1,40 +0,0 @@
1
- fastmcp/__init__.py,sha256=2bwhjiyLJisyobp1O9tVYMjriHZAx_f4bIKJYOL-Rpk,399
2
- fastmcp/exceptions.py,sha256=9_KfANmugNdfZTCSZrohSeVmFmILZX5zz1LXI3wr_pw,508
3
- fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- fastmcp/settings.py,sha256=r5LT5yAZmwS1Lppro_fzdrCUJ0otnAmgtjsNgDdQIvs,1980
5
- fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
6
- fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
7
- fastmcp/cli/cli.py,sha256=M3BAkCjtnoGbyorUgaiqmCVSQg7S7_uEXerY-pqnOhk,13997
8
- fastmcp/client/__init__.py,sha256=BXO9NUhntZ5GnUACfaRCzDJ5IzxqFJs8qKG-CRMSco4,490
9
- fastmcp/client/base.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
- fastmcp/client/client.py,sha256=OMJ1FzrlcRRYKQg3DFxiQpaNDo4uYK3Acs23rxPyE-g,7941
11
- fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
12
- fastmcp/client/sampling.py,sha256=WdRhIZbWv54rXYI8lWHv0thXmGCloZYPFpwJK9El_sQ,1613
13
- fastmcp/client/transports.py,sha256=WVChsDV1UF0I3reiefsT3dipIh-P_K262TXpucwH-YY,14602
14
- fastmcp/prompts/__init__.py,sha256=LtPAv2JKIu54AwUd3iwv-HUd4DPcwgEqy6itEd3BH_E,194
15
- fastmcp/prompts/prompt.py,sha256=wy5gHHiTeYDsDufrVlfJzej_A7lTT34WNI9jwsEaNn8,6268
16
- fastmcp/prompts/prompt_manager.py,sha256=HGwg8vsx1TVh9hBBWwuDdLm-M-FzBU5oB6Xu8nZC524,3505
17
- fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
18
- fastmcp/resources/resource.py,sha256=MrfRdLA2FEglvRJP7KgduG7na_qgkBo-_iXTzRbil6c,2038
19
- fastmcp/resources/resource_manager.py,sha256=fIre1GkXhOyWJNGM36qwYoZQC74aEznV4oXfLfCoKdw,10971
20
- fastmcp/resources/template.py,sha256=Fpjb51_ktWFpS1aQ5CFCt1SFuPe6S7CPuyzQCz7c3Mg,3742
21
- fastmcp/resources/types.py,sha256=tigil7z-SUJMakGXzDLIGSqTepPrAsRpwqwtBA4yoUY,6168
22
- fastmcp/server/__init__.py,sha256=pdkghG11VLMZiluQ-4_rl2JK1LMWmV003m9dDRUN8W4,92
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
27
- fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
28
- fastmcp/tools/tool.py,sha256=yPRqEM8sntDIbWPtZBy9evDg3BXupe0BKqnpvMwy7Sc,3872
29
- fastmcp/tools/tool_manager.py,sha256=oUT8ExxIKKpJnFxCUDwThRxdK7WADD40e6yjGBYDPmI,3498
30
- fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
31
- fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
32
- fastmcp/utilities/func_metadata.py,sha256=uh-u3gAjLD4kCcGf0ZkZZwBTTl-84JuANZTnDqP5ztI,7841
33
- fastmcp/utilities/logging.py,sha256=1ipiOXzgWUp3Vih_JtEiLX7aAFmrUDZNr4KrZbofZTM,818
34
- fastmcp/utilities/openapi.py,sha256=HiGCC5nh1sWtJRJ7DrFNhdjf1m-7SgyXWOE4nsRvyOc,48762
35
- fastmcp/utilities/types.py,sha256=m2rPYMzO-ZFvvZ46N-1-Xqyw693K7yq9Z2xR4pVELyk,2091
36
- fastmcp-2.1.1.dist-info/METADATA,sha256=Db4PK68mC7j-FCLfkUiyAg4hIaFgYho-X465u0c0sOs,26634
37
- fastmcp-2.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- fastmcp-2.1.1.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
39
- fastmcp-2.1.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
40
- fastmcp-2.1.1.dist-info/RECORD,,