Jeremiah Lowin commited on
Commit
7712403
·
unverified ·
1 Parent(s): 061c53c

Clean up complexity from PR #1426 (#1435)

Browse files
Files changed (29) hide show
  1. pyproject.toml +0 -1
  2. src/fastmcp/experimental/utilities/openapi/parser.py +3 -3
  3. src/fastmcp/experimental/utilities/openapi/schemas.py +12 -25
  4. tests/experimental/README.md +3 -0
  5. tests/experimental/openapi_parser/README.md +5 -0
  6. tests/experimental/{server → openapi_parser}/__init__.py +0 -0
  7. tests/experimental/{server → openapi_parser}/conftest.py +0 -0
  8. tests/experimental/{utilities → openapi_parser/server}/__init__.py +0 -0
  9. tests/experimental/{server → openapi_parser/server}/openapi/__init__.py +0 -0
  10. tests/experimental/{server → openapi_parser/server}/openapi/test_comprehensive.py +0 -0
  11. tests/experimental/{server → openapi_parser/server}/openapi/test_deepobject_style.py +0 -0
  12. tests/experimental/{server → openapi_parser/server}/openapi/test_end_to_end_compatibility.py +0 -0
  13. tests/experimental/{server → openapi_parser/server}/openapi/test_openapi_features.py +0 -0
  14. tests/experimental/{server → openapi_parser/server}/openapi/test_openapi_performance.py +0 -0
  15. tests/experimental/{server → openapi_parser/server}/openapi/test_parameter_collisions.py +0 -0
  16. tests/experimental/{server → openapi_parser/server}/openapi/test_performance_comparison.py +0 -0
  17. tests/experimental/{server → openapi_parser/server}/openapi/test_server.py +0 -0
  18. tests/experimental/openapi_parser/utilities/__init__.py +0 -0
  19. tests/experimental/{utilities → openapi_parser/utilities}/openapi/__init__.py +0 -0
  20. tests/experimental/{utilities → openapi_parser/utilities}/openapi/conftest.py +0 -8
  21. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_allof_requestbody.py +0 -0
  22. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_director.py +0 -0
  23. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_legacy_compatibility.py +0 -0
  24. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_models.py +0 -0
  25. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_nullable_fields.py +0 -0
  26. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_parser.py +0 -0
  27. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_schemas.py +3 -3
  28. tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_transitive_references.py +0 -0
  29. uv.lock +0 -38
pyproject.toml CHANGED
@@ -15,7 +15,6 @@ dependencies = [
15
  "pydantic[email]>=2.11.7",
16
  "pyperclip>=1.9.0",
17
  "openapi-core>=0.19.5",
18
- "msgspec>=0.19.0",
19
  ]
20
  requires-python = ">=3.10"
21
  readme = "README.md"
 
15
  "pydantic[email]>=2.11.7",
16
  "pyperclip>=1.9.0",
17
  "openapi-core>=0.19.5",
 
18
  ]
19
  requires-python = ">=3.10"
20
  readme = "README.md"
src/fastmcp/experimental/utilities/openapi/parser.py CHANGED
@@ -36,7 +36,7 @@ from .models import (
36
  )
37
  from .schemas import (
38
  _combine_schemas_and_map_params,
39
- _replace_ref_with_defs_recursive,
40
  )
41
 
42
  logger = get_logger(__name__)
@@ -225,7 +225,7 @@ class OpenAPIParser(
225
 
226
  # Convert refs from OpenAPI format to JSON Schema format using recursive approach
227
 
228
- result = _replace_ref_with_defs_recursive(result)
229
  return result
230
  except ValueError as e:
231
  # Re-raise ValueError for external reference errors and other validation issues
@@ -606,7 +606,7 @@ class OpenAPIParser(
606
  # Convert each schema definition recursively
607
  for name, schema in schema_definitions.items():
608
  if isinstance(schema, dict):
609
- schema_definitions[name] = _replace_ref_with_defs_recursive(schema)
610
 
611
  # Process paths and operations
612
  for path_str, path_item_obj in self.openapi.paths.items():
 
36
  )
37
  from .schemas import (
38
  _combine_schemas_and_map_params,
39
+ _replace_ref_with_defs,
40
  )
41
 
42
  logger = get_logger(__name__)
 
225
 
226
  # Convert refs from OpenAPI format to JSON Schema format using recursive approach
227
 
228
+ result = _replace_ref_with_defs(result)
229
  return result
230
  except ValueError as e:
231
  # Re-raise ValueError for external reference errors and other validation issues
 
606
  # Convert each schema definition recursively
607
  for name, schema in schema_definitions.items():
608
  if isinstance(schema, dict):
609
+ schema_definitions[name] = _replace_ref_with_defs(schema)
610
 
611
  # Process paths and operations
612
  for path_str, path_item_obj in self.openapi.paths.items():
src/fastmcp/experimental/utilities/openapi/schemas.py CHANGED
@@ -71,7 +71,7 @@ def clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None:
71
  return cleaned
72
 
73
 
74
- def _replace_ref_with_defs_recursive(
75
  info: dict[str, Any], description: str | None = None
76
  ) -> dict[str, Any]:
77
  """
@@ -106,35 +106,22 @@ def _replace_ref_with_defs_recursive(
106
  )
107
  elif properties := schema.get("properties"):
108
  if "$ref" in properties:
109
- schema["properties"] = _replace_ref_with_defs_recursive(properties)
110
  else:
111
  schema["properties"] = {
112
- prop_name: _replace_ref_with_defs_recursive(prop_schema)
113
  for prop_name, prop_schema in properties.items()
114
  }
115
  elif item_schema := schema.get("items"):
116
- schema["items"] = _replace_ref_with_defs_recursive(item_schema)
117
  for section in ["anyOf", "allOf", "oneOf"]:
118
  for i, item in enumerate(schema.get(section, [])):
119
- schema[section][i] = _replace_ref_with_defs_recursive(item)
120
  if info.get("description", description) and not schema.get("description"):
121
  schema["description"] = description
122
  return schema
123
 
124
 
125
- def _ensure_refs_converted(schema: dict[str, Any]) -> dict[str, Any]:
126
- """
127
- Ensure all OpenAPI refs are converted to JSON Schema format using recursive approach.
128
-
129
- Args:
130
- schema: Schema that may contain OpenAPI refs
131
-
132
- Returns:
133
- Schema with all refs converted to JSON Schema format
134
- """
135
- return _replace_ref_with_defs_recursive(schema)
136
-
137
-
138
  def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]:
139
  """
140
  Make an optional parameter schema nullable to allow None values.
@@ -250,7 +237,7 @@ def _combine_schemas_and_map_params(
250
 
251
  # Convert refs if needed
252
  if convert_refs:
253
- body_schema = _ensure_refs_converted(
254
  route.request_body.content_schema[content_type]
255
  )
256
  else:
@@ -310,7 +297,7 @@ def _combine_schemas_and_map_params(
310
 
311
  # Convert refs if needed
312
  if convert_refs:
313
- param_schema = _ensure_refs_converted(param.schema_)
314
  else:
315
  param_schema = param.schema_
316
  original_desc = param_schema.get("description", "")
@@ -337,7 +324,7 @@ def _combine_schemas_and_map_params(
337
 
338
  # Convert refs if needed
339
  if convert_refs:
340
- param_schema = _ensure_refs_converted(param.schema_)
341
  else:
342
  param_schema = param.schema_
343
 
@@ -384,7 +371,7 @@ def _combine_schemas_and_map_params(
384
  # Convert each schema definition recursively
385
  for name, schema in all_defs.items():
386
  if isinstance(schema, dict):
387
- all_defs[name] = _replace_ref_with_defs_recursive(schema)
388
 
389
  # Prune to only needed schemas
390
  used_refs = set()
@@ -514,7 +501,7 @@ def extract_output_schema_from_responses(
514
  return None
515
 
516
  # Convert refs if needed
517
- output_schema = _ensure_refs_converted(schema)
518
 
519
  # If schema has a $ref, resolve it first before processing nullable fields
520
  if "$ref" in output_schema and schema_definitions:
@@ -523,7 +510,7 @@ def extract_output_schema_from_responses(
523
  schema_name = ref_path.split("/")[-1]
524
  if schema_name in schema_definitions:
525
  # Replace $ref with the actual schema definition
526
- output_schema = _ensure_refs_converted(schema_definitions[schema_name])
527
 
528
  # Convert OpenAPI schema to JSON Schema format
529
  # Only needed for OpenAPI 3.0 - 3.1 uses standard JSON Schema null types
@@ -553,7 +540,7 @@ def extract_output_schema_from_responses(
553
  # Convert each schema definition recursively
554
  for name, schema in processed_defs.items():
555
  if isinstance(schema, dict):
556
- processed_defs[name] = _replace_ref_with_defs_recursive(schema)
557
 
558
  # Convert OpenAPI schema definitions to JSON Schema format if needed
559
  if openapi_version and openapi_version.startswith("3.0"):
 
71
  return cleaned
72
 
73
 
74
+ def _replace_ref_with_defs(
75
  info: dict[str, Any], description: str | None = None
76
  ) -> dict[str, Any]:
77
  """
 
106
  )
107
  elif properties := schema.get("properties"):
108
  if "$ref" in properties:
109
+ schema["properties"] = _replace_ref_with_defs(properties)
110
  else:
111
  schema["properties"] = {
112
+ prop_name: _replace_ref_with_defs(prop_schema)
113
  for prop_name, prop_schema in properties.items()
114
  }
115
  elif item_schema := schema.get("items"):
116
+ schema["items"] = _replace_ref_with_defs(item_schema)
117
  for section in ["anyOf", "allOf", "oneOf"]:
118
  for i, item in enumerate(schema.get(section, [])):
119
+ schema[section][i] = _replace_ref_with_defs(item)
120
  if info.get("description", description) and not schema.get("description"):
121
  schema["description"] = description
122
  return schema
123
 
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  def _make_optional_parameter_nullable(schema: dict[str, Any]) -> dict[str, Any]:
126
  """
127
  Make an optional parameter schema nullable to allow None values.
 
237
 
238
  # Convert refs if needed
239
  if convert_refs:
240
+ body_schema = _replace_ref_with_defs(
241
  route.request_body.content_schema[content_type]
242
  )
243
  else:
 
297
 
298
  # Convert refs if needed
299
  if convert_refs:
300
+ param_schema = _replace_ref_with_defs(param.schema_)
301
  else:
302
  param_schema = param.schema_
303
  original_desc = param_schema.get("description", "")
 
324
 
325
  # Convert refs if needed
326
  if convert_refs:
327
+ param_schema = _replace_ref_with_defs(param.schema_)
328
  else:
329
  param_schema = param.schema_
330
 
 
371
  # Convert each schema definition recursively
372
  for name, schema in all_defs.items():
373
  if isinstance(schema, dict):
374
+ all_defs[name] = _replace_ref_with_defs(schema)
375
 
376
  # Prune to only needed schemas
377
  used_refs = set()
 
501
  return None
502
 
503
  # Convert refs if needed
504
+ output_schema = _replace_ref_with_defs(schema)
505
 
506
  # If schema has a $ref, resolve it first before processing nullable fields
507
  if "$ref" in output_schema and schema_definitions:
 
510
  schema_name = ref_path.split("/")[-1]
511
  if schema_name in schema_definitions:
512
  # Replace $ref with the actual schema definition
513
+ output_schema = _replace_ref_with_defs(schema_definitions[schema_name])
514
 
515
  # Convert OpenAPI schema to JSON Schema format
516
  # Only needed for OpenAPI 3.0 - 3.1 uses standard JSON Schema null types
 
540
  # Convert each schema definition recursively
541
  for name, schema in processed_defs.items():
542
  if isinstance(schema, dict):
543
+ processed_defs[name] = _replace_ref_with_defs(schema)
544
 
545
  # Convert OpenAPI schema definitions to JSON Schema format if needed
546
  if openapi_version and openapi_version.startswith("3.0"):
tests/experimental/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Experimental Tests
2
+
3
+ Each directory in this folder tests a discrete experimental feature. The directory structure within each experiment should approximate the "normal" test directory structure to facilitate moving tests into place once/if the experiment is merged into the main codebase.
tests/experimental/openapi_parser/README.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # OpenAPI Parser Experiment
2
+
3
+ Performance-optimized OpenAPI parser with better transitive reference resolution.
4
+
5
+ Enabled via `experimental.enable_new_openapi_parser = True`
tests/experimental/{server → openapi_parser}/__init__.py RENAMED
File without changes
tests/experimental/{server → openapi_parser}/conftest.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/server}/__init__.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/__init__.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_comprehensive.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_deepobject_style.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_end_to_end_compatibility.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_openapi_features.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_openapi_performance.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_parameter_collisions.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_performance_comparison.py RENAMED
File without changes
tests/experimental/{server → openapi_parser/server}/openapi/test_server.py RENAMED
File without changes
tests/experimental/openapi_parser/utilities/__init__.py ADDED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/__init__.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/conftest.py RENAMED
@@ -2,14 +2,6 @@
2
 
3
  import pytest
4
 
5
- from fastmcp.utilities.tests import temporary_settings
6
-
7
-
8
- @pytest.fixture(autouse=True)
9
- def use_new_openapi_parser():
10
- with temporary_settings(experimental__enable_new_openapi_parser=True):
11
- yield
12
-
13
 
14
  @pytest.fixture
15
  def basic_openapi_30_spec():
 
2
 
3
  import pytest
4
 
 
 
 
 
 
 
 
 
5
 
6
  @pytest.fixture
7
  def basic_openapi_30_spec():
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_allof_requestbody.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_director.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_legacy_compatibility.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_models.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_nullable_fields.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_parser.py RENAMED
File without changes
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_schemas.py RENAMED
@@ -10,7 +10,7 @@ from fastmcp.experimental.utilities.openapi.models import (
10
  from fastmcp.experimental.utilities.openapi.schemas import (
11
  _combine_schemas,
12
  _combine_schemas_and_map_params,
13
- _replace_ref_with_defs_recursive,
14
  )
15
  from fastmcp.utilities.json_schema import compress_schema
16
 
@@ -244,7 +244,7 @@ class TestSchemaProcessing:
244
  }
245
 
246
  # Use our recursive replacement approach
247
- result = _replace_ref_with_defs_recursive(schema_with_ref)
248
 
249
  assert result["properties"]["user"]["$ref"] == "#/$defs/User"
250
  assert result["properties"]["items"]["items"]["$ref"] == "#/$defs/Item"
@@ -274,7 +274,7 @@ class TestSchemaProcessing:
274
  }
275
 
276
  # Use our recursive replacement approach
277
- result = _replace_ref_with_defs_recursive(nested_schema)
278
 
279
  # Check nested object property
280
  nested_prop = result["properties"]["data"]["properties"]["nested"]
 
10
  from fastmcp.experimental.utilities.openapi.schemas import (
11
  _combine_schemas,
12
  _combine_schemas_and_map_params,
13
+ _replace_ref_with_defs,
14
  )
15
  from fastmcp.utilities.json_schema import compress_schema
16
 
 
244
  }
245
 
246
  # Use our recursive replacement approach
247
+ result = _replace_ref_with_defs(schema_with_ref)
248
 
249
  assert result["properties"]["user"]["$ref"] == "#/$defs/User"
250
  assert result["properties"]["items"]["items"]["$ref"] == "#/$defs/Item"
 
274
  }
275
 
276
  # Use our recursive replacement approach
277
+ result = _replace_ref_with_defs(nested_schema)
278
 
279
  # Check nested object property
280
  nested_prop = result["properties"]["data"]["properties"]["nested"]
tests/experimental/{utilities → openapi_parser/utilities}/openapi/test_transitive_references.py RENAMED
File without changes
uv.lock CHANGED
@@ -530,7 +530,6 @@ dependencies = [
530
  { name = "exceptiongroup" },
531
  { name = "httpx" },
532
  { name = "mcp" },
533
- { name = "msgspec" },
534
  { name = "openapi-core" },
535
  { name = "openapi-pydantic" },
536
  { name = "pydantic", extra = ["email"] },
@@ -575,7 +574,6 @@ requires-dist = [
575
  { name = "exceptiongroup", specifier = ">=1.2.2" },
576
  { name = "httpx", specifier = ">=0.28.1" },
577
  { name = "mcp", specifier = ">=1.12.4,<2.0.0" },
578
- { name = "msgspec", specifier = ">=0.19.0" },
579
  { name = "openapi-core", specifier = ">=0.19.5" },
580
  { name = "openapi-pydantic", specifier = ">=0.5.1" },
581
  { name = "pydantic", extras = ["email"], specifier = ">=2.11.7" },
@@ -981,42 +979,6 @@ wheels = [
981
  { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" },
982
  ]
983
 
984
- [[package]]
985
- name = "msgspec"
986
- version = "0.19.0"
987
- source = { registry = "https://pypi.org/simple" }
988
- sdist = { url = "https://files.pythonhosted.org/packages/cf/9b/95d8ce458462b8b71b8a70fa94563b2498b89933689f3a7b8911edfae3d7/msgspec-0.19.0.tar.gz", hash = "sha256:604037e7cd475345848116e89c553aa9a233259733ab51986ac924ab1b976f8e", size = 216934, upload-time = "2024-12-27T17:40:28.597Z" }
989
- wheels = [
990
- { url = "https://files.pythonhosted.org/packages/13/40/817282b42f58399762267b30deb8ac011d8db373f8da0c212c85fbe62b8f/msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259", size = 190019, upload-time = "2024-12-27T17:39:13.803Z" },
991
- { url = "https://files.pythonhosted.org/packages/92/99/bd7ed738c00f223a8119928661167a89124140792af18af513e6519b0d54/msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36", size = 183680, upload-time = "2024-12-27T17:39:17.847Z" },
992
- { url = "https://files.pythonhosted.org/packages/e5/27/322badde18eb234e36d4a14122b89edd4e2973cdbc3da61ca7edf40a1ccd/msgspec-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe2c4bf29bf4e89790b3117470dea2c20b59932772483082c468b990d45fb947", size = 209334, upload-time = "2024-12-27T17:39:19.065Z" },
993
- { url = "https://files.pythonhosted.org/packages/c6/65/080509c5774a1592b2779d902a70b5fe008532759927e011f068145a16cb/msgspec-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e87ecfa9795ee5214861eab8326b0e75475c2e68a384002aa135ea2a27d909", size = 211551, upload-time = "2024-12-27T17:39:21.767Z" },
994
- { url = "https://files.pythonhosted.org/packages/6f/2e/1c23c6b4ca6f4285c30a39def1054e2bee281389e4b681b5e3711bd5a8c9/msgspec-0.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3c4ec642689da44618f68c90855a10edbc6ac3ff7c1d94395446c65a776e712a", size = 215099, upload-time = "2024-12-27T17:39:24.71Z" },
995
- { url = "https://files.pythonhosted.org/packages/83/fe/95f9654518879f3359d1e76bc41189113aa9102452170ab7c9a9a4ee52f6/msgspec-0.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2719647625320b60e2d8af06b35f5b12d4f4d281db30a15a1df22adb2295f633", size = 218211, upload-time = "2024-12-27T17:39:27.396Z" },
996
- { url = "https://files.pythonhosted.org/packages/79/f6/71ca7e87a1fb34dfe5efea8156c9ef59dd55613aeda2ca562f122cd22012/msgspec-0.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:695b832d0091edd86eeb535cd39e45f3919f48d997685f7ac31acb15e0a2ed90", size = 186174, upload-time = "2024-12-27T17:39:29.647Z" },
997
- { url = "https://files.pythonhosted.org/packages/24/d4/2ec2567ac30dab072cce3e91fb17803c52f0a37aab6b0c24375d2b20a581/msgspec-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa77046904db764b0462036bc63ef71f02b75b8f72e9c9dd4c447d6da1ed8f8e", size = 187939, upload-time = "2024-12-27T17:39:32.347Z" },
998
- { url = "https://files.pythonhosted.org/packages/2b/c0/18226e4328897f4f19875cb62bb9259fe47e901eade9d9376ab5f251a929/msgspec-0.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:047cfa8675eb3bad68722cfe95c60e7afabf84d1bd8938979dd2b92e9e4a9551", size = 182202, upload-time = "2024-12-27T17:39:33.633Z" },
999
- { url = "https://files.pythonhosted.org/packages/81/25/3a4b24d468203d8af90d1d351b77ea3cffb96b29492855cf83078f16bfe4/msgspec-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e78f46ff39a427e10b4a61614a2777ad69559cc8d603a7c05681f5a595ea98f7", size = 209029, upload-time = "2024-12-27T17:39:35.023Z" },
1000
- { url = "https://files.pythonhosted.org/packages/85/2e/db7e189b57901955239f7689b5dcd6ae9458637a9c66747326726c650523/msgspec-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c7adf191e4bd3be0e9231c3b6dc20cf1199ada2af523885efc2ed218eafd011", size = 210682, upload-time = "2024-12-27T17:39:36.384Z" },
1001
- { url = "https://files.pythonhosted.org/packages/03/97/7c8895c9074a97052d7e4a1cc1230b7b6e2ca2486714eb12c3f08bb9d284/msgspec-0.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f04cad4385e20be7c7176bb8ae3dca54a08e9756cfc97bcdb4f18560c3042063", size = 214003, upload-time = "2024-12-27T17:39:39.097Z" },
1002
- { url = "https://files.pythonhosted.org/packages/61/61/e892997bcaa289559b4d5869f066a8021b79f4bf8e955f831b095f47a4cd/msgspec-0.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45c8fb410670b3b7eb884d44a75589377c341ec1392b778311acdbfa55187716", size = 216833, upload-time = "2024-12-27T17:39:41.203Z" },
1003
- { url = "https://files.pythonhosted.org/packages/ce/3d/71b2dffd3a1c743ffe13296ff701ee503feaebc3f04d0e75613b6563c374/msgspec-0.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:70eaef4934b87193a27d802534dc466778ad8d536e296ae2f9334e182ac27b6c", size = 186184, upload-time = "2024-12-27T17:39:43.702Z" },
1004
- { url = "https://files.pythonhosted.org/packages/b2/5f/a70c24f075e3e7af2fae5414c7048b0e11389685b7f717bb55ba282a34a7/msgspec-0.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f98bd8962ad549c27d63845b50af3f53ec468b6318400c9f1adfe8b092d7b62f", size = 190485, upload-time = "2024-12-27T17:39:44.974Z" },
1005
- { url = "https://files.pythonhosted.org/packages/89/b0/1b9763938cfae12acf14b682fcf05c92855974d921a5a985ecc197d1c672/msgspec-0.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:43bbb237feab761b815ed9df43b266114203f53596f9b6e6f00ebd79d178cdf2", size = 183910, upload-time = "2024-12-27T17:39:46.401Z" },
1006
- { url = "https://files.pythonhosted.org/packages/87/81/0c8c93f0b92c97e326b279795f9c5b956c5a97af28ca0fbb9fd86c83737a/msgspec-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cfc033c02c3e0aec52b71710d7f84cb3ca5eb407ab2ad23d75631153fdb1f12", size = 210633, upload-time = "2024-12-27T17:39:49.099Z" },
1007
- { url = "https://files.pythonhosted.org/packages/d0/ef/c5422ce8af73928d194a6606f8ae36e93a52fd5e8df5abd366903a5ca8da/msgspec-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d911c442571605e17658ca2b416fd8579c5050ac9adc5e00c2cb3126c97f73bc", size = 213594, upload-time = "2024-12-27T17:39:51.204Z" },
1008
- { url = "https://files.pythonhosted.org/packages/19/2b/4137bc2ed45660444842d042be2cf5b18aa06efd2cda107cff18253b9653/msgspec-0.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:757b501fa57e24896cf40a831442b19a864f56d253679f34f260dcb002524a6c", size = 214053, upload-time = "2024-12-27T17:39:52.866Z" },
1009
- { url = "https://files.pythonhosted.org/packages/9d/e6/8ad51bdc806aac1dc501e8fe43f759f9ed7284043d722b53323ea421c360/msgspec-0.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f0f65f29b45e2816d8bded36e6b837a4bf5fb60ec4bc3c625fa2c6da4124537", size = 219081, upload-time = "2024-12-27T17:39:55.142Z" },
1010
- { url = "https://files.pythonhosted.org/packages/b1/ef/27dd35a7049c9a4f4211c6cd6a8c9db0a50647546f003a5867827ec45391/msgspec-0.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:067f0de1c33cfa0b6a8206562efdf6be5985b988b53dd244a8e06f993f27c8c0", size = 187467, upload-time = "2024-12-27T17:39:56.531Z" },
1011
- { url = "https://files.pythonhosted.org/packages/3c/cb/2842c312bbe618d8fefc8b9cedce37f773cdc8fa453306546dba2c21fd98/msgspec-0.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f12d30dd6266557aaaf0aa0f9580a9a8fbeadfa83699c487713e355ec5f0bd86", size = 190498, upload-time = "2024-12-27T17:40:00.427Z" },
1012
- { url = "https://files.pythonhosted.org/packages/58/95/c40b01b93465e1a5f3b6c7d91b10fb574818163740cc3acbe722d1e0e7e4/msgspec-0.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82b2c42c1b9ebc89e822e7e13bbe9d17ede0c23c187469fdd9505afd5a481314", size = 183950, upload-time = "2024-12-27T17:40:04.219Z" },
1013
- { url = "https://files.pythonhosted.org/packages/e8/f0/5b764e066ce9aba4b70d1db8b087ea66098c7c27d59b9dd8a3532774d48f/msgspec-0.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19746b50be214a54239aab822964f2ac81e38b0055cca94808359d779338c10e", size = 210647, upload-time = "2024-12-27T17:40:05.606Z" },
1014
- { url = "https://files.pythonhosted.org/packages/9d/87/bc14f49bc95c4cb0dd0a8c56028a67c014ee7e6818ccdce74a4862af259b/msgspec-0.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ef4bdb0ec8e4ad62e5a1f95230c08efb1f64f32e6e8dd2ced685bcc73858b5", size = 213563, upload-time = "2024-12-27T17:40:10.516Z" },
1015
- { url = "https://files.pythonhosted.org/packages/53/2f/2b1c2b056894fbaa975f68f81e3014bb447516a8b010f1bed3fb0e016ed7/msgspec-0.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac7f7c377c122b649f7545810c6cd1b47586e3aa3059126ce3516ac7ccc6a6a9", size = 213996, upload-time = "2024-12-27T17:40:12.244Z" },
1016
- { url = "https://files.pythonhosted.org/packages/aa/5a/4cd408d90d1417e8d2ce6a22b98a6853c1b4d7cb7669153e4424d60087f6/msgspec-0.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5bc1472223a643f5ffb5bf46ccdede7f9795078194f14edd69e3aab7020d327", size = 219087, upload-time = "2024-12-27T17:40:14.881Z" },
1017
- { url = "https://files.pythonhosted.org/packages/23/d8/f15b40611c2d5753d1abb0ca0da0c75348daf1252220e5dda2867bd81062/msgspec-0.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:317050bc0f7739cb30d257ff09152ca309bf5a369854bbf1e57dffc310c1f20f", size = 187432, upload-time = "2024-12-27T17:40:16.256Z" },
1018
- ]
1019
-
1020
  [[package]]
1021
  name = "nodeenv"
1022
  version = "1.9.1"
 
530
  { name = "exceptiongroup" },
531
  { name = "httpx" },
532
  { name = "mcp" },
 
533
  { name = "openapi-core" },
534
  { name = "openapi-pydantic" },
535
  { name = "pydantic", extra = ["email"] },
 
574
  { name = "exceptiongroup", specifier = ">=1.2.2" },
575
  { name = "httpx", specifier = ">=0.28.1" },
576
  { name = "mcp", specifier = ">=1.12.4,<2.0.0" },
 
577
  { name = "openapi-core", specifier = ">=0.19.5" },
578
  { name = "openapi-pydantic", specifier = ">=0.5.1" },
579
  { name = "pydantic", extras = ["email"], specifier = ">=2.11.7" },
 
979
  { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278, upload-time = "2025-04-22T14:17:40.49Z" },
980
  ]
981
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
  [[package]]
983
  name = "nodeenv"
984
  version = "1.9.1"