fastmcp 2.2.5__py3-none-any.whl → 2.2.7__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.
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import (
4
+ asynccontextmanager,
5
+ )
6
+ from contextvars import ContextVar
7
+
8
+ from starlette.requests import Request
9
+
10
+ from fastmcp.utilities.logging import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ _current_starlette_request: ContextVar[Request | None] = ContextVar(
16
+ "starlette_request",
17
+ default=None,
18
+ )
19
+
20
+
21
+ @asynccontextmanager
22
+ async def starlette_request_context(request: Request):
23
+ token = _current_starlette_request.set(request)
24
+ try:
25
+ yield
26
+ finally:
27
+ _current_starlette_request.reset(token)
28
+
29
+
30
+ def get_current_starlette_request() -> Request | None:
31
+ return _current_starlette_request.get()
32
+
33
+
34
+ class RequestMiddleware:
35
+ """
36
+ Middleware that stores each request in a ContextVar
37
+ """
38
+
39
+ def __init__(self, app):
40
+ self.app = app
41
+
42
+ async def __call__(self, scope, receive, send):
43
+ async with starlette_request_context(Request(scope)):
44
+ await self.app(scope, receive, send)
@@ -1001,53 +1001,153 @@ def format_description_with_responses(
1001
1001
  responses: dict[
1002
1002
  str, Any
1003
1003
  ], # Changed from specific ResponseInfo type to avoid circular imports
1004
+ parameters: list[openapi.ParameterInfo] | None = None, # Add parameters parameter
1005
+ request_body: openapi.RequestBodyInfo | None = None, # Add request_body parameter
1004
1006
  ) -> str:
1005
- """Formats the base description string with response information."""
1006
- if not responses:
1007
- return base_description
1007
+ """
1008
+ Formats the base description string with response, parameter, and request body information.
1009
+
1010
+ Args:
1011
+ base_description (str): The initial description to be formatted.
1012
+ responses (dict[str, Any]): A dictionary of response information, keyed by status code.
1013
+ parameters (list[openapi.ParameterInfo] | None, optional): A list of parameter information,
1014
+ including path and query parameters. Each parameter includes details such as name,
1015
+ location, whether it is required, and a description.
1016
+ request_body (openapi.RequestBodyInfo | None, optional): Information about the request body,
1017
+ including its description, whether it is required, and its content schema.
1008
1018
 
1019
+ Returns:
1020
+ str: The formatted description string with additional details about responses, parameters,
1021
+ and the request body.
1022
+ """
1009
1023
  desc_parts = [base_description]
1010
- response_section = "\n\n**Responses:**"
1011
- added_response_section = False
1012
1024
 
1013
- # Determine success codes (common ones)
1014
- success_codes = {"200", "201", "202", "204"} # As strings
1015
- success_status = next((s for s in success_codes if s in responses), None)
1025
+ # Add parameter information
1026
+ if parameters:
1027
+ # Process path parameters
1028
+ path_params = [p for p in parameters if p.location == "path"]
1029
+ if path_params:
1030
+ param_section = "\n\n**Path Parameters:**"
1031
+ desc_parts.append(param_section)
1032
+ for param in path_params:
1033
+ required_marker = " (Required)" if param.required else ""
1034
+ param_desc = f"\n- **{param.name}**{required_marker}: {param.description or 'No description.'}"
1035
+ desc_parts.append(param_desc)
1036
+
1037
+ # Process query parameters
1038
+ query_params = [p for p in parameters if p.location == "query"]
1039
+ if query_params:
1040
+ param_section = "\n\n**Query Parameters:**"
1041
+ desc_parts.append(param_section)
1042
+ for param in query_params:
1043
+ required_marker = " (Required)" if param.required else ""
1044
+ param_desc = f"\n- **{param.name}**{required_marker}: {param.description or 'No description.'}"
1045
+ desc_parts.append(param_desc)
1046
+
1047
+ # Add request body information if present
1048
+ if request_body and request_body.description:
1049
+ req_body_section = "\n\n**Request Body:**"
1050
+ desc_parts.append(req_body_section)
1051
+ required_marker = " (Required)" if request_body.required else ""
1052
+ desc_parts.append(f"\n{request_body.description}{required_marker}")
1053
+
1054
+ # Add request body property descriptions if available
1055
+ if request_body.content_schema:
1056
+ media_type = (
1057
+ "application/json"
1058
+ if "application/json" in request_body.content_schema
1059
+ else next(iter(request_body.content_schema), None)
1060
+ )
1061
+ if media_type:
1062
+ schema = request_body.content_schema.get(media_type, {})
1063
+ if isinstance(schema, dict) and "properties" in schema:
1064
+ desc_parts.append("\n\n**Request Properties:**")
1065
+ for prop_name, prop_schema in schema["properties"].items():
1066
+ if (
1067
+ isinstance(prop_schema, dict)
1068
+ and "description" in prop_schema
1069
+ ):
1070
+ required = prop_name in schema.get("required", [])
1071
+ req_mark = " (Required)" if required else ""
1072
+ desc_parts.append(
1073
+ f"\n- **{prop_name}**{req_mark}: {prop_schema['description']}"
1074
+ )
1016
1075
 
1017
- # Process all responses
1018
- responses_to_process = responses.items()
1076
+ # Add response information
1077
+ if responses:
1078
+ response_section = "\n\n**Responses:**"
1079
+ added_response_section = False
1019
1080
 
1020
- for status_code, resp_info in sorted(responses_to_process):
1021
- if not added_response_section:
1022
- desc_parts.append(response_section)
1023
- added_response_section = True
1081
+ # Determine success codes (common ones)
1082
+ success_codes = {"200", "201", "202", "204"} # As strings
1083
+ success_status = next((s for s in success_codes if s in responses), None)
1024
1084
 
1025
- status_marker = " (Success)" if status_code == success_status else ""
1026
- desc_parts.append(
1027
- f"\n- **{status_code}**{status_marker}: {resp_info.description or 'No description.'}"
1028
- )
1085
+ # Process all responses
1086
+ responses_to_process = responses.items()
1029
1087
 
1030
- # Process content schemas for this response
1031
- if resp_info.content_schema:
1032
- # Prioritize json, then take first available
1033
- media_type = (
1034
- "application/json"
1035
- if "application/json" in resp_info.content_schema
1036
- else next(iter(resp_info.content_schema), None)
1088
+ for status_code, resp_info in sorted(responses_to_process):
1089
+ if not added_response_section:
1090
+ desc_parts.append(response_section)
1091
+ added_response_section = True
1092
+
1093
+ status_marker = " (Success)" if status_code == success_status else ""
1094
+ desc_parts.append(
1095
+ f"\n- **{status_code}**{status_marker}: {resp_info.description or 'No description.'}"
1037
1096
  )
1038
1097
 
1039
- if media_type:
1040
- schema = resp_info.content_schema.get(media_type)
1041
- desc_parts.append(f" - Content-Type: `{media_type}`")
1098
+ # Process content schemas for this response
1099
+ if resp_info.content_schema:
1100
+ # Prioritize json, then take first available
1101
+ media_type = (
1102
+ "application/json"
1103
+ if "application/json" in resp_info.content_schema
1104
+ else next(iter(resp_info.content_schema), None)
1105
+ )
1106
+
1107
+ if media_type:
1108
+ schema = resp_info.content_schema.get(media_type)
1109
+ desc_parts.append(f" - Content-Type: `{media_type}`")
1110
+
1111
+ # Add response property descriptions
1112
+ if isinstance(schema, dict):
1113
+ # Handle array responses
1114
+ if schema.get("type") == "array" and "items" in schema:
1115
+ items_schema = schema["items"]
1116
+ if (
1117
+ isinstance(items_schema, dict)
1118
+ and "properties" in items_schema
1119
+ ):
1120
+ desc_parts.append("\n - **Response Item Properties:**")
1121
+ for prop_name, prop_schema in items_schema[
1122
+ "properties"
1123
+ ].items():
1124
+ if (
1125
+ isinstance(prop_schema, dict)
1126
+ and "description" in prop_schema
1127
+ ):
1128
+ desc_parts.append(
1129
+ f"\n - **{prop_name}**: {prop_schema['description']}"
1130
+ )
1131
+ # Handle object responses
1132
+ elif "properties" in schema:
1133
+ desc_parts.append("\n - **Response Properties:**")
1134
+ for prop_name, prop_schema in schema["properties"].items():
1135
+ if (
1136
+ isinstance(prop_schema, dict)
1137
+ and "description" in prop_schema
1138
+ ):
1139
+ desc_parts.append(
1140
+ f"\n - **{prop_name}**: {prop_schema['description']}"
1141
+ )
1042
1142
 
1043
- if schema:
1044
1143
  # Generate Example
1045
- example = generate_example_from_schema(schema)
1046
- if example != "unknown_type" and example is not None:
1047
- desc_parts.append("\n - **Example:**")
1048
- desc_parts.append(
1049
- format_json_for_description(example, indent=2)
1050
- )
1144
+ if schema:
1145
+ example = generate_example_from_schema(schema)
1146
+ if example != "unknown_type" and example is not None:
1147
+ desc_parts.append("\n - **Example:**")
1148
+ desc_parts.append(
1149
+ format_json_for_description(example, indent=2)
1150
+ )
1051
1151
 
1052
1152
  return "\n".join(desc_parts)
1053
1153
 
@@ -1069,7 +1169,15 @@ def _combine_schemas(route: openapi.HTTPRoute) -> dict[str, Any]:
1069
1169
  for param in route.parameters:
1070
1170
  if param.required:
1071
1171
  required.append(param.name)
1072
- properties[param.name] = param.schema_
1172
+
1173
+ # Copy the schema and add description if available
1174
+ param_schema = param.schema_.copy() if isinstance(param.schema_, dict) else {}
1175
+
1176
+ # Add parameter description to schema if available and not already present
1177
+ if param.description and not param_schema.get("description"):
1178
+ param_schema["description"] = param.description
1179
+
1180
+ properties[param.name] = param_schema
1073
1181
 
1074
1182
  # Add request body if it exists
1075
1183
  if route.request_body and route.request_body.content_schema:
@@ -1077,8 +1185,11 @@ def _combine_schemas(route: openapi.HTTPRoute) -> dict[str, Any]:
1077
1185
  content_type = next(iter(route.request_body.content_schema))
1078
1186
  body_schema = route.request_body.content_schema[content_type]
1079
1187
  body_props = body_schema.get("properties", {})
1188
+
1189
+ # Add request body properties
1080
1190
  for prop_name, prop_schema in body_props.items():
1081
1191
  properties[prop_name] = prop_schema
1192
+
1082
1193
  if route.request_body.required:
1083
1194
  required.extend(body_schema.get("required", []))
1084
1195
 
@@ -2,13 +2,41 @@
2
2
 
3
3
  import base64
4
4
  from pathlib import Path
5
- from typing import TypeVar
5
+ from types import UnionType
6
+ from typing import Annotated, TypeVar, Union, get_args, get_origin
6
7
 
7
8
  from mcp.types import ImageContent
8
9
 
9
10
  T = TypeVar("T")
10
11
 
11
12
 
13
+ def issubclass_safe(cls: type, base: type) -> bool:
14
+ """Check if cls is a subclass of base, even if cls is a type variable."""
15
+ try:
16
+ if origin := get_origin(cls):
17
+ return issubclass_safe(origin, base)
18
+ return issubclass(cls, base)
19
+ except TypeError:
20
+ return False
21
+
22
+
23
+ def is_class_member_of_type(cls: type, base: type) -> bool:
24
+ """Check if cls is a member of base, even if cls is a type variable."""
25
+ origin = get_origin(cls)
26
+ # Handle both types of unions: UnionType (from types module, used with | syntax)
27
+ # and typing.Union (used with Union[] syntax)
28
+ if origin is UnionType or origin == Union:
29
+ return any(is_class_member_of_type(arg, base) for arg in get_args(cls))
30
+ elif origin is Annotated:
31
+ # For Annotated[T, ...], check if T is a member of base
32
+ args = get_args(cls)
33
+ if args:
34
+ return is_class_member_of_type(args[0], base)
35
+ return False
36
+ else:
37
+ return issubclass_safe(cls, base)
38
+
39
+
12
40
  def _convert_set_defaults(maybe_set: set[T] | list[T] | None) -> set[T]:
13
41
  """Convert a set or list to a set, defaulting to an empty set if None."""
14
42
  if maybe_set is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.2.5
3
+ Version: 2.2.7
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
@@ -19,12 +19,12 @@ Classifier: Typing :: Typed
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: exceptiongroup>=1.2.2
21
21
  Requires-Dist: httpx>=0.28.1
22
- Requires-Dist: mcp<2.0.0,>=1.6.0
22
+ Requires-Dist: mcp<2.0.0,>=1.7.1
23
23
  Requires-Dist: openapi-pydantic>=0.5.1
24
24
  Requires-Dist: python-dotenv>=1.1.0
25
25
  Requires-Dist: rich>=13.9.4
26
26
  Requires-Dist: typer>=0.15.2
27
- Requires-Dist: websockets>=15.0.1
27
+ Requires-Dist: websockets>=14.0
28
28
  Description-Content-Type: text/markdown
29
29
 
30
30
  <div align="center">
@@ -789,7 +789,7 @@ Contributions make the open-source community vibrant! We welcome improvements an
789
789
 
790
790
  Run the test suite:
791
791
  ```bash
792
- uv run pytest -vv
792
+ uv run --frozen pytest -vv
793
793
  ```
794
794
 
795
795
  #### Formatting & Linting
@@ -1,49 +1,51 @@
1
- fastmcp/__init__.py,sha256=2bwhjiyLJisyobp1O9tVYMjriHZAx_f4bIKJYOL-Rpk,399
1
+ fastmcp/__init__.py,sha256=e-wu5UQpgduOauB-H8lzbnxv_H9K90fCJVnc1qgaAhM,413
2
2
  fastmcp/exceptions.py,sha256=QKVHbftoZp4YZQ2NxA-t1SjztqspFdX95YTFOAmr5EE,640
3
3
  fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- fastmcp/settings.py,sha256=VCjc-3011pKRYjt2h9rZ68XhVEekbpyLyVUREVBTSrg,1955
4
+ fastmcp/settings.py,sha256=Ix_VrAegM27wpPViCJvC04dmPm2QsBgtbxcZXHGQOhM,2213
5
5
  fastmcp/cli/__init__.py,sha256=Ii284TNoG5lxTP40ETMGhHEq3lQZWxu9m9JuU57kUpQ,87
6
6
  fastmcp/cli/claude.py,sha256=IAlcZ4qZKBBj09jZUMEx7EANZE_IR3vcu7zOBJmMOuU,4567
7
7
  fastmcp/cli/cli.py,sha256=wsFIYTv48_nr0mcW8vjI1l7_thr4cOrRxzo9g-qr2l8,15726
8
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=XXpN28epV9N5w-keQSDPBCdLFtZ5EPK2msxuQ6PxTmo,7732
9
+ fastmcp/client/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ fastmcp/client/client.py,sha256=hzf7YZDKkRimkhsvl2c3GpN_0WVnteJnyPn_JP_jW1A,15333
11
+ fastmcp/client/logging.py,sha256=Q8jYcZj4KA15Yiz3RP8tBXj8sd9IxL3VThF_Y0O4Upc,356
11
12
  fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
12
- fastmcp/client/sampling.py,sha256=WdRhIZbWv54rXYI8lWHv0thXmGCloZYPFpwJK9El_sQ,1613
13
- fastmcp/client/transports.py,sha256=7dJUQemdxj6UHNThizPzSJbHTGiJHlM77vLf4X9g11M,15491
13
+ fastmcp/client/sampling.py,sha256=UlDHxnd6k_HoU8RA3ob0g8-e6haJBc9u27N_v291QoI,1698
14
+ fastmcp/client/transports.py,sha256=FMMniyxnaaHyWeFHXXP_ayOpb4jtCoyjaiqLobgYb_M,16885
14
15
  fastmcp/contrib/README.md,sha256=rKknYSI1T192UvSszqwwDlQ2eYQpxywrNTLoj177SYU,878
15
16
  fastmcp/contrib/bulk_tool_caller/README.md,sha256=5aUUY1TSFKtz1pvTLSDqkUCkGkuqMfMZNsLeaNqEgAc,1960
16
17
  fastmcp/contrib/bulk_tool_caller/__init__.py,sha256=xvGSSaUXTQrc31erBoi1Gh7BikgOliETDiYVTP3rLxY,75
17
- fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py,sha256=BlZnZRY_l4X5VnrIeMWzmQM2lRsQPMcnIafKIl4GXxE,4216
18
+ fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py,sha256=2NcrGS59qvHo1lfbRaT8NSWfCxN66knciLxFvnGwCLY,4165
18
19
  fastmcp/contrib/bulk_tool_caller/example.py,sha256=3RdsU2KrRwYZHEdVAmHOGJsO3ZJBxSaqz8BTznkPg7Y,321
19
20
  fastmcp/contrib/mcp_mixin/README.md,sha256=9DDTJXWkA3yv1fp5V58gofmARPQ2xWDhblYGvUhKpDQ,1689
20
21
  fastmcp/contrib/mcp_mixin/__init__.py,sha256=aw9IQ1ssNjCgws4ZNt8bkdpossAAGVAwwjBpMp9O5ZQ,153
21
22
  fastmcp/contrib/mcp_mixin/example.py,sha256=GnunkXmtG5hLLTUsM8aW5ZURU52Z8vI4tNLl-fK7Dg0,1228
22
23
  fastmcp/contrib/mcp_mixin/mcp_mixin.py,sha256=cfIRbnSxsVzglTD-auyTE0izVQeHP7Oz18qzYoBZJgg,7899
23
24
  fastmcp/prompts/__init__.py,sha256=LtPAv2JKIu54AwUd3iwv-HUd4DPcwgEqy6itEd3BH_E,194
24
- fastmcp/prompts/prompt.py,sha256=xNSlvs-vRB-kz7xnMYf2RwkiilbE0HcOoXgMS6gUogk,7974
25
+ fastmcp/prompts/prompt.py,sha256=0RWTlsE3xxRq1falPkZhKosb9Ugo6cVqzLR6wxQ5ebU,8087
25
26
  fastmcp/prompts/prompt_manager.py,sha256=4CInlSWASjHUfGu2i0ig2ZICzHHxCMFGTXhuXgYdukQ,3237
26
27
  fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
27
28
  fastmcp/resources/resource.py,sha256=GGG0XugoIMbgAJRMVxsBcZLbt19W3COy8PyTh_uoWjs,2705
28
29
  fastmcp/resources/resource_manager.py,sha256=yfQNCEUooZiQ8LNslnAzjQp4Vh-y1YOlFyGEQZ0BtAg,9586
29
- fastmcp/resources/template.py,sha256=oa85KiuTjh3C7aZvMwmO4fwbTi6IvwlQ3fxizJuv3dk,7261
30
- fastmcp/resources/types.py,sha256=c1z6BQSosgrlPQ3v67DuXCvDjCJMq9Xl45npEpyk0ik,7710
30
+ fastmcp/resources/template.py,sha256=0g_3Yif9XYrZW3SpbrdoHVJ13Uk7ZJBXcXo_WsAhfl0,7322
31
+ fastmcp/resources/types.py,sha256=ht0Jzo-CQwmmJ1XIz2VaCDETsUcGbAiN81wm1uxDSmk,7559
31
32
  fastmcp/server/__init__.py,sha256=pdkghG11VLMZiluQ-4_rl2JK1LMWmV003m9dDRUN8W4,92
32
- fastmcp/server/context.py,sha256=s1885AZRipKB3VltfaO3VEtMxGefKs8fdZByj-4tbNI,7120
33
- fastmcp/server/openapi.py,sha256=hFMOVe-bzudxP8SE-CqQhUWlUCVF5inGfMVL28HlqDs,21179
34
- fastmcp/server/proxy.py,sha256=xOufto2gIfLk2BZfjhpLdZOlKDlJk5Rn6hCP0pzvaCU,10110
35
- fastmcp/server/server.py,sha256=89RreIOw0siZmc6SlVlYWm6d6cFvjfVPY7mczXCNGFM,33032
33
+ fastmcp/server/context.py,sha256=X8mBdF7SPwH_x8qG4oDoZBPZO9ibYFLjZu_pZoQvafg,7858
34
+ fastmcp/server/openapi.py,sha256=XN6xI3qz6YYN42gToO3nBp2a7sEkbFVegeUfOPyCJh0,24221
35
+ fastmcp/server/proxy.py,sha256=296gGXkmhaBaQzn0WYNZay4mFbjEhRX_QLB0my1W3IU,10169
36
+ fastmcp/server/server.py,sha256=fksGETa-kPWXIfH4Cj55uyd088kbKRadJVMiwHymTOA,42947
36
37
  fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
37
- fastmcp/tools/tool.py,sha256=k797XAeXdcUuBfeuvxkEy8xckXi7xSzQVgkzL876rBQ,6755
38
- fastmcp/tools/tool_manager.py,sha256=hClv7fwj0cQSSwW0i-Swt7xiVqR4T9LVmr1Tp704nW4,3283
38
+ fastmcp/tools/tool.py,sha256=J1tSgXiyv6tkugYetW3QwHJ5oDOxdrg5UaRYf_RngEI,6759
39
+ fastmcp/tools/tool_manager.py,sha256=whi3oYfTqUXC0vXWvSvGBaUqwo1YKZxSI-clRAKKnWQ,3606
39
40
  fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
40
41
  fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
41
42
  fastmcp/utilities/func_metadata.py,sha256=iYXnx7MILOSL8mUQ6Rtq_6U7qA08OkoEN2APY802hJg,8141
43
+ fastmcp/utilities/http.py,sha256=EzOe38e2oC0SjAnLmcf__1AjEtF19px8ZIVxPLt3Cr0,991
42
44
  fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,1159
43
- fastmcp/utilities/openapi.py,sha256=PrH3usbTblaVC6jIH1UGiPEfgB2sSCLj33zA5dH7o_s,45193
44
- fastmcp/utilities/types.py,sha256=m2rPYMzO-ZFvvZ46N-1-Xqyw693K7yq9Z2xR4pVELyk,2091
45
- fastmcp-2.2.5.dist-info/METADATA,sha256=JKwmnn7QiN_KbNkC3ZyTpTEoUOTIDHPoCZFraw3eMR8,27769
46
- fastmcp-2.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
- fastmcp-2.2.5.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
48
- fastmcp-2.2.5.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
49
- fastmcp-2.2.5.dist-info/RECORD,,
45
+ fastmcp/utilities/openapi.py,sha256=Er3G1MyFwiWVxZXicXtD2j-BvttHEDTi1dgkq1KiBQc,51073
46
+ fastmcp/utilities/types.py,sha256=ZcPRtm_4U7Veh1vmW0uGj0TN66FWROwyzKEIcYE_758,3167
47
+ fastmcp-2.2.7.dist-info/METADATA,sha256=rIbdMviw7gLEA-QGO-3jk50CewCTyev1FQEC04JNzoY,27776
48
+ fastmcp-2.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
49
+ fastmcp-2.2.7.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
50
+ fastmcp-2.2.7.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
51
+ fastmcp-2.2.7.dist-info/RECORD,,