fastmcp 2.3.3__py3-none-any.whl → 2.3.4__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.
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import copy
4
- from collections.abc import Mapping, Sequence
5
4
 
6
5
 
7
6
  def _prune_param(schema: dict, param: str) -> dict:
@@ -14,6 +13,7 @@ def _prune_param(schema: dict, param: str) -> dict:
14
13
  removed = props.pop(param, None)
15
14
  if removed is None: # nothing to do
16
15
  return schema
16
+
17
17
  # Keep empty properties object rather than removing it entirely
18
18
  schema["properties"] = props
19
19
  if param in schema.get("required", []):
@@ -21,39 +21,100 @@ def _prune_param(schema: dict, param: str) -> dict:
21
21
  if not schema["required"]:
22
22
  schema.pop("required")
23
23
 
24
- # ── 2. collect all remaining local $ref targets ───────────────────
24
+ return schema
25
+
26
+
27
+ def _walk_and_prune(
28
+ schema: dict,
29
+ prune_defs: bool = False,
30
+ prune_titles: bool = False,
31
+ prune_additional_properties: bool = False,
32
+ ) -> dict:
33
+ """Walk the schema and optionally prune titles, unused definitions, and additionalProperties: false."""
34
+
35
+ # Will only be used if prune_defs is True
25
36
  used_defs: set[str] = set()
26
37
 
27
- def walk(node: object) -> None: # depth-first traversal
28
- if isinstance(node, Mapping):
29
- ref = node.get("$ref")
30
- if isinstance(ref, str) and ref.startswith("#/$defs/"):
31
- used_defs.add(ref.split("/")[-1])
38
+ def walk(node: object) -> None:
39
+ if isinstance(node, dict):
40
+ # Process $ref for definition tracking
41
+ if prune_defs:
42
+ ref = node.get("$ref")
43
+ if isinstance(ref, str) and ref.startswith("#/$defs/"):
44
+ used_defs.add(ref.split("/")[-1])
45
+
46
+ # Remove title if requested
47
+ if prune_titles and "title" in node:
48
+ node.pop("title")
49
+
50
+ # Remove additionalProperties: false at any level if requested
51
+ if (
52
+ prune_additional_properties
53
+ and node.get("additionalProperties", None) is False
54
+ ):
55
+ node.pop("additionalProperties")
56
+
57
+ # Walk children
32
58
  for v in node.values():
33
59
  walk(v)
34
- elif isinstance(node, Sequence) and not isinstance(node, str | bytes):
60
+
61
+ elif isinstance(node, list):
35
62
  for v in node:
36
63
  walk(v)
37
64
 
65
+ # Traverse the schema once
38
66
  walk(schema)
39
67
 
40
- # ── 3. remove orphaned definitions ────────────────────────────────
41
- defs = schema.get("$defs", {})
42
- for def_name in list(defs):
43
- if def_name not in used_defs:
44
- defs.pop(def_name)
45
- if not defs:
46
- schema.pop("$defs", None)
68
+ # Remove orphaned definitions if requested
69
+ if prune_defs:
70
+ defs = schema.get("$defs", {})
71
+ for def_name in list(defs):
72
+ if def_name not in used_defs:
73
+ defs.pop(def_name)
74
+ if not defs:
75
+ schema.pop("$defs", None)
76
+
77
+ return schema
78
+
47
79
 
80
+ def _prune_additional_properties(schema: dict) -> dict:
81
+ """Remove additionalProperties from the schema if it is False."""
82
+ if schema.get("additionalProperties", None) is False:
83
+ schema.pop("additionalProperties")
48
84
  return schema
49
85
 
50
86
 
51
- def prune_params(schema: dict, params: list[str]) -> dict:
87
+ def compress_schema(
88
+ schema: dict,
89
+ prune_params: list[str] | None = None,
90
+ prune_defs: bool = True,
91
+ prune_additional_properties: bool = True,
92
+ prune_titles: bool = False,
93
+ ) -> dict:
52
94
  """
53
95
  Remove the given parameters from the schema.
54
96
 
97
+ Args:
98
+ schema: The schema to compress
99
+ prune_params: List of parameter names to remove from properties
100
+ prune_defs: Whether to remove unused definitions
101
+ prune_additional_properties: Whether to remove additionalProperties: false
102
+ prune_titles: Whether to remove title fields from the schema
55
103
  """
104
+ # Make a copy so we don't modify the original
56
105
  schema = copy.deepcopy(schema)
57
- for param in params:
106
+
107
+ # Remove specific parameters if requested
108
+ for param in prune_params or []:
58
109
  schema = _prune_param(schema, param=param)
110
+
111
+ # Do a single walk to handle pruning operations
112
+ if prune_defs or prune_titles or prune_additional_properties:
113
+ schema = _walk_and_prune(
114
+ schema,
115
+ prune_defs=prune_defs,
116
+ prune_titles=prune_titles,
117
+ prune_additional_properties=prune_additional_properties,
118
+ )
119
+
59
120
  return schema
@@ -21,22 +21,27 @@ def get_logger(name: str) -> logging.Logger:
21
21
 
22
22
  def configure_logging(
23
23
  level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] | int = "INFO",
24
+ logger: logging.Logger | None = None,
24
25
  ) -> None:
25
- """Configure logging for FastMCP.
26
+ """
27
+ Configure logging for FastMCP.
26
28
 
27
29
  Args:
30
+ logger: the logger to configure
28
31
  level: the log level to use
29
32
  """
33
+ if logger is None:
34
+ logger = logging.getLogger("FastMCP")
35
+
30
36
  # Only configure the FastMCP logger namespace
31
37
  handler = RichHandler(console=Console(stderr=True), rich_tracebacks=True)
32
38
  formatter = logging.Formatter("%(message)s")
33
39
  handler.setFormatter(formatter)
34
40
 
35
- fastmcp_logger = logging.getLogger("FastMCP")
36
- fastmcp_logger.setLevel(level)
41
+ logger.setLevel(level)
37
42
 
38
43
  # Remove any existing handlers to avoid duplicates on reconfiguration
39
- for hdlr in fastmcp_logger.handlers[:]:
40
- fastmcp_logger.removeHandler(hdlr)
44
+ for hdlr in logger.handlers[:]:
45
+ logger.removeHandler(hdlr)
41
46
 
42
- fastmcp_logger.addHandler(handler)
47
+ logger.addHandler(handler)
@@ -84,6 +84,9 @@ class HTTPRoute(BaseModel):
84
84
  responses: dict[str, ResponseInfo] = Field(
85
85
  default_factory=dict
86
86
  ) # Key: status code str
87
+ schema_definitions: dict[str, JsonSchema] = Field(
88
+ default_factory=dict
89
+ ) # Store component schemas
87
90
 
88
91
 
89
92
  # Export public symbols
@@ -221,6 +224,27 @@ class OpenAPI31Parser(BaseOpenAPIParser):
221
224
  logger.warning("OpenAPI schema has no paths defined.")
222
225
  return []
223
226
 
227
+ # Extract component schemas to add to each route
228
+ schema_definitions = {}
229
+ if hasattr(self.openapi, "components") and self.openapi.components:
230
+ components = self.openapi.components
231
+ if hasattr(components, "schemas") and components.schemas:
232
+ for name, schema in components.schemas.items():
233
+ try:
234
+ if isinstance(schema, Reference):
235
+ resolved_schema = self._resolve_ref(schema)
236
+ schema_definitions[name] = self._extract_schema_as_dict(
237
+ resolved_schema
238
+ )
239
+ else:
240
+ schema_definitions[name] = self._extract_schema_as_dict(
241
+ schema
242
+ )
243
+ except Exception as e:
244
+ logger.warning(
245
+ f"Failed to extract schema definition '{name}': {e}"
246
+ )
247
+
224
248
  for path_str, path_item_obj in self.openapi.paths.items():
225
249
  if not isinstance(path_item_obj, PathItem):
226
250
  logger.warning(
@@ -269,6 +293,7 @@ class OpenAPI31Parser(BaseOpenAPIParser):
269
293
  parameters=parameters,
270
294
  request_body=request_body_info,
271
295
  responses=responses,
296
+ schema_definitions=schema_definitions,
272
297
  )
273
298
  routes.append(route)
274
299
  logger.info(
@@ -386,16 +411,36 @@ class OpenAPI31Parser(BaseOpenAPIParser):
386
411
 
387
412
  param_schema_dict = {}
388
413
  if param_schema_obj: # Check if schema exists
414
+ # Resolve the schema if it's a reference
415
+ resolved_schema = self._resolve_ref(param_schema_obj)
389
416
  param_schema_dict = self._extract_schema_as_dict(param_schema_obj)
417
+
418
+ # Ensure default value is preserved from resolved schema
419
+ if (
420
+ not isinstance(resolved_schema, Reference)
421
+ and hasattr(resolved_schema, "default")
422
+ and resolved_schema.default is not None
423
+ ):
424
+ param_schema_dict["default"] = resolved_schema.default
390
425
  elif parameter.content:
391
426
  # Handle complex parameters with 'content'
392
427
  first_media_type = next(iter(parameter.content.values()), None)
393
428
  if (
394
429
  first_media_type and first_media_type.media_type_schema
395
430
  ): # CORRECTED: Use 'media_type_schema'
396
- param_schema_dict = self._extract_schema_as_dict(
397
- first_media_type.media_type_schema
398
- )
431
+ # Resolve the schema if it's a reference
432
+ media_schema = first_media_type.media_type_schema
433
+ resolved_media_schema = self._resolve_ref(media_schema)
434
+ param_schema_dict = self._extract_schema_as_dict(media_schema)
435
+
436
+ # Ensure default value is preserved from resolved schema
437
+ if (
438
+ not isinstance(resolved_media_schema, Reference)
439
+ and hasattr(resolved_media_schema, "default")
440
+ and resolved_media_schema.default is not None
441
+ ):
442
+ param_schema_dict["default"] = resolved_media_schema.default
443
+
399
444
  logger.debug(
400
445
  f"Parameter '{parameter.name}' using schema from 'content' field."
401
446
  )
@@ -543,6 +588,27 @@ class OpenAPI30Parser(BaseOpenAPIParser):
543
588
  logger.warning("OpenAPI schema has no paths defined.")
544
589
  return []
545
590
 
591
+ # Extract component schemas to add to each route
592
+ schema_definitions = {}
593
+ if hasattr(self.openapi, "components") and self.openapi.components:
594
+ components = self.openapi.components
595
+ if hasattr(components, "schemas") and components.schemas:
596
+ for name, schema in components.schemas.items():
597
+ try:
598
+ if isinstance(schema, Reference_30):
599
+ resolved_schema = self._resolve_ref(schema)
600
+ schema_definitions[name] = self._extract_schema_as_dict(
601
+ resolved_schema
602
+ )
603
+ else:
604
+ schema_definitions[name] = self._extract_schema_as_dict(
605
+ schema
606
+ )
607
+ except Exception as e:
608
+ logger.warning(
609
+ f"Failed to extract schema definition '{name}': {e}"
610
+ )
611
+
546
612
  for path_str, path_item_obj in self.openapi.paths.items():
547
613
  if not isinstance(path_item_obj, PathItem_30):
548
614
  logger.warning(
@@ -593,6 +659,7 @@ class OpenAPI30Parser(BaseOpenAPIParser):
593
659
  parameters=parameters,
594
660
  request_body=request_body_info,
595
661
  responses=responses,
662
+ schema_definitions=schema_definitions,
596
663
  )
597
664
  routes.append(route)
598
665
  logger.info(
@@ -711,14 +778,34 @@ class OpenAPI30Parser(BaseOpenAPIParser):
711
778
 
712
779
  param_schema_dict = {}
713
780
  if param_schema_obj: # Check if schema exists
781
+ # Resolve the schema if it's a reference
782
+ resolved_schema = self._resolve_ref(param_schema_obj)
714
783
  param_schema_dict = self._extract_schema_as_dict(param_schema_obj)
784
+
785
+ # Ensure default value is preserved from resolved schema
786
+ if (
787
+ not isinstance(resolved_schema, Reference_30)
788
+ and hasattr(resolved_schema, "default")
789
+ and resolved_schema.default is not None
790
+ ):
791
+ param_schema_dict["default"] = resolved_schema.default
715
792
  elif parameter.content:
716
793
  # Handle complex parameters with 'content'
717
794
  first_media_type = next(iter(parameter.content.values()), None)
718
795
  if first_media_type and first_media_type.media_type_schema:
719
- param_schema_dict = self._extract_schema_as_dict(
720
- first_media_type.media_type_schema
721
- )
796
+ # Resolve the schema if it's a reference
797
+ media_schema = first_media_type.media_type_schema
798
+ resolved_media_schema = self._resolve_ref(media_schema)
799
+ param_schema_dict = self._extract_schema_as_dict(media_schema)
800
+
801
+ # Ensure default value is preserved from resolved schema
802
+ if (
803
+ not isinstance(resolved_media_schema, Reference_30)
804
+ and hasattr(resolved_media_schema, "default")
805
+ and resolved_media_schema.default is not None
806
+ ):
807
+ param_schema_dict["default"] = resolved_media_schema.default
808
+
722
809
  logger.debug(
723
810
  f"Parameter '{parameter.name}' using schema from 'content' field."
724
811
  )
@@ -1173,6 +1260,23 @@ def _combine_schemas(route: openapi.HTTPRoute) -> dict[str, Any]:
1173
1260
  # Copy the schema and add description if available
1174
1261
  param_schema = param.schema_.copy() if isinstance(param.schema_, dict) else {}
1175
1262
 
1263
+ # Convert #/components/schemas references to #/$defs references
1264
+ if isinstance(param_schema, dict) and "$ref" in param_schema:
1265
+ ref_path = param_schema["$ref"]
1266
+ if ref_path.startswith("#/components/schemas/"):
1267
+ schema_name = ref_path.split("/")[-1]
1268
+ param_schema["$ref"] = f"#/$defs/{schema_name}"
1269
+
1270
+ # Also handle anyOf, allOf, oneOf references
1271
+ for section in ["anyOf", "allOf", "oneOf"]:
1272
+ if section in param_schema and isinstance(param_schema[section], list):
1273
+ for i, item in enumerate(param_schema[section]):
1274
+ if isinstance(item, dict) and "$ref" in item:
1275
+ ref_path = item["$ref"]
1276
+ if ref_path.startswith("#/components/schemas/"):
1277
+ schema_name = ref_path.split("/")[-1]
1278
+ param_schema[section][i]["$ref"] = f"#/$defs/{schema_name}"
1279
+
1176
1280
  # Add parameter description to schema if available and not already present
1177
1281
  if param.description and not param_schema.get("description"):
1178
1282
  param_schema["description"] = param.description
@@ -1193,8 +1297,19 @@ def _combine_schemas(route: openapi.HTTPRoute) -> dict[str, Any]:
1193
1297
  if route.request_body.required:
1194
1298
  required.extend(body_schema.get("required", []))
1195
1299
 
1196
- return {
1300
+ result = {
1197
1301
  "type": "object",
1198
1302
  "properties": properties,
1199
1303
  "required": required,
1200
1304
  }
1305
+
1306
+ # Add schema definitions if available
1307
+ if route.schema_definitions:
1308
+ result["$defs"] = route.schema_definitions
1309
+
1310
+ # Use compress_schema to remove unused definitions
1311
+ from fastmcp.utilities.json_schema import compress_schema
1312
+
1313
+ result = compress_schema(result)
1314
+
1315
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.3.3
3
+ Version: 2.3.4
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,7 +19,7 @@ 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.8.0
22
+ Requires-Dist: mcp<2.0.0,>=1.8.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
@@ -1,17 +1,17 @@
1
1
  fastmcp/__init__.py,sha256=yTAqLZORsPqbr7AE0ayw6zIYBeMlxQlI-3HE2WqbvHk,435
2
- fastmcp/exceptions.py,sha256=QKVHbftoZp4YZQ2NxA-t1SjztqspFdX95YTFOAmr5EE,640
2
+ fastmcp/exceptions.py,sha256=YvaKqOT3w0boXF9ylIoaSIzW9XiQ1qLFG1LZq6B60H8,680
3
3
  fastmcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- fastmcp/settings.py,sha256=rDClnYEpYjEl8VsvvVrKp9oaE4YLfNQcMoZ41H_bDL0,2968
4
+ fastmcp/settings.py,sha256=Slo7sRSbXblgmTQcRSee6ns6CsW2yW-TvBY--kAhJqg,3856
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=Tb-WiIXFZiq4nqlZ6LMXN2iYY30clC4Om_gP89HbJcE,15641
8
- fastmcp/client/__init__.py,sha256=BXO9NUhntZ5GnUACfaRCzDJ5IzxqFJs8qKG-CRMSco4,490
8
+ fastmcp/client/__init__.py,sha256=Ri8GFHolIKOZnXaMzIc3VpkLcEqAmOoYGCKgmSk6NnE,550
9
9
  fastmcp/client/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- fastmcp/client/client.py,sha256=zfSLWSGqiBoADveKehsAL66CdyGGqmzVHR1q46zfdQY,15479
10
+ fastmcp/client/client.py,sha256=EOS6_2zd9zWxhCN_cY6cAiqGmCSWMX1t3g6PxemjTtw,17982
11
11
  fastmcp/client/logging.py,sha256=Q8jYcZj4KA15Yiz3RP8tBXj8sd9IxL3VThF_Y0O4Upc,356
12
12
  fastmcp/client/roots.py,sha256=IxI_bHwHTmg6c2H-s1av1ZgrRnNDieHtYwdGFbzXT5c,2471
13
13
  fastmcp/client/sampling.py,sha256=UlDHxnd6k_HoU8RA3ob0g8-e6haJBc9u27N_v291QoI,1698
14
- fastmcp/client/transports.py,sha256=rIOodm2_7tuwy2oMgobiSlZsU08Z2Iy0XJs0dhvprWE,18705
14
+ fastmcp/client/transports.py,sha256=30SubI-fgrqVknyPlySVJ7sGkOMAjm8CP-Owrb8gu58,19704
15
15
  fastmcp/contrib/README.md,sha256=rKknYSI1T192UvSszqwwDlQ2eYQpxywrNTLoj177SYU,878
16
16
  fastmcp/contrib/bulk_tool_caller/README.md,sha256=5aUUY1TSFKtz1pvTLSDqkUCkGkuqMfMZNsLeaNqEgAc,1960
17
17
  fastmcp/contrib/bulk_tool_caller/__init__.py,sha256=xvGSSaUXTQrc31erBoi1Gh7BikgOliETDiYVTP3rLxY,75
@@ -25,33 +25,34 @@ fastmcp/low_level/README.md,sha256=IRvElvOOc_RLLsqbUm7e6VOEwrKHPJeox0pV7JVKHWw,1
25
25
  fastmcp/low_level/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  fastmcp/low_level/sse_server_transport.py,sha256=pUG3AL4Wjf9LgH9fj1l3emGEjFDFDhmKcDfgiiFJcuQ,4448
27
27
  fastmcp/prompts/__init__.py,sha256=An8uMBUh9Hrb7qqcn_5_Hent7IOeSh7EA2IUVsIrtHc,179
28
- fastmcp/prompts/prompt.py,sha256=psc-YiBRttbjETINaP9P9QV328yk96mDBsZgjOHVyKM,7777
28
+ fastmcp/prompts/prompt.py,sha256=CGu11NbOvO0b79F3EDCG7YbajGslpoPu59VOEzanIp0,7968
29
29
  fastmcp/prompts/prompt_manager.py,sha256=9VcioLE-AoUKe1e9SynNQME9SvWy0q1QAvO1ewIWVmI,3126
30
30
  fastmcp/resources/__init__.py,sha256=t0x1j8lc74rjUKtXe9H5Gs4fpQt82K4NgBK6Y7A0xTg,467
31
31
  fastmcp/resources/resource.py,sha256=Rx1My_fi1f-oqnQ9R_v7ejopAk4BJDfbB75-s4d31dM,2492
32
- fastmcp/resources/resource_manager.py,sha256=_kJQMjb7U0NYPhWZEayDCgpMeYOnvtoCzi2sMZc71NA,9442
33
- fastmcp/resources/template.py,sha256=X_x1wnmIWGykoKwhwGLZmvGysXIubVLHDrvSmbsu4kg,7306
34
- fastmcp/resources/types.py,sha256=QPDeka_cM1hmvwW4FeFhqy6BEEi4MlwtpvhWUVWh5Fc,6459
32
+ fastmcp/resources/resource_manager.py,sha256=aJFbn1-Rc-oMahp3SSWNJXJEu8Nxu-gc24i6lTVALQo,10316
33
+ fastmcp/resources/template.py,sha256=ex1s2kBQmGUU1zQ-b__egyJoNlNNKI42JALO0uxxAaE,7408
34
+ fastmcp/resources/types.py,sha256=5fUFvzRlekNjtfihtq8S-fT0alKoNfclzrugqeM5JRE,6366
35
35
  fastmcp/server/__init__.py,sha256=bMD4aQD4yJqLz7-mudoNsyeV8UgQfRAg3PRwPvwTEds,119
36
36
  fastmcp/server/context.py,sha256=ykitQygA7zT5prbFTLCuYlnAzuljf_9ErUT0FYBPv3E,8135
37
37
  fastmcp/server/dependencies.py,sha256=1utkxFsV37HZcWBwI69JyngVN2ppGO_PEgxUlUHHy_Q,742
38
38
  fastmcp/server/http.py,sha256=utl7vJkMvKUnKIflCptVWk1oqOi7_sJJHqUl22g4JC8,10473
39
- fastmcp/server/openapi.py,sha256=0nANnwHJ5VZInNyo2f9ErmO0K3igMv6bwyxf3G-BSls,23473
40
- fastmcp/server/proxy.py,sha256=LDTjzc_iQj8AldsfMU37flGRAfJic1w6qsherfyHPAA,9622
41
- fastmcp/server/server.py,sha256=FLcZe-ccCUe5288sdEEPl7xlrKgaVunGhJnq7nv70vc,44483
39
+ fastmcp/server/openapi.py,sha256=_7U0XtPk4wCkGOBfYx3J3ujA9iqQtnsc0scA4sCsIT0,24170
40
+ fastmcp/server/proxy.py,sha256=mt3eM6TQWfnZD5XehmTXisskZ4CBbsWyjRPjprlTjBY,9653
41
+ fastmcp/server/server.py,sha256=EM5BqwXRlG73sMZg6186yMVKfXYGn5gT4qqAxfNa8lU,45454
42
42
  fastmcp/tools/__init__.py,sha256=ocw-SFTtN6vQ8fgnlF8iNAOflRmh79xS1xdO0Bc3QPE,96
43
- fastmcp/tools/tool.py,sha256=HGcHjMecqAeN6eI-IfE_2UBcd1KpTV-VOTFLx9tlbpU,7809
44
- fastmcp/tools/tool_manager.py,sha256=p2nHyLFgz28tbsLpWOurkbWRU2Z34_HcDohjrvwjI0E,3369
43
+ fastmcp/tools/tool.py,sha256=k7awnrPoId_1XJHFce7X3mAEgBsJyr2v5kuuxXrE5Ww,7602
44
+ fastmcp/tools/tool_manager.py,sha256=v4Ur-JXDPXUxHqHJxA52IIcZfSiCBOnoFFLOmmJR1A8,4157
45
45
  fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31
46
46
  fastmcp/utilities/cache.py,sha256=aV3oZ-ZhMgLSM9iAotlUlEy5jFvGXrVo0Y5Bj4PBtqY,707
47
47
  fastmcp/utilities/decorators.py,sha256=AjhjsetQZF4YOPV5MTZmIxO21iFp_4fDIS3O2_KNCEg,2990
48
- fastmcp/utilities/json_schema.py,sha256=mSakhP8bENxhLFMwHJSxJAFllNeByIBDjVohwlpac6w,2026
49
- fastmcp/utilities/logging.py,sha256=zav8pnFxG_fvGJHUV2XpobmT9WVrmv1mlQBSCz-CPx4,1159
50
- fastmcp/utilities/openapi.py,sha256=Er3G1MyFwiWVxZXicXtD2j-BvttHEDTi1dgkq1KiBQc,51073
48
+ fastmcp/utilities/exceptions.py,sha256=Aax9K0larjzrrgJBS6o_PQwoIrvBvVwck2suZvgafXE,1359
49
+ fastmcp/utilities/json_schema.py,sha256=m65XU9lPq7pCxJ9vvCeGRl0HOFr6ArezvYpMBR6-gAg,3777
50
+ fastmcp/utilities/logging.py,sha256=n4P7P-aFDCuUFz8O-ykzUOj2sXl789HtWI_pX3ynGaY,1234
51
+ fastmcp/utilities/openapi.py,sha256=V3hANT6KcD_Bloq9uHDVkVJRcGaZIq8GH5ZZ7bKVmXY,56943
51
52
  fastmcp/utilities/tests.py,sha256=mAV2EjDeCbm9V9NsVIUjcmzf93MgDjfj8kMvHpf4vgo,3224
52
53
  fastmcp/utilities/types.py,sha256=6CcqAQ1QqCO2HGSFlPS6FO5JRWnacjCcO2-EhyEnZV0,4400
53
- fastmcp-2.3.3.dist-info/METADATA,sha256=4z9X0B1oEDwqQBFJ7d2_YJtEoj2cKLn6TcJafKxATIE,15754
54
- fastmcp-2.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- fastmcp-2.3.3.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
56
- fastmcp-2.3.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
57
- fastmcp-2.3.3.dist-info/RECORD,,
54
+ fastmcp-2.3.4.dist-info/METADATA,sha256=9uZJ9YUNRVnoD6oFmDatjuiaQn8GAWt9XKzqagsngyQ,15754
55
+ fastmcp-2.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ fastmcp-2.3.4.dist-info/entry_points.txt,sha256=ff8bMtKX1JvXyurMibAacMSKbJEPmac9ffAKU9mLnM8,44
57
+ fastmcp-2.3.4.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
58
+ fastmcp-2.3.4.dist-info/RECORD,,