diff --git a/.loq_cache b/.loq_cache new file mode 100644 index 000000000..7f2a12d15 --- /dev/null +++ b/.loq_cache @@ -0,0 +1 @@ +{"version":1,"config_hash":11396655348812081611,"entries":{"src/fastmcp/utilities/openapi/director.py":{"mtime_secs":1767910209,"mtime_nanos":992787531,"lines":214},"tests/cli/test_server_args.py":{"mtime_secs":1767910209,"mtime_nanos":995113177,"lines":135},"src/fastmcp/tools/tool_transform.py":{"mtime_secs":1768275412,"mtime_nanos":806607645,"lines":954},"tests/deprecated/test_exclude_args.py":{"mtime_secs":1767910209,"mtime_nanos":999348759,"lines":122},"docs/python-sdk/fastmcp-server-providers-openapi-components.mdx":{"mtime_secs":1768065435,"mtime_nanos":779723054,"lines":62},"src/fastmcp/client/auth/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":977333260,"lines":4},"src/fastmcp/utilities/openapi/parser.py":{"mtime_secs":1768275516,"mtime_nanos":979099727,"lines":821},"SECURITY.md":{"mtime_secs":1767910209,"mtime_nanos":926953703,"lines":16},"src/fastmcp/utilities/ui.py":{"mtime_secs":1767910209,"mtime_nanos":993615038,"lines":626},"src/fastmcp/server/providers/base.py":{"mtime_secs":1768273914,"mtime_nanos":444251277,"lines":440},".git/logs/refs/heads/docs/v3-features":{"mtime_secs":1767969249,"mtime_nanos":769502827,"lines":2},"examples/auth/aws_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":968159215,"lines":42},"tests/server/tasks/test_progress_dependency.py":{"mtime_secs":1767910210,"mtime_nanos":8447303,"lines":155},"examples/auth/mounted/README.md":{"mtime_secs":1767910209,"mtime_nanos":969564771,"lines":39},"docs/python-sdk/fastmcp-server-auth-providers-oci.mdx":{"mtime_secs":1767910209,"mtime_nanos":956156603,"lines":97},"src/fastmcp/client/sampling/handlers/anthropic.py":{"mtime_secs":1768275398,"mtime_nanos":427536231,"lines":387},"docs/getting-started/welcome.mdx":{"mtime_secs":1767910209,"mtime_nanos":944326533,"lines":111},"docs/python-sdk/fastmcp-server-providers-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":958130038,"lines":34},"docs/python-sdk/fastmcp-client-sampling-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":953356451,"lines":14},"src/fastmcp/server/middleware/ping.py":{"mtime_secs":1768091029,"mtime_nanos":865950840,"lines":70},".git/logs/refs/remotes/origin/main":{"mtime_secs":1768274758,"mtime_nanos":717800467,"lines":20},"docs/python-sdk/fastmcp-utilities-async_utils.mdx":{"mtime_secs":1767910209,"mtime_nanos":960405768,"lines":31},"tests/utilities/openapi/test_schemas.py":{"mtime_secs":1767910210,"mtime_nanos":13283015,"lines":641},"docs/python-sdk/fastmcp-server-providers-openapi-routing.mdx":{"mtime_secs":1767910209,"mtime_nanos":958712293,"lines":23},"docs/servers/auth/full-oauth-server.mdx":{"mtime_secs":1767910209,"mtime_nanos":962925875,"lines":229},"tests/prompts/test_prompt.py":{"mtime_secs":1767910210,"mtime_nanos":542895,"lines":552},"docs/python-sdk/fastmcp-cli-install-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":951870646,"lines":9},".git/objects/e7/e6381760d02ce95c255e23d0af84c02f42220a":{"mtime_secs":1768005421,"mtime_nanos":534528398,"lines":1},"tests/server/providers/test_transforming_provider.py":{"mtime_secs":1768273914,"mtime_nanos":459430812,"lines":317},"docs/python-sdk/fastmcp-utilities-logging.mdx":{"mtime_secs":1767910209,"mtime_nanos":961037482,"lines":58},"tests/cli/test_run.py":{"mtime_secs":1767966312,"mtime_nanos":928730568,"lines":628},".git/hooks/post-update.sample":{"mtime_secs":1767910208,"mtime_nanos":710089045,"lines":8},"src/fastmcp/client/sampling/handlers/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":978318686,"lines":0},".git/refs/tags/v2.14.3":{"mtime_secs":1768250271,"mtime_nanos":813098679,"lines":1},"tests/utilities/openapi/test_direct_array_schemas.py":{"mtime_secs":1767910210,"mtime_nanos":12777094,"lines":323},"docs/patterns/cli.mdx":{"mtime_secs":1767966312,"mtime_nanos":924886254,"lines":581},"v3-notes/provider-test-pattern.md":{"mtime_secs":1767910210,"mtime_nanos":15350284,"lines":60},"examples/auth/azure_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":968397926,"lines":51},"examples/fastmcp_config_demo/fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":971255411,"lines":14},"examples/auth/workos_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":970195151,"lines":32},"tests/server/providers/test_local_provider_tools.py":{"mtime_secs":1768273914,"mtime_nanos":458671764,"lines":1554},"examples/sampling/server_fallback.py":{"mtime_secs":1767910209,"mtime_nanos":972350172,"lines":88},".git/objects/b8/6856651bde553af9d8eb4dc0e5071d3c8d5aa1":{"mtime_secs":1768264193,"mtime_nanos":370429063,"lines":1},"tests/cli/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":993941083,"lines":1},".git/logs/refs/heads/main":{"mtime_secs":1768307169,"mtime_nanos":292873529,"lines":18},"docs/snippets/version-badge.mdx":{"mtime_secs":1767910209,"mtime_nanos":965687359,"lines":7},"tests/resources/test_resource_template_meta.py":{"mtime_secs":1767910210,"mtime_nanos":1079442,"lines":25},".gitignore":{"mtime_secs":1768258809,"mtime_nanos":233149804,"lines":78},"src/fastmcp/server/providers/fastmcp_provider.py":{"mtime_secs":1768273914,"mtime_nanos":444544279,"lines":586},".github/workflows/run-tests.yml":{"mtime_secs":1767910209,"mtime_nanos":926165612,"lines":115},".git/refs/remotes/origin/marvin/update-sdk-docs":{"mtime_secs":1768089463,"mtime_nanos":978028759,"lines":1},"src/fastmcp/contrib/component_manager/component_manager.py":{"mtime_secs":1767910209,"mtime_nanos":979707033,"lines":186},"tests/utilities/test_tests.py":{"mtime_secs":1767910210,"mtime_nanos":14002730,"lines":10},"docs/integrations/google.mdx":{"mtime_secs":1767910209,"mtime_nanos":946224301,"lines":190},"examples/auth/discord_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":968815055,"lines":32},"tests/client/transports/test_uv_transport.py":{"mtime_secs":1767910209,"mtime_nanos":998203373,"lines":120},"examples/providers/sqlite/server.py":{"mtime_secs":1767910209,"mtime_nanos":972114669,"lines":137},"tests/server/tasks/test_task_meta_parameter.py":{"mtime_secs":1768275439,"mtime_nanos":342125571,"lines":314},".git/hooks/fsmonitor-watchman.sample":{"mtime_secs":1767910208,"mtime_nanos":709482747,"lines":174},"tests/server/auth/test_static_token_verifier.py":{"mtime_secs":1767910210,"mtime_nanos":3875135,"lines":112},"examples/atproto_mcp/fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":966618743,"lines":11},"tests/client/tasks/test_client_tool_tasks.py":{"mtime_secs":1767910209,"mtime_nanos":996490982,"lines":88},"examples/atproto_mcp/src/atproto_mcp/types.py":{"mtime_secs":1767910209,"mtime_nanos":967685295,"lines":142},"tests/deprecated/test_settings.py":{"mtime_secs":1767910209,"mtime_nanos":999584095,"lines":319},"src/fastmcp/server/auth/providers/workos.py":{"mtime_secs":1767910209,"mtime_nanos":984868373,"lines":334},"justfile":{"mtime_secs":1768065435,"mtime_nanos":785265946,"lines":33},"examples/simple_echo.py":{"mtime_secs":1767910209,"mtime_nanos":972764176,"lines":14},"tests/client/sampling/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":995675724,"lines":0},".git/objects/31/9dbc29ca3b5d2fa8002996bc59551ff71f9d70":{"mtime_secs":1768062579,"mtime_nanos":885083643,"lines":4},".git/objects/93/35be4d05ca027881cfcba80b81a6c1713a4f1d":{"mtime_secs":1768260294,"mtime_nanos":900190040,"lines":1},"tests/tools/test_tool_future_annotations.py":{"mtime_secs":1767910210,"mtime_nanos":12078754,"lines":170},"src/fastmcp/server/sampling/sampling_tool.py":{"mtime_secs":1768274887,"mtime_nanos":463792397,"lines":109},".git/logs/refs/remotes/origin/docs/2.14.3-release-notes":{"mtime_secs":1768257849,"mtime_nanos":588909791,"lines":2},"docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx":{"mtime_secs":1768065435,"mtime_nanos":777932327,"lines":450},"docs/python-sdk/fastmcp-server-tasks-capabilities.mdx":{"mtime_secs":1767910209,"mtime_nanos":959386550,"lines":24},"examples/atproto_mcp/pyproject.toml":{"mtime_secs":1767910209,"mtime_nanos":966687993,"lines":24},"tests/server/auth/test_redirect_validation.py":{"mtime_secs":1767910210,"mtime_nanos":3701592,"lines":141},"examples/providers/sqlite/README.md":{"mtime_secs":1767910209,"mtime_nanos":972017710,"lines":59},"docs/python-sdk/fastmcp-fs-provider.mdx":{"mtime_secs":1768065435,"mtime_nanos":776887191,"lines":111},"src/fastmcp/server/middleware/caching.py":{"mtime_secs":1767910209,"mtime_nanos":986217719,"lines":523},".git/hooks/pre-commit.sample":{"mtime_secs":1767910208,"mtime_nanos":709028535,"lines":49},".claude/skills/code-review/SKILL.md":{"mtime_secs":1768258809,"mtime_nanos":228829565,"lines":94},"tests/experimental/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":999851972,"lines":0},"docs/integrations/authkit.mdx":{"mtime_secs":1767910209,"mtime_nanos":944617328,"lines":97},"examples/auth/azure_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":968616595,"lines":44},"docs/python-sdk/fastmcp-server-auth-providers-scalekit.mdx":{"mtime_secs":1767910209,"mtime_nanos":956226145,"lines":61},"docs/css/version-badge.css":{"mtime_secs":1767910209,"mtime_nanos":942847894,"lines":23},"src/fastmcp/server/sampling/run.py":{"mtime_secs":1767910209,"mtime_nanos":988831452,"lines":301},"src/fastmcp/server/elicitation.py":{"mtime_secs":1768275640,"mtime_nanos":259244716,"lines":464},"docs/python-sdk/fastmcp-server-server.mdx":{"mtime_secs":1768065435,"mtime_nanos":780328686,"lines":999},"src/fastmcp/utilities/openapi/json_schema_converter.py":{"mtime_secs":1767910209,"mtime_nanos":992962449,"lines":344},"tests/utilities/test_types.py":{"mtime_secs":1768275527,"mtime_nanos":948940954,"lines":673},".git/objects/4c/60c479486a200c6964459fde312735a7e98b7f":{"mtime_secs":1768086708,"mtime_nanos":458223620,"lines":2},"docs/servers/authorization.mdx":{"mtime_secs":1768267998,"mtime_nanos":893587923,"lines":348},"tests/client/test_stdio.py":{"mtime_secs":1767910209,"mtime_nanos":997871370,"lines":364},"docs/servers/auth/remote-oauth.mdx":{"mtime_secs":1767994445,"mtime_nanos":311035162,"lines":226},"docs/python-sdk/fastmcp-utilities-openapi-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":961889240,"lines":9},"docs/clients/tasks.mdx":{"mtime_secs":1767910209,"mtime_nanos":942184180,"lines":138},"docs/integrations/cursor.mdx":{"mtime_secs":1767910209,"mtime_nanos":945471836,"lines":284},"tests/utilities/openapi/test_models.py":{"mtime_secs":1768275463,"mtime_nanos":454793530,"lines":476},"tests/server/tasks/test_task_dependencies.py":{"mtime_secs":1768275526,"mtime_nanos":565296627,"lines":273},"tests/server/test_dependencies.py":{"mtime_secs":1768091029,"mtime_nanos":869855251,"lines":1046},"src/fastmcp/tools/__init__.py":{"mtime_secs":1768273914,"mtime_nanos":450073905,"lines":12},"examples/tags_example.py":{"mtime_secs":1767910209,"mtime_nanos":973748226,"lines":141},"src/fastmcp/server/auth/providers/jwt.py":{"mtime_secs":1768275400,"mtime_nanos":826342650,"lines":527},"examples/sampling/text.py":{"mtime_secs":1767910209,"mtime_nanos":972486756,"lines":78},"docs/snippets/youtube-embed.mdx":{"mtime_secs":1767910209,"mtime_nanos":965751026,"lines":12},"src/fastmcp/utilities/tests.py":{"mtime_secs":1767910209,"mtime_nanos":993432787,"lines":286},"tests/server/tasks/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":8212759,"lines":1},"docs/python-sdk/fastmcp-mcp_config.mdx":{"mtime_secs":1768065435,"mtime_nanos":777111069,"lines":188},"docs/python-sdk/fastmcp-server-middleware-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":957037111,"lines":8},".git/objects/f3/d31eb8ff45646ba054dbe7b987651d3ebdb521":{"mtime_secs":1768239135,"mtime_nanos":940129268,"lines":2},"examples/auth/google_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":969466895,"lines":37},".git/objects/2f/565079e9b10d352c7239ad621fcc8598d5ed20":{"mtime_secs":1768062579,"mtime_nanos":824886000,"lines":1},"src/fastmcp/tools/tool.py":{"mtime_secs":1768273914,"mtime_nanos":451154831,"lines":482},"src/fastmcp/_vendor/docket_di/README.md":{"mtime_secs":1768091029,"mtime_nanos":862241388,"lines":7},"docs/clients/elicitation.mdx":{"mtime_secs":1767910209,"mtime_nanos":941396839,"lines":125},"src/fastmcp/server/tasks/requests.py":{"mtime_secs":1768091029,"mtime_nanos":868130360,"lines":426},"docs/python-sdk/fastmcp-tools-tool_transform.mdx":{"mtime_secs":1767910209,"mtime_nanos":960260975,"lines":310},"src/fastmcp/server/transforms/namespace.py":{"mtime_secs":1768273914,"mtime_nanos":448345558,"lines":188},"docs/servers/auth/token-verification.mdx":{"mtime_secs":1767910209,"mtime_nanos":963487422,"lines":333},"docs/integrations/gemini.mdx":{"mtime_secs":1767910209,"mtime_nanos":946005674,"lines":108},".git/logs/HEAD":{"mtime_secs":1768275805,"mtime_nanos":78401130,"lines":55},"src/fastmcp/utilities/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":990509176,"lines":1},"src/fastmcp/server/auth/providers/supabase.py":{"mtime_secs":1767910209,"mtime_nanos":984783914,"lines":164},"examples/filesystem-provider/mcp/prompts/assistant.py":{"mtime_secs":1768065435,"mtime_nanos":784210477,"lines":39},"tests/server/auth/test_jwt_provider.py":{"mtime_secs":1768275438,"mtime_nanos":820705989,"lines":1101},"docs/tutorials/create-mcp-server.mdx":{"mtime_secs":1767910209,"mtime_nanos":965895778,"lines":198},"docs/python-sdk/fastmcp-server-tasks-routing.mdx":{"mtime_secs":1767910209,"mtime_nanos":959793637,"lines":37},"src/fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py":{"mtime_secs":1768275416,"mtime_nanos":702325579,"lines":447},"src/fastmcp/contrib/component_manager/component_service.py":{"mtime_secs":1768273914,"mtime_nanos":439669741,"lines":275},"docs/python-sdk/fastmcp-server-middleware-timing.mdx":{"mtime_secs":1767910209,"mtime_nanos":957700700,"lines":105},"docs/python-sdk/fastmcp-client-tasks.mdx":{"mtime_secs":1767910209,"mtime_nanos":953645496,"lines":219},".git/logs/refs/remotes/origin/extract-parsedfunction":{"mtime_secs":1768307169,"mtime_nanos":50943729,"lines":2},"src/fastmcp/contrib/component_manager/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":979611615,"lines":4},"docs/python-sdk/fastmcp-server-lifespan.mdx":{"mtime_secs":1768065435,"mtime_nanos":779457593,"lines":101},".git/objects/68/785c1444ba42226616d47299fae0f392c5d399":{"mtime_secs":1768273107,"mtime_nanos":896549041,"lines":3},".github/workflows/run-static.yml":{"mtime_secs":1767910209,"mtime_nanos":926107028,"lines":54},".git/objects/ea/1a67ab3a6b6d08e1123a73d1bc8eb92dbff771":{"mtime_secs":1768239165,"mtime_nanos":730277220,"lines":3},"docs/integrations/supabase.mdx":{"mtime_secs":1767910209,"mtime_nanos":950541508,"lines":112},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config.mdx":{"mtime_secs":1767910209,"mtime_nanos":961622696,"lines":235},"tests/server/providers/test_local_provider_resources.py":{"mtime_secs":1768275439,"mtime_nanos":342156321,"lines":974},"tests/integration_tests/test_github_mcp_remote.py":{"mtime_secs":1767910210,"mtime_nanos":364144,"lines":123},"examples/fastmcp_config_demo/README.md":{"mtime_secs":1767910209,"mtime_nanos":971133452,"lines":55},"examples/testing_demo/server.py":{"mtime_secs":1767910209,"mtime_nanos":974447858,"lines":61},"CODE_OF_CONDUCT.md":{"mtime_secs":1767910209,"mtime_nanos":926670575,"lines":128},"examples/auth/scalekit_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":969898232,"lines":41},"tests/deprecated/test_function_component_imports.py":{"mtime_secs":1768273914,"mtime_nanos":452909053,"lines":121},"docs/python-sdk/fastmcp-server-auth-middleware.mdx":{"mtime_secs":1767910209,"mtime_nanos":954913966,"lines":26},"tests/server/sampling/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":8038674,"lines":0},".git/objects/cb/9fc0ca58c46cc07138bf43393b12ef01eda4b4":{"mtime_secs":1768257849,"mtime_nanos":518775600,"lines":2},"src/fastmcp/server/http.py":{"mtime_secs":1768275411,"mtime_nanos":464247158,"lines":382},"src/fastmcp/resources/template.py":{"mtime_secs":1768275400,"mtime_nanos":82008964,"lines":576},"examples/auth/workos_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":970270527,"lines":37},".git/objects/19/7fe9cf35897c43fb5d57867dd764138f5fc2ab":{"mtime_secs":1767926606,"mtime_nanos":96458141,"lines":2},"docs/servers/prompts.mdx":{"mtime_secs":1768273914,"mtime_nanos":435132289,"lines":447},"tests/prompts/test_standalone_decorator.py":{"mtime_secs":1768275437,"mtime_nanos":873943221,"lines":138},"src/fastmcp/resources/function_resource.py":{"mtime_secs":1768275399,"mtime_nanos":858506815,"lines":311},".ccignore":{"mtime_secs":1767910209,"mtime_nanos":924747432,"lines":7},".git/objects/9b/33a05d09785b4230a6451273c18304be7c0492":{"mtime_secs":1768239135,"mtime_nanos":936469614,"lines":2},"src/fastmcp/server/providers/openapi/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":987919818,"lines":39},"src/fastmcp/server/middleware/logging.py":{"mtime_secs":1767910209,"mtime_nanos":986532138,"lines":256},"docs/deployment/fastmcp-cloud.mdx":{"mtime_secs":1767910209,"mtime_nanos":943008979,"lines":89},"tests/server/middleware/test_error_handling.py":{"mtime_secs":1767910210,"mtime_nanos":4735185,"lines":652},"docs/python-sdk/fastmcp-server-middleware-error_handling.mdx":{"mtime_secs":1767910209,"mtime_nanos":957332864,"lines":60},"tests/server/auth/test_jwt_issuer.py":{"mtime_secs":1767910210,"mtime_nanos":2784666,"lines":230},"docs/integrations/openapi.mdx":{"mtime_secs":1767910209,"mtime_nanos":950315964,"lines":455},"tests/server/test_mount.py":{"mtime_secs":1768273914,"mtime_nanos":460305652,"lines":1560},"tests/test_mcp_config.py":{"mtime_secs":1768008814,"mtime_nanos":227519168,"lines":930},"docs/python-sdk/fastmcp-server-providers-transforming.mdx":{"mtime_secs":1767910209,"mtime_nanos":958857420,"lines":117},"src/fastmcp/server/auth/providers/aws.py":{"mtime_secs":1767910209,"mtime_nanos":983269149,"lines":194},"docs/integrations/aws-cognito.mdx":{"mtime_secs":1767910209,"mtime_nanos":944737995,"lines":279},"examples/screenshot.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":972635924,"lines":7},"src/fastmcp/server/providers/openapi/components.py":{"mtime_secs":1767910209,"mtime_nanos":988069653,"lines":332},"tests/integration_tests/auth/test_github_provider_integration.py":{"mtime_secs":1767910210,"mtime_nanos":179975,"lines":401},"docs/python-sdk/fastmcp-tools-tool.mdx":{"mtime_secs":1767910209,"mtime_nanos":960127473,"lines":183},"src/fastmcp/server/auth/redirect_validation.py":{"mtime_secs":1767910209,"mtime_nanos":984937332,"lines":65},".git/objects/cd/904efabf4a61a937065edd2987f5891d02d7b0":{"mtime_secs":1768243987,"mtime_nanos":509810050,"lines":1},"scripts/auto_close_needs_mre.py":{"mtime_secs":1767910209,"mtime_nanos":975791829,"lines":384},".git/objects/37/21dde8cfec40eb43bd4388a209a25840683666":{"mtime_secs":1768268287,"mtime_nanos":210062148,"lines":3},"tests/cli/test_cursor.py":{"mtime_secs":1767910209,"mtime_nanos":994239961,"lines":433},"tests/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":993815832,"lines":0},"docs/python-sdk/fastmcp-server-auth-jwt_issuer.mdx":{"mtime_secs":1767910209,"mtime_nanos":954849799,"lines":104},"src/fastmcp/server/middleware/timing.py":{"mtime_secs":1767910209,"mtime_nanos":986826808,"lines":156},"tests/client/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":995406347,"lines":0},"docs/python-sdk/fastmcp-client-sampling-handlers-anthropic.mdx":{"mtime_secs":1767910209,"mtime_nanos":953481661,"lines":17},".git/refs/remotes/origin/main":{"mtime_secs":1768274758,"mtime_nanos":708872106,"lines":1},"docs/.cursor/rules/mintlify.mdc":{"mtime_secs":1767910209,"mtime_nanos":927227914,"lines":364},"docs/python-sdk/fastmcp-fs-__init__.mdx":{"mtime_secs":1768065435,"mtime_nanos":775960848,"lines":43},".git/objects/27/67471e1ff17ae54ae8414a0f7dbda7b8c8ca23":{"mtime_secs":1768052340,"mtime_nanos":61644163,"lines":2},"docs/servers/lifespan.mdx":{"mtime_secs":1768267998,"mtime_nanos":896437900,"lines":143},"examples/auth/authkit_dcr/README.md":{"mtime_secs":1767910209,"mtime_nanos":967833754,"lines":25},"src/fastmcp/client/auth/bearer.py":{"mtime_secs":1767910209,"mtime_nanos":977402511,"lines":17},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-base.mdx":{"mtime_secs":1767910209,"mtime_nanos":961402777,"lines":43},"tests/server/auth/test_oauth_proxy_storage.py":{"mtime_secs":1767910210,"mtime_nanos":3496173,"lines":209},"docs/servers/resources.mdx":{"mtime_secs":1768273914,"mtime_nanos":437562516,"lines":731},"tests/server/tasks/test_custom_subclass_tasks.py":{"mtime_secs":1767910210,"mtime_nanos":8360302,"lines":184},"src/fastmcp/server/providers/proxy.py":{"mtime_secs":1768275412,"mtime_nanos":47513686,"lines":823},".git/objects/de/4d234c6bd2ea6aa73fa53984b455575f58d5ce":{"mtime_secs":1767969241,"mtime_nanos":781244482,"lines":1},"examples/testing_demo/pyproject.toml":{"mtime_secs":1767910209,"mtime_nanos":974385191,"lines":18},"docs/python-sdk/fastmcp-fs-discovery.mdx":{"mtime_secs":1768065435,"mtime_nanos":776652105,"lines":103},"tests/cli/test_mcp_server_config_schema.py":{"mtime_secs":1767910209,"mtime_nanos":994628340,"lines":172},"docs/python-sdk/fastmcp-client-auth-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":952495652,"lines":8},".git/objects/72/6976a99f9047380adade068b0f62fff32db528":{"mtime_secs":1767994305,"mtime_nanos":379680587,"lines":1},".git/refs/remotes/origin/docs/2.14.3-release-notes":{"mtime_secs":1768257849,"mtime_nanos":588603706,"lines":1},".git/objects/7f/2c4c108dac2b2aff8177cca5d47b6f3f5595bf":{"mtime_secs":1768274969,"mtime_nanos":977579000,"lines":1},"tests/utilities/openapi/conftest.py":{"mtime_secs":1767910210,"mtime_nanos":12521841,"lines":222},"examples/auth/discord_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":968888139,"lines":35},"tests/server/auth/providers/test_azure.py":{"mtime_secs":1767910210,"mtime_nanos":1640864,"lines":809},"tests/cli/test_run_config.py":{"mtime_secs":1767910209,"mtime_nanos":995001801,"lines":312},".github/workflows/update-sdk-docs.yml":{"mtime_secs":1767910209,"mtime_nanos":926281071,"lines":74},"src/fastmcp/server/middleware/__init__.py":{"mtime_secs":1768267998,"mtime_nanos":906111003,"lines":15},".github/dependabot.yml":{"mtime_secs":1767910209,"mtime_nanos":925259687,"lines":20},"src/fastmcp/utilities/openapi/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":992693280,"lines":63},"docs/clients/transports.mdx":{"mtime_secs":1767910209,"mtime_nanos":942395223,"lines":383},"tests/utilities/openapi/test_director.py":{"mtime_secs":1767910210,"mtime_nanos":12861053,"lines":462},".git/objects/81/69bc871348c622817043ae2a2cc69915daeef8":{"mtime_secs":1767925044,"mtime_nanos":717035266,"lines":1},"docs/python-sdk/fastmcp-client-oauth_callback.mdx":{"mtime_secs":1767910209,"mtime_nanos":953129908,"lines":70},"docs/python-sdk/fastmcp-server-auth-providers-descope.mdx":{"mtime_secs":1767910209,"mtime_nanos":955584222,"lines":60},"tests/utilities/openapi/test_legacy_compatibility.py":{"mtime_secs":1767910210,"mtime_nanos":12980679,"lines":192},".git/objects/cb/7f22fd7fe77f5d6debbf0d4384697a05ea0c5a":{"mtime_secs":1767997243,"mtime_nanos":68587672,"lines":1},"docs/updates.mdx":{"mtime_secs":1768258809,"mtime_nanos":241599904,"lines":413},"src/fastmcp/server/tasks/config.py":{"mtime_secs":1768091029,"mtime_nanos":867540563,"lines":152},"docs/deployment/http.mdx":{"mtime_secs":1767910209,"mtime_nanos":943106230,"lines":736},"src/fastmcp/server/transforms/__init__.py":{"mtime_secs":1768273914,"mtime_nanos":448032140,"lines":206},"docs/python-sdk/fastmcp-cli-install-shared.mdx":{"mtime_secs":1767910209,"mtime_nanos":952237024,"lines":33},"examples/tasks/README.md":{"mtime_secs":1767910209,"mtime_nanos":973995479,"lines":60},"src/fastmcp/client/progress.py":{"mtime_secs":1767910209,"mtime_nanos":978046642,"lines":38},"docs/python-sdk/fastmcp-server-context.mdx":{"mtime_secs":1768065435,"mtime_nanos":778914671,"lines":550},"docs/python-sdk/fastmcp-server-sampling-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":958973587,"lines":9},"tests/utilities/test_json_schema_type.py":{"mtime_secs":1767910210,"mtime_nanos":13865979,"lines":1584},"docs/python-sdk/fastmcp-server-auth-redirect_validation.mdx":{"mtime_secs":1767910209,"mtime_nanos":956452022,"lines":53},"tests/deprecated/test_add_tool_transformation.py":{"mtime_secs":1768273914,"mtime_nanos":452351048,"lines":104},".git/objects/37/3e318a95450b58f8b20a92cbf27caac2fc2342":{"mtime_secs":1768267927,"mtime_nanos":435378629,"lines":1},".git/objects/0e/9e0ff5d475ea45930b384363d976ec95c1b924":{"mtime_secs":1768258466,"mtime_nanos":330572205,"lines":2},"docs/python-sdk/fastmcp-cli-cli.mdx":{"mtime_secs":1768065435,"mtime_nanos":774813294,"lines":129},"tests/client/test_client.py":{"mtime_secs":1768008814,"mtime_nanos":227071121,"lines":1438},"docs/servers/elicitation.mdx":{"mtime_secs":1767910209,"mtime_nanos":963706257,"lines":462},".git/objects/25/a1f02624c36d90a452fe2cc7dc59f25cb58e12":{"mtime_secs":1767926596,"mtime_nanos":883682449,"lines":2},"src/fastmcp/utilities/types.py":{"mtime_secs":1768275416,"mtime_nanos":475484989,"lines":496},"src/fastmcp/server/auth/oauth_proxy.py":{"mtime_secs":1768275400,"mtime_nanos":665658828,"lines":2282},"tests/server/http/test_bearer_auth_backend.py":{"mtime_secs":1768275438,"mtime_nanos":343602974,"lines":179},"src/fastmcp/py.typed":{"mtime_secs":1767910209,"mtime_nanos":981481633,"lines":0},".git/hooks/push-to-checkout.sample":{"mtime_secs":1767910208,"mtime_nanos":711401932,"lines":78},"src/fastmcp/server/transforms/visibility.py":{"mtime_secs":1768273914,"mtime_nanos":448909855,"lines":304},"v3-notes/prompt-internal-types.md":{"mtime_secs":1767910210,"mtime_nanos":15218783,"lines":121},"src/fastmcp/client/oauth_callback.py":{"mtime_secs":1767910209,"mtime_nanos":977978183,"lines":242},"tests/resources/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":639313,"lines":0},"tests/server/test_log_level.py":{"mtime_secs":1767910210,"mtime_nanos":10843825,"lines":88},"tests/client/tasks/test_client_task_protocol.py":{"mtime_secs":1767910209,"mtime_nanos":996408940,"lines":85},"src/fastmcp/server/auth/providers/in_memory.py":{"mtime_secs":1767910209,"mtime_nanos":984178533,"lines":364},"docs/python-sdk/fastmcp-utilities-openapi-director.mdx":{"mtime_secs":1767910209,"mtime_nanos":961954824,"lines":36},"tests/fs/test_discovery.py":{"mtime_secs":1768065435,"mtime_nanos":794675879,"lines":353},"docs/python-sdk/fastmcp-client-client.mdx":{"mtime_secs":1768065435,"mtime_nanos":775437843,"lines":704},".git/objects/d5/3bd83853418fecbe901cdb7971a9bdafa995b0":{"mtime_secs":1767994305,"mtime_nanos":375220000,"lines":2},"tests/resources/test_file_resources.py":{"mtime_secs":1767910210,"mtime_nanos":751897,"lines":121},"docs/python-sdk/fastmcp-utilities-openapi-json_schema_converter.mdx":{"mtime_secs":1767910209,"mtime_nanos":962117325,"lines":62},"examples/auth/authkit_dcr/server.py":{"mtime_secs":1767910209,"mtime_nanos":967980006,"lines":33},"docs/servers/providers/filesystem.mdx":{"mtime_secs":1768065435,"mtime_nanos":782146372,"lines":255},"src/fastmcp/client/client.py":{"mtime_secs":1768275398,"mtime_nanos":55253387,"lines":1722},".git/objects/54/c1f3adc038af7afd824ece430df893a9c1bf9f":{"mtime_secs":1768275805,"mtime_nanos":77719375,"lines":3},"tests/server/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":1230985,"lines":0},"examples/testing_demo/README.md":{"mtime_secs":1767910209,"mtime_nanos":974324648,"lines":84},"tests/utilities/test_visibility.py":{"mtime_secs":1768273914,"mtime_nanos":462834797,"lines":118},"tests/client/tasks/test_task_result_caching.py":{"mtime_secs":1767910209,"mtime_nanos":996716526,"lines":325},"tests/server/middleware/test_rate_limiting.py":{"mtime_secs":1767910210,"mtime_nanos":5112397,"lines":457},"tests/tools/test_tool.py":{"mtime_secs":1768275439,"mtime_nanos":994409388,"lines":1923},"docs/python-sdk/fastmcp-fs-decorators.mdx":{"mtime_secs":1768065435,"mtime_nanos":776280351,"lines":156},".github/workflows/update-config-schema.yml":{"mtime_secs":1767910209,"mtime_nanos":926226363,"lines":91},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base.mdx":{"mtime_secs":1767910209,"mtime_nanos":961754197,"lines":42},".github/workflows/marvin-dedupe-issues.yml":{"mtime_secs":1767966312,"mtime_nanos":628563953,"lines":95},"tests/server/http/test_custom_routes.py":{"mtime_secs":1767910210,"mtime_nanos":4124137,"lines":105},"src/fastmcp/server/auth/handlers/authorize.py":{"mtime_secs":1767910209,"mtime_nanos":982424516,"lines":326},"tests/server/tasks/test_task_mount.py":{"mtime_secs":1767910210,"mtime_nanos":9343228,"lines":965},"src/fastmcp/utilities/inspect.py":{"mtime_secs":1767910209,"mtime_nanos":991093306,"lines":494},"src/fastmcp/server/auth/oidc_proxy.py":{"mtime_secs":1767994445,"mtime_nanos":315804138,"lines":419},"docs/integrations/azure.mdx":{"mtime_secs":1767910209,"mtime_nanos":944826829,"lines":281},"docs/python-sdk/fastmcp-client-transports.mdx":{"mtime_secs":1768065435,"mtime_nanos":775719304,"lines":297},"v3-notes/resource-internal-types.md":{"mtime_secs":1767910210,"mtime_nanos":15442160,"lines":196},"tests/deprecated/test_import_server.py":{"mtime_secs":1768273914,"mtime_nanos":453311931,"lines":714},"tests/cli/test_with_argv.py":{"mtime_secs":1767910209,"mtime_nanos":995324471,"lines":91},"src/fastmcp/cli/install/cursor.py":{"mtime_secs":1767910209,"mtime_nanos":976640254,"lines":333},"src/fastmcp/server/middleware/authorization.py":{"mtime_secs":1768267998,"mtime_nanos":907053550,"lines":299},"src/fastmcp/resources/types.py":{"mtime_secs":1767910209,"mtime_nanos":981937470,"lines":172},".git/objects/76/2c6269085b45785c856660da44aeb26922de38":{"mtime_secs":1768260294,"mtime_nanos":920717306,"lines":1},"scripts/auto_close_duplicates.py":{"mtime_secs":1767910209,"mtime_nanos":975661203,"lines":331},".git/refs/remotes/origin/metadata-decorators":{"mtime_secs":1768270101,"mtime_nanos":748739992,"lines":1},"docs/python-sdk/fastmcp-utilities-http.mdx":{"mtime_secs":1767910209,"mtime_nanos":960742854,"lines":18},"src/fastmcp/contrib/mcp_mixin/README.md":{"mtime_secs":1767910209,"mtime_nanos":980068411,"lines":147},"docs/python-sdk/fastmcp-server-auth-providers-aws.mdx":{"mtime_secs":1767910209,"mtime_nanos":955292595,"lines":82},"tests/server/auth/providers/test_descope.py":{"mtime_secs":1767910210,"mtime_nanos":1776032,"lines":238},"docs/python-sdk/fastmcp-cli-install-claude_desktop.mdx":{"mtime_secs":1767910209,"mtime_nanos":951999480,"lines":58},"docs/clients/progress.mdx":{"mtime_secs":1767910209,"mtime_nanos":941630674,"lines":70},"tests/server/providers/proxy/test_proxy_client.py":{"mtime_secs":1768275463,"mtime_nanos":382953013,"lines":430},"tests/server/providers/test_local_provider_prompts.py":{"mtime_secs":1768275439,"mtime_nanos":331951498,"lines":472},".git/objects/f6/a9d43ef0746d2da0a90b41a3ece2e33898d749":{"mtime_secs":1768270101,"mtime_nanos":714676512,"lines":2},"examples/smart_home/README.md":{"mtime_secs":1767910209,"mtime_nanos":972864843,"lines":15},"src/fastmcp/prompts/prompt.py":{"mtime_secs":1768273914,"mtime_nanos":441505130,"lines":425},"tests/client/transports/test_transports.py":{"mtime_secs":1767910209,"mtime_nanos":998136498,"lines":42},"docs/python-sdk/fastmcp-cli-run.mdx":{"mtime_secs":1768065435,"mtime_nanos":774987338,"lines":117},"tests/utilities/openapi/test_allof_requestbody.py":{"mtime_secs":1767910210,"mtime_nanos":12613300,"lines":227},"tests/utilities/test_json_schema.py":{"mtime_secs":1767966312,"mtime_nanos":635623411,"lines":543},"docs/python-sdk/fastmcp-utilities-exceptions.mdx":{"mtime_secs":1767910209,"mtime_nanos":960675020,"lines":20},"tests/server/auth/providers/test_aws.py":{"mtime_secs":1767910210,"mtime_nanos":1558488,"lines":107},"src/fastmcp/cli/install/claude_desktop.py":{"mtime_secs":1767910209,"mtime_nanos":976514544,"lines":210},"tests/contrib/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":998376833,"lines":1},"src/fastmcp/server/server.py":{"mtime_secs":1768275412,"mtime_nanos":450905711,"lines":2942},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":961254151,"lines":8},".git/logs/refs/remotes/origin/HEAD":{"mtime_secs":1768307169,"mtime_nanos":241461629,"lines":56},".git/objects/62/d50edd711afd1f6c1284ad63568a917e64fd8b":{"mtime_secs":1767936789,"mtime_nanos":901827035,"lines":2},"tests/server/middleware/test_middleware.py":{"mtime_secs":1768275520,"mtime_nanos":313642874,"lines":1070},"tests/server/providers/test_local_provider.py":{"mtime_secs":1768273914,"mtime_nanos":457293045,"lines":784},"docs/python-sdk/fastmcp-cli-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":951712644,"lines":9},"examples/tasks/client.py":{"mtime_secs":1767910209,"mtime_nanos":974091146,"lines":160},"docs/servers/middleware.mdx":{"mtime_secs":1768273914,"mtime_nanos":434736161,"lines":846},"docs/python-sdk/fastmcp-cli-tasks.mdx":{"mtime_secs":1767910209,"mtime_nanos":952379192,"lines":41},"tests/server/auth/providers/test_auth0.py":{"mtime_secs":1767910210,"mtime_nanos":1466446,"lines":98},"tests/server/auth/test_debug_verifier.py":{"mtime_secs":1767910210,"mtime_nanos":2589998,"lines":169},"v3-notes/get-methods-consolidation.md":{"mtime_secs":1767910210,"mtime_nanos":15154783,"lines":69},"AGENTS.md":{"mtime_secs":1768273914,"mtime_nanos":432682436,"lines":116},"docs/servers/server.mdx":{"mtime_secs":1768065435,"mtime_nanos":783859515,"lines":404},"tests/client/tasks/test_client_prompt_tasks.py":{"mtime_secs":1767910209,"mtime_nanos":996158187,"lines":103},"docs/python-sdk/fastmcp-server-auth-providers-github.mdx":{"mtime_secs":1767910209,"mtime_nanos":955718849,"lines":66},"docs/python-sdk/fastmcp-server-providers-local_provider.mdx":{"mtime_secs":1767910209,"mtime_nanos":958449999,"lines":372},"src/fastmcp/server/low_level.py":{"mtime_secs":1767910209,"mtime_nanos":986025759,"lines":290},".git/objects/9a/ff985badef9af930ce45850d2407c473da973e":{"mtime_secs":1768273107,"mtime_nanos":900478739,"lines":2},"src/fastmcp/experimental/sampling/handlers/openai.py":{"mtime_secs":1767910209,"mtime_nanos":980797834,"lines":5},"tests/client/transports/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":998056205,"lines":0},"examples/mount_example.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":971796458,"lines":4},"src/fastmcp/server/auth/providers/descope.py":{"mtime_secs":1767910209,"mtime_nanos":983615361,"lines":198},"docs/python-sdk/fastmcp-cli-install-mcp_json.mdx":{"mtime_secs":1767910209,"mtime_nanos":952176565,"lines":49},".git/objects/cf/72cd4938c4fc6f7a8a16554ceefc9404fa5870":{"mtime_secs":1768008006,"mtime_nanos":563809624,"lines":4},"tests/client/sampling/handlers/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":995786726,"lines":0},".git/objects/d9/ee2a1c35476aaf5706c39d6669fa22bde7cb57":{"mtime_secs":1768062579,"mtime_nanos":840053345,"lines":4},"tests/server/providers/openapi/test_end_to_end_compatibility.py":{"mtime_secs":1767910210,"mtime_nanos":6388742,"lines":221},"src/fastmcp/experimental/server/openapi/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":980948086,"lines":40},"docs/python-sdk/fastmcp-server-auth-providers-auth0.mdx":{"mtime_secs":1767910209,"mtime_nanos":955218344,"lines":41},"src/fastmcp/client/transports.py":{"mtime_secs":1768275399,"mtime_nanos":20196202,"lines":1208},"docs/clients/roots.mdx":{"mtime_secs":1767910209,"mtime_nanos":941919385,"lines":42},"src/fastmcp/cli/__main__.py":{"mtime_secs":1767910209,"mtime_nanos":976096457,"lines":5},"docs/python-sdk/fastmcp-utilities-components.mdx":{"mtime_secs":1767910209,"mtime_nanos":960609478,"lines":115},"docs/python-sdk/fastmcp-server-middleware-middleware.mdx":{"mtime_secs":1767910209,"mtime_nanos":957556241,"lines":112},"docs/deployment/running-server.mdx":{"mtime_secs":1767966312,"mtime_nanos":924405293,"lines":282},"src/fastmcp/dependencies.py":{"mtime_secs":1768091029,"mtime_nanos":862680809,"lines":36},".git/objects/a1/7a4142d8f032cee80c7b57ff95dd0dfe931709":{"mtime_secs":1768257849,"mtime_nanos":517889178,"lines":3},".git/objects/0b/a7dfdbb7f2c3eeda565e5e09f375d9f6928592":{"mtime_secs":1768086974,"mtime_nanos":70925454,"lines":2},"docs/patterns/contrib.mdx":{"mtime_secs":1767910209,"mtime_nanos":950960429,"lines":45},"src/fastmcp/server/auth/providers/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":983013772,"lines":0},"examples/fastmcp_config/server.py":{"mtime_secs":1767910209,"mtime_nanos":970957367,"lines":39},"src/fastmcp/contrib/mcp_mixin/mcp_mixin.py":{"mtime_secs":1767910209,"mtime_nanos":980336955,"lines":283},"tests/server/middleware/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":4543891,"lines":0},"src/fastmcp/utilities/exceptions.py":{"mtime_secs":1767910209,"mtime_nanos":990943263,"lines":49},"examples/auth/authkit_dcr/client.py":{"mtime_secs":1767910209,"mtime_nanos":967906296,"lines":32},".github/workflows/martian-test-failure.yml":{"mtime_secs":1767966312,"mtime_nanos":628238410,"lines":185},"src/fastmcp/server/providers/local_provider.py":{"mtime_secs":1768273914,"mtime_nanos":445853789,"lines":962},"docs/python-sdk/fastmcp-client-auth-bearer.mdx":{"mtime_secs":1767910209,"mtime_nanos":952567611,"lines":18},"docs/python-sdk/fastmcp-resources-resource.mdx":{"mtime_secs":1767910209,"mtime_nanos":954293002,"lines":214},"tests/server/auth/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":1309152,"lines":0},"examples/atproto_mcp/demo.py":{"mtime_secs":1767910209,"mtime_nanos":966549075,"lines":257},"tests/server/test_server.py":{"mtime_secs":1768273914,"mtime_nanos":461020449,"lines":458},"docs/python-sdk/fastmcp-utilities-mcp_server_config-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":961183233,"lines":13},"docs/python-sdk/fastmcp-client-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":952443068,"lines":8},"src/fastmcp/server/providers/filesystem_discovery.py":{"mtime_secs":1768273914,"mtime_nanos":445485620,"lines":327},"docs/servers/context.mdx":{"mtime_secs":1768267998,"mtime_nanos":895493810,"lines":650},"docs/integrations/oci.mdx":{"mtime_secs":1767910209,"mtime_nanos":950135838,"lines":249},"examples/smart_home/src/smart_home/hub.py":{"mtime_secs":1767966312,"mtime_nanos":925390299,"lines":31},".git/logs/refs/remotes/origin/metadata-decorators":{"mtime_secs":1768270101,"mtime_nanos":749305663,"lines":4},".git/COMMIT_EDITMSG":{"mtime_secs":1768275805,"mtime_nanos":77533624,"lines":4},"docs/servers/auth/oauth-proxy.mdx":{"mtime_secs":1767910209,"mtime_nanos":963100585,"lines":590},"docs/python-sdk/fastmcp-client-progress.mdx":{"mtime_secs":1767910209,"mtime_nanos":953193492,"lines":25},"src/fastmcp/server/auth/providers/azure.py":{"mtime_secs":1767910209,"mtime_nanos":983391775,"lines":358},"src/fastmcp/decorators.py":{"mtime_secs":1768273914,"mtime_nanos":440038410,"lines":41},"docs/development/releases.mdx":{"mtime_secs":1767910209,"mtime_nanos":943595068,"lines":79},"tests/utilities/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":12317048,"lines":1},".git/objects/98/c654b8693488ba02040fe9edda733a2f9f8c96":{"mtime_secs":1768260294,"mtime_nanos":894109748,"lines":3},"tests/server/test_app_state.py":{"mtime_secs":1767910210,"mtime_nanos":10038193,"lines":26},"src/fastmcp/utilities/mcp_server_config/v1/environments/base.py":{"mtime_secs":1767910209,"mtime_nanos":991865564,"lines":29},"tests/server/auth/providers/test_workos.py":{"mtime_secs":1767910210,"mtime_nanos":2371579,"lines":152},"examples/filesystem-provider/mcp/resources/config.py":{"mtime_secs":1768065435,"mtime_nanos":784378770,"lines":55},"docs/python-sdk/fastmcp-server-middleware-tool_injection.mdx":{"mtime_secs":1767910209,"mtime_nanos":957778243,"lines":91},"src/fastmcp/server/middleware/middleware.py":{"mtime_secs":1767910209,"mtime_nanos":986644598,"lines":205},"docs/python-sdk/fastmcp-server-middleware-logging.mdx":{"mtime_secs":1767910209,"mtime_nanos":957453865,"lines":58},"src/fastmcp/tools/function_parsing.py":{"mtime_secs":1768275412,"mtime_nanos":738988075,"lines":201},"examples/filesystem-provider/mcp/tools/greeting.py":{"mtime_secs":1768065435,"mtime_nanos":784707482,"lines":28},"docs/python-sdk/fastmcp-server-auth-oidc_proxy.mdx":{"mtime_secs":1767910209,"mtime_nanos":955077926,"lines":82},"docs/python-sdk/fastmcp-utilities-types.mdx":{"mtime_secs":1767910209,"mtime_nanos":962493496,"lines":169},"tests/cli/test_mcp_server_config_integration.py":{"mtime_secs":1768275457,"mtime_nanos":35815783,"lines":293},"tests/server/tasks/test_sync_function_task_disabled.py":{"mtime_secs":1768273914,"mtime_nanos":459901149,"lines":229},"src/fastmcp/utilities/version_check.py":{"mtime_secs":1768258809,"mtime_nanos":250704508,"lines":153},"src/fastmcp/server/event_store.py":{"mtime_secs":1767910209,"mtime_nanos":985794840,"lines":177},".git/objects/fe/630a275a451a5d7dd8a70942990a6bd97ae895":{"mtime_secs":1768269718,"mtime_nanos":997369257,"lines":1},"tests/server/tasks/test_task_methods.py":{"mtime_secs":1767910210,"mtime_nanos":9256061,"lines":217},"src/fastmcp/utilities/cli.py":{"mtime_secs":1768258809,"mtime_nanos":250333464,"lines":281},"tests/integration_tests/conftest.py":{"mtime_secs":1767910210,"mtime_nanos":263434,"lines":72},".git/objects/a7/563df98b7e4ffdc0b3f18183317b582a1e716b":{"mtime_secs":1768275805,"mtime_nanos":65319911,"lines":4},"tests/server/http/test_http_dependencies.py":{"mtime_secs":1767910210,"mtime_nanos":4296431,"lines":128},"src/fastmcp/utilities/http.py":{"mtime_secs":1767910209,"mtime_nanos":991005389,"lines":8},"src/fastmcp/contrib/bulk_tool_caller/example.py":{"mtime_secs":1767910209,"mtime_nanos":979380154,"lines":17},"src/fastmcp/cli/install/mcp_json.py":{"mtime_secs":1767910209,"mtime_nanos":976825214,"lines":191},"src/fastmcp/server/providers/openapi/provider.py":{"mtime_secs":1767910209,"mtime_nanos":988137612,"lines":384},"examples/fastmcp_config/simple.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":971018868,"lines":7},"tests/utilities/openapi/test_transitive_references.py":{"mtime_secs":1767910210,"mtime_nanos":13368933,"lines":842},"src/fastmcp/resources/resource.py":{"mtime_secs":1768273914,"mtime_nanos":443312394,"lines":443},"tests/server/auth/test_enhanced_error_responses.py":{"mtime_secs":1767910210,"mtime_nanos":2661749,"lines":364},"tests/server/auth/providers/test_discord.py":{"mtime_secs":1767910210,"mtime_nanos":1849033,"lines":70},"examples/smart_home/src/smart_home/__main__.py":{"mtime_secs":1767910209,"mtime_nanos":973255388,"lines":9},".git/logs/refs/heads/feature/reload-flag":{"mtime_secs":1767927080,"mtime_nanos":441195568,"lines":6},"src/fastmcp/client/elicitation.py":{"mtime_secs":1767910209,"mtime_nanos":977704347,"lines":80},"docs/integrations/discord.mdx":{"mtime_secs":1767910209,"mtime_nanos":945622545,"lines":184},"src/fastmcp/server/providers/aggregate.py":{"mtime_secs":1768273914,"mtime_nanos":443889815,"lines":207},"pyproject.toml":{"mtime_secs":1768275127,"mtime_nanos":809106522,"lines":199},"docs/python-sdk/fastmcp-server-auth-providers-debug.mdx":{"mtime_secs":1767910209,"mtime_nanos":955510138,"lines":70},"docs/servers/providers/custom.mdx":{"mtime_secs":1767910209,"mtime_nanos":964369180,"lines":241},".git/objects/75/af171cb9d1dc8540209c0bcaf469419fbe32fb":{"mtime_secs":1768065435,"mtime_nanos":673049972,"lines":1},"docs/python-sdk/fastmcp-server-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":954571879,"lines":8},"docs/python-sdk/fastmcp-prompts-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":954008249,"lines":8},"src/fastmcp/server/auth/providers/debug.py":{"mtime_secs":1767910209,"mtime_nanos":983478151,"lines":114},"docs/community/showcase.mdx":{"mtime_secs":1767910209,"mtime_nanos":942564725,"lines":65},"docs/clients/auth/bearer.mdx":{"mtime_secs":1767910209,"mtime_nanos":941034794,"lines":88},"tests/integration_tests/auth/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":70224,"lines":0},"src/fastmcp/utilities/mcp_server_config/v1/sources/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":992272359,"lines":0},"docs/clients/auth/oauth.mdx":{"mtime_secs":1767910209,"mtime_nanos":941154961,"lines":132},"tests/server/auth/providers/test_google.py":{"mtime_secs":1767910210,"mtime_nanos":2030118,"lines":118},".git/logs/refs/remotes/origin/release/2.x":{"mtime_secs":1768258884,"mtime_nanos":704964868,"lines":5},"docs/integrations/mcp-json-configuration.mdx":{"mtime_secs":1767910209,"mtime_nanos":949996545,"lines":422},"examples/sampling/structured_output.py":{"mtime_secs":1767910209,"mtime_nanos":972414464,"lines":110},"examples/atproto_mcp/src/atproto_mcp/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":966835036,"lines":3},".git/hooks/pre-rebase.sample":{"mtime_secs":1767910208,"mtime_nanos":708822658,"lines":169},".git/objects/f2/62d9de7f50fc6133bc108376e5da45f11f0d20":{"mtime_secs":1768267926,"mtime_nanos":725680430,"lines":2},"src/fastmcp/cli/tasks.py":{"mtime_secs":1767910209,"mtime_nanos":977123758,"lines":110},"src/fastmcp/server/lifespan.py":{"mtime_secs":1768275411,"mtime_nanos":615886206,"lines":198},".git/objects/7c/7064e7b31cdfc85810d317c89218d5bc3599c6":{"mtime_secs":1768062579,"mtime_nanos":836300417,"lines":2},"src/fastmcp/utilities/async_utils.py":{"mtime_secs":1767910209,"mtime_nanos":990579635,"lines":56},"examples/text_me.py":{"mtime_secs":1767910209,"mtime_nanos":975166490,"lines":72},"tests/prompts/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":444353,"lines":0},".git/refs/remotes/origin/release/2.x":{"mtime_secs":1768258884,"mtime_nanos":704644282,"lines":1},"tests/tools/test_tool_transform.py":{"mtime_secs":1768273914,"mtime_nanos":462189625,"lines":1748},"src/fastmcp/cli/install/shared.py":{"mtime_secs":1767910209,"mtime_nanos":976920215,"lines":127},"docs/python-sdk/fastmcp-utilities-tests.mdx":{"mtime_secs":1767910209,"mtime_nanos":962405120,"lines":106},"docs/getting-started/quickstart.mdx":{"mtime_secs":1767910209,"mtime_nanos":944227157,"lines":136},"LICENSE":{"mtime_secs":1767910209,"mtime_nanos":926792951,"lines":201},"docs/development/tests.mdx":{"mtime_secs":1767910209,"mtime_nanos":943683277,"lines":396},"docs/python-sdk/fastmcp-client-elicitation.mdx":{"mtime_secs":1767910209,"mtime_nanos":952787988,"lines":18},"src/fastmcp/server/openapi/routing.py":{"mtime_secs":1767910209,"mtime_nanos":987139311,"lines":46},".git/objects/8e/72b7124ee74702ac890c0dfc0d4c861f6b0cc1":{"mtime_secs":1767980392,"mtime_nanos":450449955,"lines":1},"docs/python-sdk/fastmcp-cli-install-cursor.mdx":{"mtime_secs":1767910209,"mtime_nanos":952060147,"lines":107},".claude/skills/python-tests/SKILL.md":{"mtime_secs":1768258809,"mtime_nanos":229630363,"lines":220},"examples/auth/scalekit_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":969967691,"lines":58},"tests/server/middleware/test_caching.py":{"mtime_secs":1768273914,"mtime_nanos":455815867,"lines":617},"docs/python-sdk/fastmcp-utilities-openapi-formatters.mdx":{"mtime_secs":1767910209,"mtime_nanos":962050533,"lines":115},"docs/python-sdk/fastmcp-utilities-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":960333309,"lines":9},"tests/deprecated/server/test_include_exclude_tags.py":{"mtime_secs":1767910209,"mtime_nanos":999204758,"lines":50},"src/fastmcp/utilities/auth.py":{"mtime_secs":1767910209,"mtime_nanos":990662719,"lines":34},"docs/clients/messages.mdx":{"mtime_secs":1767910209,"mtime_nanos":941569007,"lines":129},"docs/servers/sampling.mdx":{"mtime_secs":1767910209,"mtime_nanos":965018853,"lines":488},"examples/fastmcp_config/full_example.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":970897700,"lines":27},"examples/memory.py":{"mtime_secs":1767910209,"mtime_nanos":971732666,"lines":341},"docs/integrations/auth0.mdx":{"mtime_secs":1767910209,"mtime_nanos":944548410,"lines":196},"docs/integrations/workos.mdx":{"mtime_secs":1767910209,"mtime_nanos":950630384,"lines":201},"tests/integration_tests/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":999967723,"lines":0},"src/fastmcp/client/__init__.py":{"mtime_secs":1768008814,"mtime_nanos":226119109,"lines":30},".git/objects/e3/1e3fddb2bf4e0da2b4036ac7a562ecb8c8474e":{"mtime_secs":1768077500,"mtime_nanos":171713640,"lines":1},"docs/snippets/local-focus.mdx":{"mtime_secs":1767910209,"mtime_nanos":965621067,"lines":7},"docs/patterns/testing.mdx":{"mtime_secs":1767910209,"mtime_nanos":951143389,"lines":104},"docs/assets/brand/favicon.svg":{"mtime_secs":1767910209,"mtime_nanos":927970754,"lines":3},"tests/server/test_tool_transformation.py":{"mtime_secs":1768273914,"mtime_nanos":461395160,"lines":120},"tests/server/providers/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":5479067,"lines":0},"docs/integrations/chatgpt.mdx":{"mtime_secs":1767910209,"mtime_nanos":944944831,"lines":157},"tests/resources/test_function_resources.py":{"mtime_secs":1768273914,"mtime_nanos":454083728,"lines":278},"src/fastmcp/client/tasks.py":{"mtime_secs":1768275398,"mtime_nanos":912262801,"lines":551},"src/fastmcp/utilities/lifespan.py":{"mtime_secs":1768065435,"mtime_nanos":794376750,"lines":56},".github/workflows/marvin.yml":{"mtime_secs":1767966312,"mtime_nanos":629167665,"lines":81},"src/fastmcp/server/openapi/server.py":{"mtime_secs":1767910209,"mtime_nanos":987208020,"lines":127},"docs/integrations/descope.mdx":{"mtime_secs":1767910209,"mtime_nanos":945529961,"lines":114},"examples/atproto_mcp/src/atproto_mcp/_atproto/_read.py":{"mtime_secs":1767910209,"mtime_nanos":967326749,"lines":124},"src/fastmcp/server/providers/openapi/README.md":{"mtime_secs":1767910209,"mtime_nanos":987850984,"lines":266},"tests/server/http/test_stale_access_token.py":{"mtime_secs":1767910210,"mtime_nanos":4471766,"lines":159},"tests/server/providers/openapi/test_performance_comparison.py":{"mtime_secs":1767910210,"mtime_nanos":6845496,"lines":286},"tests/server/providers/openapi/test_server.py":{"mtime_secs":1767910210,"mtime_nanos":6929330,"lines":305},".github/workflows/martian-issue-triage.yml":{"mtime_secs":1767966312,"mtime_nanos":627746823,"lines":176},"src/fastmcp/client/logging.py":{"mtime_secs":1767910209,"mtime_nanos":977769348,"lines":54},"tests/server/http/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":3943011,"lines":0},"tests/server/tasks/test_task_security.py":{"mtime_secs":1767910210,"mtime_nanos":9776357,"lines":47},"tests/deprecated/test_openapi_deprecations.py":{"mtime_secs":1767910209,"mtime_nanos":999511427,"lines":78},"src/fastmcp/server/auth/providers/scalekit.py":{"mtime_secs":1767910209,"mtime_nanos":984682204,"lines":200},"src/fastmcp/server/auth/authorization.py":{"mtime_secs":1768267998,"mtime_nanos":903622237,"lines":190},".github/ISSUE_TEMPLATE/bug.yml":{"mtime_secs":1767910209,"mtime_nanos":925024851,"lines":71},"tests/server/auth/providers/test_github.py":{"mtime_secs":1767910210,"mtime_nanos":1943950,"lines":129},"tests/server/tasks/test_task_capabilities.py":{"mtime_secs":1767910210,"mtime_nanos":8809848,"lines":44},"src/fastmcp/server/context.py":{"mtime_secs":1768275401,"mtime_nanos":371133151,"lines":1272},"docs/css/python-sdk.css":{"mtime_secs":1767910209,"mtime_nanos":942730060,"lines":3},"tests/server/providers/proxy/test_stateful_proxy_client.py":{"mtime_secs":1768065435,"mtime_nanos":796294562,"lines":147},"docs/servers/providers/namespacing.mdx":{"mtime_secs":1768273914,"mtime_nanos":436395548,"lines":9},".git/logs/refs/heads/extract-parsedfunction":{"mtime_secs":1768275805,"mtime_nanos":78436214,"lines":4},"examples/auth/aws_oauth/requirements.txt":{"mtime_secs":1767910209,"mtime_nanos":968220049,"lines":2},"tests/server/tasks/test_task_status_notifications.py":{"mtime_secs":1767910210,"mtime_nanos":9864775,"lines":160},"docs/python-sdk/fastmcp-server-openapi-routing.mdx":{"mtime_secs":1767910209,"mtime_nanos":957969995,"lines":13},".git/objects/9c/aaff7cacbcca5b163465fdfab5a1a269b45fde":{"mtime_secs":1768005473,"mtime_nanos":488121058,"lines":1},"examples/fastmcp_config/fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":970837324,"lines":13},".github/workflows/publish.yml":{"mtime_secs":1767910209,"mtime_nanos":926045736,"lines":26},"tests/conftest.py":{"mtime_secs":1767910209,"mtime_nanos":998275582,"lines":83},".git/config":{"mtime_secs":1768307285,"mtime_nanos":997779687,"lines":36},"docs/python-sdk/fastmcp-exceptions.mdx":{"mtime_secs":1767910209,"mtime_nanos":953845581,"lines":65},"src/fastmcp/experimental/sampling/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":980599833,"lines":0},".git/objects/6e/8c60b65e449d516dde82cefb6ffdd228c25c8a":{"mtime_secs":1767994305,"mtime_nanos":380528458,"lines":1},".git/objects/03/0e7190c2b413df5967214c240f685a1d7115c6":{"mtime_secs":1768258884,"mtime_nanos":602393117,"lines":4},"tests/experimental/README.md":{"mtime_secs":1767910209,"mtime_nanos":999797763,"lines":3},"docs/python-sdk/fastmcp-server-auth-providers-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":955145218,"lines":8},"docs/deployment/server-configuration.mdx":{"mtime_secs":1767910209,"mtime_nanos":943334065,"lines":640},".git/hooks/update.sample":{"mtime_secs":1767910208,"mtime_nanos":711016678,"lines":128},"docs/integrations/claude-code.mdx":{"mtime_secs":1767910209,"mtime_nanos":945072873,"lines":177},"examples/atproto_mcp/src/atproto_mcp/_atproto/_client.py":{"mtime_secs":1767910209,"mtime_nanos":967089706,"lines":16},"examples/auth/google_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":969278893,"lines":34},"tests/server/providers/test_fastmcp_provider.py":{"mtime_secs":1767910210,"mtime_nanos":7080540,"lines":446},"docs/servers/logging.mdx":{"mtime_secs":1767910209,"mtime_nanos":963926634,"lines":249},"docs/integrations/permit.mdx":{"mtime_secs":1767910209,"mtime_nanos":950379382,"lines":352},"docs/clients/client.mdx":{"mtime_secs":1767910209,"mtime_nanos":941287296,"lines":340},".git/objects/42/dcb8918df7d94962d750a3280285ff5da0f63c":{"mtime_secs":1767966312,"mtime_nanos":838173543,"lines":4},"src/fastmcp/server/tasks/routing.py":{"mtime_secs":1767910209,"mtime_nanos":989862420,"lines":76},"examples/auth/discord_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":968748430,"lines":33},".git/objects/c0/48dfe4743324bc13eef500552c06e12f61fd7c":{"mtime_secs":1768157206,"mtime_nanos":108578893,"lines":3},"src/fastmcp/client/roots.py":{"mtime_secs":1767910209,"mtime_nanos":978121726,"lines":76},"src/fastmcp/server/tasks/capabilities.py":{"mtime_secs":1768091029,"mtime_nanos":867126809,"lines":34},"docs/python-sdk/fastmcp-utilities-cli.mdx":{"mtime_secs":1768065435,"mtime_nanos":781075235,"lines":48},"tests/resources/test_resource_template.py":{"mtime_secs":1768273914,"mtime_nanos":454701233,"lines":1009},"examples/providers/sqlite/setup_db.py":{"mtime_secs":1767910209,"mtime_nanos":972189045,"lines":102},"tests/server/auth/providers/test_introspection.py":{"mtime_secs":1767910210,"mtime_nanos":2098076,"lines":419},"tests/server/sampling/test_sampling_tool.py":{"mtime_secs":1767910210,"mtime_nanos":8119342,"lines":121},".git/objects/d8/b95a00e25fdcf12b6cba3ff42c26d77deccb07":{"mtime_secs":1767924161,"mtime_nanos":21715199,"lines":2},".git/objects/ac/9079f73a6e2b735527a3a7118603ff2bb0068b":{"mtime_secs":1767994305,"mtime_nanos":375725000,"lines":1},"docs/python-sdk/fastmcp-server-middleware-rate_limiting.mdx":{"mtime_secs":1767910209,"mtime_nanos":957619908,"lines":97},"src/fastmcp/server/transforms/tool_transform.py":{"mtime_secs":1768273914,"mtime_nanos":448749020,"lines":94},"src/fastmcp/exceptions.py":{"mtime_secs":1768267998,"mtime_nanos":897398697,"lines":43},"src/fastmcp/contrib/mcp_mixin/example.py":{"mtime_secs":1767910209,"mtime_nanos":980202079,"lines":52},"src/fastmcp/server/auth/providers/google.py":{"mtime_secs":1767910209,"mtime_nanos":984075157,"lines":278},"tests/server/test_server_docket.py":{"mtime_secs":1768091029,"mtime_nanos":870620842,"lines":227},"src/fastmcp/client/messages.py":{"mtime_secs":1767910209,"mtime_nanos":977857807,"lines":128},".git/packed-refs":{"mtime_secs":1768008006,"mtime_nanos":500897988,"lines":145},"examples/custom_tool_serializer_decorator.py":{"mtime_secs":1767910209,"mtime_nanos":970512529,"lines":72},"examples/smart_home/src/smart_home/settings.py":{"mtime_secs":1767910209,"mtime_nanos":973653059,"lines":12},"docs/servers/providers/local.mdx":{"mtime_secs":1768273914,"mtime_nanos":435480166,"lines":160},"tests/server/tasks/test_resource_task_meta_parameter.py":{"mtime_secs":1767910210,"mtime_nanos":8588638,"lines":287},"docs/development/contributing.mdx":{"mtime_secs":1767910209,"mtime_nanos":943504442,"lines":187},"tests/server/tasks/test_task_return_types.py":{"mtime_secs":1767910210,"mtime_nanos":9705148,"lines":662},"src/fastmcp/server/tasks/keys.py":{"mtime_secs":1768065435,"mtime_nanos":793456949,"lines":91},"examples/auth/scalekit_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":969833523,"lines":55},"tests/server/middleware/test_tool_injection.py":{"mtime_secs":1768273914,"mtime_nanos":456109327,"lines":509},"src/fastmcp/contrib/component_manager/example.py":{"mtime_secs":1767910209,"mtime_nanos":979920868,"lines":59},"tests/server/test_logging.py":{"mtime_secs":1767910210,"mtime_nanos":10930826,"lines":192},".git/objects/de/5a3ce3e2f6bcf15d55b07022fc07537797de81":{"mtime_secs":1768052340,"mtime_nanos":63993054,"lines":1},".git/refs/heads/main":{"mtime_secs":1768307169,"mtime_nanos":292258069,"lines":1},"docs/servers/providers/overview.mdx":{"mtime_secs":1768273914,"mtime_nanos":436692676,"lines":80},"tests/deprecated/test_deprecated.py":{"mtime_secs":1767910209,"mtime_nanos":999277675,"lines":48},"tests/utilities/test_cli.py":{"mtime_secs":1767910210,"mtime_nanos":13466600,"lines":195},"docs/python-sdk/fastmcp-server-sampling-sampling_tool.mdx":{"mtime_secs":1767910209,"mtime_nanos":959157548,"lines":76},"src/fastmcp/client/sampling/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":978238352,"lines":69},"docs/python-sdk/fastmcp-server-http.mdx":{"mtime_secs":1767910209,"mtime_nanos":956912152,"lines":106},"docs/servers/providers/mounting.mdx":{"mtime_secs":1768273914,"mtime_nanos":436124922,"lines":309},"docs/servers/visibility.mdx":{"mtime_secs":1768273914,"mtime_nanos":438443314,"lines":315},".git/objects/dc/2aab1e6a4c598aff1a536e7f1c055effb9180c":{"mtime_secs":1767994445,"mtime_nanos":248328183,"lines":2},"tests/server/auth/providers/test_scalekit.py":{"mtime_secs":1767910210,"mtime_nanos":2193077,"lines":220},"tests/server/providers/openapi/test_deepobject_style.py":{"mtime_secs":1767910210,"mtime_nanos":6122865,"lines":346},"src/fastmcp/utilities/mcp_server_config/v1/sources/base.py":{"mtime_secs":1767910209,"mtime_nanos":992359110,"lines":29},"docs/python-sdk/fastmcp-utilities-openapi-parser.mdx":{"mtime_secs":1767910209,"mtime_nanos":962271494,"lines":43},"examples/atproto_mcp/README.md":{"mtime_secs":1767910209,"mtime_nanos":966418699,"lines":156},".github/pull_request_template.md":{"mtime_secs":1767910209,"mtime_nanos":925329438,"lines":28},"tests/client/test_openapi.py":{"mtime_secs":1767910209,"mtime_nanos":997398366,"lines":202},"docs/clients/logging.mdx":{"mtime_secs":1767910209,"mtime_nanos":941466506,"lines":111},".git/objects/c1/12657274e038c4e3410b611169802645ea4497":{"mtime_secs":1768136535,"mtime_nanos":142455545,"lines":5},".git/objects/eb/9c9b95e3d66491a629f384bead15039ca1876f":{"mtime_secs":1768275801,"mtime_nanos":181061270,"lines":3},"src/fastmcp/server/auth/providers/oci.py":{"mtime_secs":1767910209,"mtime_nanos":984547203,"lines":170},".git/objects/e3/a8a4a2e6f845a16028b93ea74d70136b5162a3":{"mtime_secs":1767923559,"mtime_nanos":76282333,"lines":2},"tests/server/tasks/test_server_tasks_parameter.py":{"mtime_secs":1767910210,"mtime_nanos":8657055,"lines":388},"tests/client/test_sampling.py":{"mtime_secs":1767910209,"mtime_nanos":997618034,"lines":1002},"tests/client/test_progress.py":{"mtime_secs":1767910209,"mtime_nanos":997459283,"lines":70},"tests/client/tasks/test_client_task_notifications.py":{"mtime_secs":1767910209,"mtime_nanos":996339981,"lines":209},"examples/auth/azure_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":968465052,"lines":32},"tests/server/test_icons.py":{"mtime_secs":1767910210,"mtime_nanos":10713533,"lines":342},"tests/server/tasks/test_task_proxy.py":{"mtime_secs":1767910210,"mtime_nanos":9575980,"lines":169},"docs/python-sdk/fastmcp-server-tasks-config.mdx":{"mtime_secs":1767910209,"mtime_nanos":959449842,"lines":91},".git/objects/c1/27dd979fd3b0b901db73662cfe9e5a9d6f7e95":{"mtime_secs":1768232585,"mtime_nanos":565466942,"lines":1},"examples/atproto_mcp/src/atproto_mcp/py.typed":{"mtime_secs":1767910209,"mtime_nanos":967425584,"lines":0},"tests/cli/test_project_prepare.py":{"mtime_secs":1767910209,"mtime_nanos":994755966,"lines":306},"tests/server/providers/openapi/test_parameter_collisions.py":{"mtime_secs":1767910210,"mtime_nanos":6721579,"lines":225},"docs/python-sdk/fastmcp-cli-install-gemini_cli.mdx":{"mtime_secs":1767910209,"mtime_nanos":952120690,"lines":68},"tests/server/tasks/test_task_tools.py":{"mtime_secs":1767910210,"mtime_nanos":9922317,"lines":102},"tests/client/tasks/conftest.py":{"mtime_secs":1767910209,"mtime_nanos":996082770,"lines":3},"tests/cli/test_config.py":{"mtime_secs":1767910209,"mtime_nanos":994129210,"lines":461},"examples/testing_demo/tests/test_server.py":{"mtime_secs":1767910209,"mtime_nanos":974626193,"lines":160},"examples/atproto_mcp/src/atproto_mcp/_atproto/_profile.py":{"mtime_secs":1767910209,"mtime_nanos":967258041,"lines":33},"docs/assets/schemas/mcp_server_config/latest.json":{"mtime_secs":1767910209,"mtime_nanos":939489571,"lines":348},"tests/server/providers/openapi/test_comprehensive.py":{"mtime_secs":1767910210,"mtime_nanos":5668985,"lines":741},".git/objects/87/b0d87df4d64af89a01cf2e756dd129194dd17d":{"mtime_secs":1768239135,"mtime_nanos":975651666,"lines":3},"src/fastmcp/prompts/__init__.py":{"mtime_secs":1768273914,"mtime_nanos":440717874,"lines":12},"tests/server/test_input_validation.py":{"mtime_secs":1767910210,"mtime_nanos":10779575,"lines":365},"logo.py":{"mtime_secs":1767910209,"mtime_nanos":975379367,"lines":25},"tests/server/providers/test_base_provider.py":{"mtime_secs":1768273914,"mtime_nanos":456788916,"lines":79},"docs/python-sdk/fastmcp-utilities-visibility.mdx":{"mtime_secs":1767910209,"mtime_nanos":962630289,"lines":86},"docs/python-sdk/fastmcp-utilities-ui.mdx":{"mtime_secs":1767910209,"mtime_nanos":962563413,"lines":140},"docs/python-sdk/fastmcp-resources-template.mdx":{"mtime_secs":1767910209,"mtime_nanos":954408545,"lines":238},"tests/server/test_auth_integration.py":{"mtime_secs":1767910210,"mtime_nanos":10156111,"lines":1242},"examples/get_file.py":{"mtime_secs":1767910209,"mtime_nanos":971439871,"lines":39},".git/objects/e5/2eb8258e13ea178e85c5067fd082b1c44ec2dd":{"mtime_secs":1768264034,"mtime_nanos":516252135,"lines":1},"tests/cli/test_install.py":{"mtime_secs":1767910209,"mtime_nanos":994379629,"lines":313},"tests/contrib/test_component_manager.py":{"mtime_secs":1767910209,"mtime_nanos":998586085,"lines":767},".git/refs/heads/docs/v3-features":{"mtime_secs":1767969249,"mtime_nanos":769250202,"lines":1},"examples/echo.py":{"mtime_secs":1767910209,"mtime_nanos":970660156,"lines":30},"docs/python-sdk/fastmcp-server-openapi-components.mdx":{"mtime_secs":1767910209,"mtime_nanos":957905244,"lines":12},"tests/resources/test_resources.py":{"mtime_secs":1768275463,"mtime_nanos":447592187,"lines":356},"examples/smart_home/src/smart_home/lights/server.py":{"mtime_secs":1767966312,"mtime_nanos":925762092,"lines":318},"tests/utilities/test_components.py":{"mtime_secs":1767910210,"mtime_nanos":13544851,"lines":397},"docs/getting-started/installation.mdx":{"mtime_secs":1768065435,"mtime_nanos":773145402,"lines":108},"src/fastmcp/utilities/openapi/schemas.py":{"mtime_secs":1767910209,"mtime_nanos":993300535,"lines":593},"tests/server/test_tool_annotations.py":{"mtime_secs":1767910210,"mtime_nanos":11624083,"lines":254},"docs/clients/sampling.mdx":{"mtime_secs":1767910209,"mtime_nanos":942082887,"lines":258},".git/objects/1b/723f302da048cc8f2239b36b56377bdb55bbcb":{"mtime_secs":1768273107,"mtime_nanos":883124144,"lines":3},".git/objects/8c/ba4cbc83f3ffda6351164f6c250f2ed089d292":{"mtime_secs":1768060130,"mtime_nanos":836709979,"lines":1},"tests/server/tasks/test_task_resources.py":{"mtime_secs":1767910210,"mtime_nanos":9633314,"lines":108},"docs/servers/auth/oidc-proxy.mdx":{"mtime_secs":1767910209,"mtime_nanos":963302212,"lines":265},"examples/atproto_mcp/src/atproto_mcp/_atproto/_posts.py":{"mtime_secs":1767910209,"mtime_nanos":967171331,"lines":420},"tests/client/test_sse.py":{"mtime_secs":1767910209,"mtime_nanos":997730827,"lines":200},".git/objects/07/d30483d73953e50aa6f4ac6f68952ba2ff4dc2":{"mtime_secs":1768250271,"mtime_nanos":586648207,"lines":2},"docs/python-sdk/fastmcp-server-tasks-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":959328716,"lines":12},"src/fastmcp/server/auth/providers/discord.py":{"mtime_secs":1767910209,"mtime_nanos":983750362,"lines":247},"docs/python-sdk/fastmcp-server-auth-providers-discord.mdx":{"mtime_secs":1767910209,"mtime_nanos":955651598,"lines":66},"tests/server/auth/test_oauth_consent_flow.py":{"mtime_secs":1767910210,"mtime_nanos":2977001,"lines":1056},".git/objects/40/5708aad91a93c14d09d6c968f1e97a4ec65342":{"mtime_secs":1767927080,"mtime_nanos":432731822,"lines":2},".git/objects/cc/bc829ec456c3f0d9b46030f115f9537e3f18f8":{"mtime_secs":1768237034,"mtime_nanos":428248296,"lines":3},"src/fastmcp/client/sampling/handlers/openai.py":{"mtime_secs":1768275398,"mtime_nanos":609072954,"lines":399},".git/hooks/pre-merge-commit.sample":{"mtime_secs":1767910208,"mtime_nanos":710236963,"lines":13},"tests/server/tasks/test_task_metadata.py":{"mtime_secs":1767910210,"mtime_nanos":9168393,"lines":63},"tests/server/middleware/test_timing.py":{"mtime_secs":1767910210,"mtime_nanos":5259440,"lines":317},"docs/servers/providers/transforms.mdx":{"mtime_secs":1768273914,"mtime_nanos":437247180,"lines":405},"src/fastmcp/cli/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":976035831,"lines":3},"docs/integrations/anthropic.mdx":{"mtime_secs":1767910209,"mtime_nanos":944458951,"lines":228},"src/fastmcp/server/sampling/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":988707951,"lines":10},".git/hooks/applypatch-msg.sample":{"mtime_secs":1767910208,"mtime_nanos":709283745,"lines":15},"tests/server/providers/openapi/test_openapi_features.py":{"mtime_secs":1767910210,"mtime_nanos":6487285,"lines":414},"tests/server/middleware/test_logging.py":{"mtime_secs":1767910210,"mtime_nanos":4915603,"lines":670},"src/fastmcp/utilities/mcp_server_config/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":991568102,"lines":25},"src/fastmcp/server/auth/middleware.py":{"mtime_secs":1767910209,"mtime_nanos":982585310,"lines":96},".git/objects/f3/d6ef1b75fc9547e8207dbc27f7bc3f1243a8f8":{"mtime_secs":1768260294,"mtime_nanos":941960828,"lines":3},"v3-notes/provider-architecture.md":{"mtime_secs":1767910210,"mtime_nanos":15284076,"lines":116},"tests/deprecated/test_tool_serializer.py":{"mtime_secs":1767910209,"mtime_nanos":999675470,"lines":185},".git/hooks/pre-push.sample":{"mtime_secs":1767910208,"mtime_nanos":710809135,"lines":53},"tests/integration_tests/test_timeout_fix.py":{"mtime_secs":1768258809,"mtime_nanos":251733807,"lines":51},".git/objects/ff/633b567d8765e0d017965eca9618ab06084e7b":{"mtime_secs":1768062579,"mtime_nanos":791355786,"lines":1},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":961324776,"lines":9},"tests/cli/test_shared.py":{"mtime_secs":1767910209,"mtime_nanos":995183886,"lines":29},"tests/client/tasks/test_client_resource_tasks.py":{"mtime_secs":1767910209,"mtime_nanos":996234896,"lines":108},"tests/utilities/test_inspect.py":{"mtime_secs":1767910210,"mtime_nanos":13635102,"lines":1111},"docs/python-sdk/fastmcp-client-roots.mdx":{"mtime_secs":1767910209,"mtime_nanos":953280451,"lines":20},"docs/python-sdk/fastmcp-utilities-openapi-schemas.mdx":{"mtime_secs":1767910209,"mtime_nanos":962333327,"lines":43},"tests/cli/test_cli.py":{"mtime_secs":1767910209,"mtime_nanos":994032501,"lines":618},".git/objects/41/0ea090cfe37e1db2c504e490202ec5dcaa142e":{"mtime_secs":1768062579,"mtime_nanos":780660422,"lines":2},"src/fastmcp/utilities/logging.py":{"mtime_secs":1767910209,"mtime_nanos":991394559,"lines":230},"src/fastmcp/contrib/mcp_mixin/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":980134245,"lines":8},"tests/server/tasks/test_task_prompts.py":{"mtime_secs":1767910210,"mtime_nanos":9422479,"lines":92},"docs/integrations/gemini-cli.mdx":{"mtime_secs":1767910209,"mtime_nanos":945929715,"lines":174},".git/objects/ff/7c37d39c39be4edf7bdc7334b7b6784b20d40b":{"mtime_secs":1767980392,"mtime_nanos":422200587,"lines":1},"tests/server/auth/providers/test_supabase.py":{"mtime_secs":1767910210,"mtime_nanos":2284328,"lines":202},"examples/auth/github_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":969038641,"lines":31},".github/release.yml":{"mtime_secs":1767910209,"mtime_nanos":925387105,"lines":47},"src/fastmcp/server/auth/auth.py":{"mtime_secs":1767910209,"mtime_nanos":982233390,"lines":558},"examples/tasks/server.py":{"mtime_secs":1767910209,"mtime_nanos":974223731,"lines":75},"src/fastmcp/server/middleware/tool_injection.py":{"mtime_secs":1767910209,"mtime_nanos":986907600,"lines":116},"examples/complex_inputs.py":{"mtime_secs":1767910209,"mtime_nanos":970366070,"lines":30},"docs/public/schemas/fastmcp.json/latest.json":{"mtime_secs":1767910209,"mtime_nanos":951495059,"lines":365},"examples/fastmcp_config_demo/server.py":{"mtime_secs":1767910209,"mtime_nanos":971338662,"lines":70},"tests/contrib/test_bulk_tool_caller.py":{"mtime_secs":1767910209,"mtime_nanos":998498209,"lines":288},".git/hooks/sendemail-validate.sample":{"mtime_secs":1767910208,"mtime_nanos":708929117,"lines":77},"tests/server/middleware/test_initialization_middleware.py":{"mtime_secs":1767910210,"mtime_nanos":4816977,"lines":377},".git/info/exclude":{"mtime_secs":1767910208,"mtime_nanos":707674272,"lines":6},"tests/client/sampling/handlers/test_openai_handler.py":{"mtime_secs":1767910209,"mtime_nanos":995983144,"lines":100},"docs/python-sdk/fastmcp-utilities-openapi-models.mdx":{"mtime_secs":1767910209,"mtime_nanos":962199493,"lines":35},"README.md":{"mtime_secs":1767910209,"mtime_nanos":926876660,"lines":515},"src/fastmcp/server/tasks/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":989433624,"lines":22},".git/hooks/pre-applypatch.sample":{"mtime_secs":1767910208,"mtime_nanos":710476631,"lines":14},"docs/python-sdk/fastmcp-cli-install-claude_code.mdx":{"mtime_secs":1767910209,"mtime_nanos":951940605,"lines":71},"tests/server/tasks/conftest.py":{"mtime_secs":1767910210,"mtime_nanos":8271426,"lines":3},"src/fastmcp/server/auth/providers/introspection.py":{"mtime_secs":1767910209,"mtime_nanos":984330951,"lines":232},"tests/cli/test_tasks.py":{"mtime_secs":1767910209,"mtime_nanos":995257804,"lines":51},"docs/integrations/scalekit.mdx":{"mtime_secs":1767910209,"mtime_nanos":950470966,"lines":156},"src/fastmcp/server/openapi/components.py":{"mtime_secs":1767910209,"mtime_nanos":987083310,"lines":28},".git/objects/c0/51569de5b770348628df23c2f84594a484428c":{"mtime_secs":1768065435,"mtime_nanos":682174277,"lines":2},"src/fastmcp/contrib/README.md":{"mtime_secs":1767910209,"mtime_nanos":978996318,"lines":19},"docs/clients/tools.mdx":{"mtime_secs":1767910209,"mtime_nanos":942310347,"lines":295},"docs/python-sdk/fastmcp-resources-types.mdx":{"mtime_secs":1767910209,"mtime_nanos":954495420,"lines":134},".git/objects/b3/78331fafc94d484d48d0f4f01ac0c687559a15":{"mtime_secs":1768091029,"mtime_nanos":664477613,"lines":2},"docs/python-sdk/fastmcp-server-dependencies.mdx":{"mtime_secs":1767910209,"mtime_nanos":956670108,"lines":262},"src/fastmcp/server/__init__.py":{"mtime_secs":1768065435,"mtime_nanos":788985319,"lines":6},"docs/community/README.md":{"mtime_secs":1767910209,"mtime_nanos":942496057,"lines":22},"docs/python-sdk/fastmcp-client-logging.mdx":{"mtime_secs":1767910209,"mtime_nanos":952942739,"lines":24},".pre-commit-config.yaml":{"mtime_secs":1768307298,"mtime_nanos":259500063,"lines":55},".git/objects/81/adcdcb7c774850074e85fb0fa4b4a85d132af5":{"mtime_secs":1768248151,"mtime_nanos":545778925,"lines":4},"docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx":{"mtime_secs":1767910209,"mtime_nanos":955890184,"lines":96},".git/objects/4f/bd5ed99f52aae16d807b592b43b0fdaa5824df":{"mtime_secs":1768093424,"mtime_nanos":848695151,"lines":2},".git/objects/e7/42cfb59a305cd014e1b332a2a5fd080f6da783":{"mtime_secs":1768065435,"mtime_nanos":693951109,"lines":1},"docs/python-sdk/fastmcp-server-elicitation.mdx":{"mtime_secs":1768065435,"mtime_nanos":779122840,"lines":145},"src/fastmcp/utilities/openapi/formatters.py":{"mtime_secs":1767910209,"mtime_nanos":992885407,"lines":389},"examples/auth/aws_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":968091465,"lines":47},"src/fastmcp/utilities/openapi/models.py":{"mtime_secs":1767910209,"mtime_nanos":993090367,"lines":88},"src/fastmcp/resources/__init__.py":{"mtime_secs":1768273914,"mtime_nanos":441763591,"lines":24},"docs/css/banner.css":{"mtime_secs":1767910209,"mtime_nanos":942669476,"lines":67},"src/fastmcp/cli/cli.py":{"mtime_secs":1768258809,"mtime_nanos":243879670,"lines":957},"docs/development/v3-notes/v3-features.mdx":{"mtime_secs":1768273914,"mtime_nanos":433838987,"lines":539},"src/fastmcp/contrib/component_manager/README.md":{"mtime_secs":1767910209,"mtime_nanos":979545406,"lines":170},".git/objects/b8/91305e65b62f19eeffc83ab0cb654a0ea2891c":{"mtime_secs":1768137600,"mtime_nanos":542451715,"lines":3},"src/fastmcp/server/middleware/error_handling.py":{"mtime_secs":1767910209,"mtime_nanos":986327803,"lines":210},"examples/auth/mounted/client.py":{"mtime_secs":1767910209,"mtime_nanos":969622104,"lines":50},"tests/resources/test_standalone_decorator.py":{"mtime_secs":1768273914,"mtime_nanos":455366988,"lines":154},".git/objects/50/efa27eb0bb623fce9db94455ec83ee0fd6f243":{"mtime_secs":1768137020,"mtime_nanos":832057407,"lines":2},"docs/python-sdk/fastmcp-server-low_level.mdx":{"mtime_secs":1767910209,"mtime_nanos":956974902,"lines":83},"docs/python-sdk/fastmcp-server-auth-providers-workos.mdx":{"mtime_secs":1767910209,"mtime_nanos":956390605,"lines":102},"tests/server/test_streamable_http_no_redirect.py":{"mtime_secs":1767910210,"mtime_nanos":11504748,"lines":85},"docs/python-sdk/fastmcp-server-tasks-keys.mdx":{"mtime_secs":1768065435,"mtime_nanos":780571022,"lines":89},"docs/integrations/claude-desktop.mdx":{"mtime_secs":1768065435,"mtime_nanos":773611157,"lines":299},"examples/tool_result_echo.py":{"mtime_secs":1767910209,"mtime_nanos":975231865,"lines":41},"docs/python-sdk/fastmcp-tools-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":959998597,"lines":8},".cursor/rules/core-mcp-objects.mdc":{"mtime_secs":1768258809,"mtime_nanos":230499327,"lines":13},"docs/servers/auth/authentication.mdx":{"mtime_secs":1767910209,"mtime_nanos":962792123,"lines":216},"docs/servers/tools.mdx":{"mtime_secs":1768273914,"mtime_nanos":438023311,"lines":1040},"docs/tutorials/rest-api.mdx":{"mtime_secs":1767910209,"mtime_nanos":966102071,"lines":203},"src/fastmcp/server/auth/providers/github.py":{"mtime_secs":1767910209,"mtime_nanos":983912989,"lines":240},"src/fastmcp/server/providers/openapi/routing.py":{"mtime_secs":1767910209,"mtime_nanos":988211654,"lines":109},"examples/filesystem-provider/mcp/tools/calculator.py":{"mtime_secs":1768065435,"mtime_nanos":784557439,"lines":24},"examples/mount_example.py":{"mtime_secs":1767910209,"mtime_nanos":971860417,"lines":111},"docs/python-sdk/fastmcp-server-auth-auth.mdx":{"mtime_secs":1768065435,"mtime_nanos":777532531,"lines":279},"tests/utilities/test_logging.py":{"mtime_secs":1767910210,"mtime_nanos":13941146,"lines":56},".github/ISSUE_TEMPLATE/enhancement.yml":{"mtime_secs":1767910209,"mtime_nanos":925150269,"lines":34},".git/objects/24/edfdf9c8de9c126bfdb7729d9b07045c5194ba":{"mtime_secs":1768260294,"mtime_nanos":897853065,"lines":2},"src/fastmcp/server/middleware/rate_limiting.py":{"mtime_secs":1767910209,"mtime_nanos":986740807,"lines":231},"tests/server/middleware/test_ping.py":{"mtime_secs":1768091029,"mtime_nanos":869390580,"lines":241},"tests/server/tasks/test_task_protocol.py":{"mtime_secs":1767910210,"mtime_nanos":9487438,"lines":81},"src/fastmcp/server/tasks/subscriptions.py":{"mtime_secs":1768275412,"mtime_nanos":374665205,"lines":212},"examples/smart_home/src/smart_home/py.typed":{"mtime_secs":1767910209,"mtime_nanos":973567933,"lines":0},"examples/config_server.py":{"mtime_secs":1767910209,"mtime_nanos":970438737,"lines":46},"src/fastmcp/server/openapi/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":986999268,"lines":55},"tests/server/auth/test_oauth_mounting.py":{"mtime_secs":1767910210,"mtime_nanos":3070377,"lines":380},"docs/python-sdk/fastmcp-server-providers-base.mdx":{"mtime_secs":1767910209,"mtime_nanos":958254497,"lines":264},"tests/deprecated/openapi/test_openapi.py":{"mtime_secs":1767910209,"mtime_nanos":999024506,"lines":170},"tests/server/auth/test_auth_provider.py":{"mtime_secs":1767910210,"mtime_nanos":2489414,"lines":105},"src/fastmcp/utilities/mcp_server_config/v1/environments/uv.py":{"mtime_secs":1767910209,"mtime_nanos":992003815,"lines":271},"docs/python-sdk/fastmcp-server-auth-providers-supabase.mdx":{"mtime_secs":1768065435,"mtime_nanos":778338040,"lines":67},"src/fastmcp/mcp_config.py":{"mtime_secs":1768273914,"mtime_nanos":440422955,"lines":347},"docs/servers/storage-backends.mdx":{"mtime_secs":1767910209,"mtime_nanos":965258063,"lines":275},"tests/server/tasks/test_task_config.py":{"mtime_secs":1767910210,"mtime_nanos":8878557,"lines":393},"tests/utilities/openapi/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":12415174,"lines":1},"src/fastmcp/contrib/bulk_tool_caller/README.md":{"mtime_secs":1767910209,"mtime_nanos":979124569,"lines":35},".git/objects/59/2d0dad58b8002509e9f9f06a8cdfb79e0d1dab":{"mtime_secs":1768239135,"mtime_nanos":941631237,"lines":2},"docs/servers/progress.mdx":{"mtime_secs":1767910209,"mtime_nanos":964130844,"lines":189},"tests/server/auth/test_oauth_proxy_redirect_validation.py":{"mtime_secs":1767910210,"mtime_nanos":3339088,"lines":201},"src/fastmcp/_vendor/docket_di/__init__.py":{"mtime_secs":1768091029,"mtime_nanos":862366764,"lines":163},"src/fastmcp/server/proxy.py":{"mtime_secs":1767910209,"mtime_nanos":988580366,"lines":41},"examples/auth/workos_oauth/README.md":{"mtime_secs":1767910209,"mtime_nanos":970071234,"lines":27},"tests/client/auth/test_oauth_client.py":{"mtime_secs":1767910209,"mtime_nanos":995588932,"lines":358},"docs/python-sdk/fastmcp-server-providers-openapi-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":958510542,"lines":23},"docs/python-sdk/fastmcp-server-auth-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":954643088,"lines":8},".github/workflows/marvin-label-triage.yml":{"mtime_secs":1767966312,"mtime_nanos":628909164,"lines":146},"examples/atproto_mcp/src/atproto_mcp/server.py":{"mtime_secs":1767910209,"mtime_nanos":967541751,"lines":149},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem.mdx":{"mtime_secs":1767910209,"mtime_nanos":961820823,"lines":37},"docs/python-sdk/fastmcp-server-openapi-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":957844785,"lines":27},"tests/server/test_run_server.py":{"mtime_secs":1767910210,"mtime_nanos":11204996,"lines":98},".git/objects/f8/d9010672351b6167eef945a77d6070afaf64ce":{"mtime_secs":1768065435,"mtime_nanos":673484560,"lines":2},"docs/python-sdk/fastmcp-client-sampling-handlers-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":953418535,"lines":8},"docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx":{"mtime_secs":1767910209,"mtime_nanos":958383124,"lines":302},"docs/css/style.css":{"mtime_secs":1767910209,"mtime_nanos":942789518,"lines":20},"src/fastmcp/utilities/mcp_server_config/v1/environments/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":991794771,"lines":6},"tests/fs/test_provider.py":{"mtime_secs":1768065435,"mtime_nanos":794991090,"lines":421},"tests/client/auth/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":995499598,"lines":0},".git/objects/d4/9a61f5bcb362f24223a4f661810a9b99a5a488":{"mtime_secs":1767994445,"mtime_nanos":252684078,"lines":2},"examples/filesystem-provider/server.py":{"mtime_secs":1768065435,"mtime_nanos":784861734,"lines":29},"tests/server/auth/test_remote_auth_provider.py":{"mtime_secs":1767910210,"mtime_nanos":3773259,"lines":485},".git/objects/1d/6120c42e970f0f6a8fd3d2a598c0b44aee49a9":{"mtime_secs":1768274986,"mtime_nanos":132346000,"lines":2},".git/hooks/prepare-commit-msg.sample":{"mtime_secs":1767910208,"mtime_nanos":709952752,"lines":42},"tests/client/test_logs.py":{"mtime_secs":1767910209,"mtime_nanos":997087988,"lines":242},"src/fastmcp/server/providers/__init__.py":{"mtime_secs":1768273914,"mtime_nanos":443672230,"lines":59},"examples/memory.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":971591748,"lines":7},"docs/python-sdk/fastmcp-server-middleware-caching.mdx":{"mtime_secs":1767910209,"mtime_nanos":957244946,"lines":221},".git/objects/92/05b08279f6a5a450c93d8d7dc8c66475ea9892":{"mtime_secs":1768091029,"mtime_nanos":673956202,"lines":6},"src/fastmcp/cli/install/gemini_cli.py":{"mtime_secs":1767910209,"mtime_nanos":976730005,"lines":237},"tests/server/http/test_http_middleware.py":{"mtime_secs":1767910210,"mtime_nanos":4386306,"lines":234},"docs/python-sdk/fastmcp-server-openapi-server.mdx":{"mtime_secs":1767910209,"mtime_nanos":958024704,"lines":43},"tests/tools/test_standalone_decorator.py":{"mtime_secs":1768275439,"mtime_nanos":418338078,"lines":138},"docs/python-sdk/fastmcp-settings.mdx":{"mtime_secs":1767910209,"mtime_nanos":959928388,"lines":50},"docs/integrations/github.mdx":{"mtime_secs":1767910209,"mtime_nanos":946128633,"lines":176},"tests/client/test_streamable_http.py":{"mtime_secs":1767910209,"mtime_nanos":997986163,"lines":271},"docs/public/schemas/fastmcp.json/v1.json":{"mtime_secs":1767910209,"mtime_nanos":951616102,"lines":365},"docs/servers/providers/proxy.mdx":{"mtime_secs":1768065435,"mtime_nanos":783643971,"lines":301},"tests/client/sampling/handlers/test_anthropic_handler.py":{"mtime_secs":1767910209,"mtime_nanos":995913727,"lines":247},"examples/atproto_mcp/src/atproto_mcp/__main__.py":{"mtime_secs":1767910209,"mtime_nanos":966907829,"lines":9},"docs/assets/schemas/mcp_server_config/v1.json":{"mtime_secs":1767910209,"mtime_nanos":939610864,"lines":348},"src/fastmcp/prompts/function_prompt.py":{"mtime_secs":1768275399,"mtime_nanos":600096498,"lines":449},"examples/smart_home/pyproject.toml":{"mtime_secs":1767910209,"mtime_nanos":973064428,"lines":19},"tests/client/test_oauth_callback_xss.py":{"mtime_secs":1767910209,"mtime_nanos":997307573,"lines":159},"docs/python-sdk/fastmcp-client-messages.mdx":{"mtime_secs":1767910209,"mtime_nanos":953050615,"lines":107},"examples/atproto_mcp/src/atproto_mcp/_atproto/_social.py":{"mtime_secs":1767910209,"mtime_nanos":967394458,"lines":108},".git/logs/refs/remotes/origin/consolidate-tool-transforms":{"mtime_secs":1768273613,"mtime_nanos":507273178,"lines":18},"examples/auth/github_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":969166267,"lines":35},"examples/screenshot.py":{"mtime_secs":1767910209,"mtime_nanos":972703258,"lines":34},"tests/server/providers/openapi/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":5586401,"lines":1},"docs/tutorials/mcp.mdx":{"mtime_secs":1767910209,"mtime_nanos":965996779,"lines":120},"tests/deprecated/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":998823837,"lines":1},"src/fastmcp/contrib/bulk_tool_caller/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":979201236,"lines":3},"tests/client/test_notifications.py":{"mtime_secs":1768275422,"mtime_nanos":287907709,"lines":556},".git/objects/b4/7896b09cafd1e4cc9842989e81be260587794e":{"mtime_secs":1768267926,"mtime_nanos":984003065,"lines":2},"src/fastmcp/server/tasks/handlers.py":{"mtime_secs":1768275412,"mtime_nanos":311074872,"lines":157},"tests/client/tasks/test_task_context_validation.py":{"mtime_secs":1767910209,"mtime_nanos":996601025,"lines":210},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":961695196,"lines":8},".git/objects/22/c76d09a8464e9a804f410fa924f7561f6cbd09":{"mtime_secs":1768250271,"mtime_nanos":584234730,"lines":1},".git/HEAD":{"mtime_secs":1768274985,"mtime_nanos":556264810,"lines":1},"tests/server/auth/test_oauth_proxy.py":{"mtime_secs":1767910210,"mtime_nanos":3208920,"lines":1899},"src/fastmcp/utilities/mcp_server_config/v1/schema.json":{"mtime_secs":1767910209,"mtime_nanos":992200484,"lines":365},".git/objects/c3/53e943d6333c628c16a10a9e4f1e33f823e692":{"mtime_secs":1768092842,"mtime_nanos":316929328,"lines":1},"docs/clients/resources.mdx":{"mtime_secs":1767910209,"mtime_nanos":941839676,"lines":204},"tests/server/auth/providers/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":1390487,"lines":0},".git/objects/54/fdf5869298aba0a93a0a729b2da3050a6554a8":{"mtime_secs":1768062579,"mtime_nanos":837677255,"lines":2},".git/objects/4f/f5f87468426a2dde6d5c25e38ad49397e5f23e":{"mtime_secs":1768076204,"mtime_nanos":852890704,"lines":1},".git/objects/b3/42b8df0618b78a6a2de5b884a64e23c2be68d3":{"mtime_secs":1767969249,"mtime_nanos":767370869,"lines":1},"examples/smart_home/lights.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":972989303,"lines":9},"tests/utilities/test_version_check.py":{"mtime_secs":1768258809,"mtime_nanos":253462361,"lines":315},"docs/python-sdk/fastmcp-server-auth-providers-azure.mdx":{"mtime_secs":1767910209,"mtime_nanos":955435012,"lines":70},".git/objects/be/91b6879d10acd18f93082b754c7a40a6d39c0d":{"mtime_secs":1767924161,"mtime_nanos":31520224,"lines":3},".git/objects/2a/b252054735a46f7cfddd7df8717960078336ff":{"mtime_secs":1767979182,"mtime_nanos":99591203,"lines":1},"examples/smart_home/src/smart_home/lights/hue_utils.py":{"mtime_secs":1767910209,"mtime_nanos":973462974,"lines":34},"tests/server/test_file_server.py":{"mtime_secs":1767910210,"mtime_nanos":10591323,"lines":134},"docs/python-sdk/fastmcp-utilities-inspect.mdx":{"mtime_secs":1767910209,"mtime_nanos":960831480,"lines":143},".git/refs/remotes/origin/extract-parsedfunction":{"mtime_secs":1768307169,"mtime_nanos":50322937,"lines":1},".git/refs/remotes/origin/HEAD":{"mtime_secs":1768307169,"mtime_nanos":239296625,"lines":1},"examples/smart_home/hub.fastmcp.json":{"mtime_secs":1767910209,"mtime_nanos":972926385,"lines":9},".git/objects/96/ce76a64c3cf09b157694aec6a7ebc0b30b03d3":{"mtime_secs":1768086974,"mtime_nanos":56235000,"lines":5},"docs/python-sdk/fastmcp-server-proxy.mdx":{"mtime_secs":1767910209,"mtime_nanos":958916837,"lines":14},"tests/utilities/test_typeadapter.py":{"mtime_secs":1767910210,"mtime_nanos":14133815,"lines":273},"docs/clients/prompts.mdx":{"mtime_secs":1767910209,"mtime_nanos":941722842,"lines":216},"docs/python-sdk/fastmcp-server-providers-openapi-provider.mdx":{"mtime_secs":1767910209,"mtime_nanos":958655209,"lines":94},"src/fastmcp/_vendor/__init__.py":{"mtime_secs":1768091029,"mtime_nanos":862090970,"lines":1},"examples/tasks/docker-compose.yml":{"mtime_secs":1767910209,"mtime_nanos":974155897,"lines":10},"docs/python-sdk/fastmcp-utilities-auth.mdx":{"mtime_secs":1767910209,"mtime_nanos":960472893,"lines":25},".git/logs/refs/remotes/origin/marvin/update-sdk-docs":{"mtime_secs":1768089463,"mtime_nanos":978178526,"lines":9},"tests/client/test_roots.py":{"mtime_secs":1767910209,"mtime_nanos":997526742,"lines":44},"examples/smart_home/src/smart_home/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":973199055,"lines":3},".git/objects/2f/3cf47a6e5b38d74d38f94c7b01d45a1a45d7df":{"mtime_secs":1768065435,"mtime_nanos":674737781,"lines":4},".git/objects/50/23b9a6307492ec254f8d91cde94a95c4ad2838":{"mtime_secs":1768267926,"mtime_nanos":669355907,"lines":2},"docs/integrations/openai.mdx":{"mtime_secs":1767910209,"mtime_nanos":950235214,"lines":227},"tests/tools/__init__.py":{"mtime_secs":1767910210,"mtime_nanos":11842918,"lines":0},"src/fastmcp/utilities/json_schema.py":{"mtime_secs":1767966312,"mtime_nanos":634635197,"lines":401},".git/objects/fb/c0a3b35dd3df3d6e83c4b5164632a7ce8e50f4":{"mtime_secs":1768076785,"mtime_nanos":251090027,"lines":1},"src/fastmcp/cli/install/claude_code.py":{"mtime_secs":1767910209,"mtime_nanos":976420377,"lines":240},"src/fastmcp/server/dependencies.py":{"mtime_secs":1768275409,"mtime_nanos":847832657,"lines":908},"docs/python-sdk/fastmcp-server-sampling-run.mdx":{"mtime_secs":1767910209,"mtime_nanos":959035838,"lines":116},".git/refs/remotes/origin/consolidate-tool-transforms":{"mtime_secs":1768273613,"mtime_nanos":506487922,"lines":1},"v3-notes/visibility.md":{"mtime_secs":1767910210,"mtime_nanos":15599745,"lines":113},"tests/server/test_server_lifespan.py":{"mtime_secs":1768065435,"mtime_nanos":797942538,"lines":464},"tests/client/test_elicitation.py":{"mtime_secs":1767910209,"mtime_nanos":996938111,"lines":1132},"docs/development/upgrade-guide.mdx":{"mtime_secs":1768273914,"mtime_nanos":433401067,"lines":401},"tests/deprecated/conftest.py":{"mtime_secs":1767910209,"mtime_nanos":998886838,"lines":9},"docs/python-sdk/fastmcp-utilities-json_schema_type.mdx":{"mtime_secs":1767910209,"mtime_nanos":960965106,"lines":110},"docs/.ccignore":{"mtime_secs":1767910209,"mtime_nanos":927042704,"lines":2},"src/fastmcp/utilities/json_schema_type.py":{"mtime_secs":1768275640,"mtime_nanos":345539294,"lines":650},"docs/changelog.mdx":{"mtime_secs":1768258809,"mtime_nanos":238537549,"lines":2280},"examples/sampling/README.md":{"mtime_secs":1767910209,"mtime_nanos":972285171,"lines":62},"docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-environments-uv.mdx":{"mtime_secs":1767910209,"mtime_nanos":961491570,"lines":45},"tests/server/test_context.py":{"mtime_secs":1768275525,"mtime_nanos":455869065,"lines":294},"examples/auth/github_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":969101225,"lines":32},"docs/integrations/eunomia-authorization.mdx":{"mtime_secs":1767910209,"mtime_nanos":945746296,"lines":129},"src/fastmcp/server/auth/__init__.py":{"mtime_secs":1768267998,"mtime_nanos":902825982,"lines":39},"docs/python-sdk/fastmcp-dependencies.mdx":{"mtime_secs":1767910209,"mtime_nanos":953769580,"lines":14},"src/fastmcp/utilities/mcp_server_config/v1/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":991637978,"lines":0},".git/refs/heads/feature/reload-flag":{"mtime_secs":1767927080,"mtime_nanos":440919312,"lines":1},"docs/python-sdk/fastmcp-client-sampling-handlers-openai.mdx":{"mtime_secs":1767910209,"mtime_nanos":953555870,"lines":17},".git/objects/15/ada0e0abca1bdd7c3e13fcdf6dd292f27d617c":{"mtime_secs":1768062579,"mtime_nanos":868791012,"lines":1},"tests/utilities/openapi/test_parser.py":{"mtime_secs":1767910210,"mtime_nanos":13193514,"lines":460},".git/objects/16/47fbea1a21e3f2b8384f4032724b2a4b994cca":{"mtime_secs":1768060130,"mtime_nanos":826156272,"lines":1},".git/objects/c8/2a2396c74b64e77bd7669372ac34bd8dfc680b":{"mtime_secs":1768157216,"mtime_nanos":347003934,"lines":3},"tests/server/auth/test_authorization.py":{"mtime_secs":1768267998,"mtime_nanos":912749711,"lines":604},"src/fastmcp/client/auth/oauth.py":{"mtime_secs":1767910209,"mtime_nanos":977478262,"lines":329},"src/fastmcp/cli/install/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":976311792,"lines":22},"tests/server/providers/openapi/test_openapi_performance.py":{"mtime_secs":1767910210,"mtime_nanos":6598286,"lines":136},"examples/atproto_mcp/src/atproto_mcp/_atproto/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":967014205,"lines":20},"tests/contrib/test_mcp_mixin.py":{"mtime_secs":1767910209,"mtime_nanos":998716003,"lines":339},"docs/servers/tasks.mdx":{"mtime_secs":1768091029,"mtime_nanos":860856583,"lines":253},"docs/integrations/fastapi.mdx":{"mtime_secs":1768065435,"mtime_nanos":774079745,"lines":445},".git/hooks/pre-receive.sample":{"mtime_secs":1767910208,"mtime_nanos":709805292,"lines":24},"docs/python-sdk/fastmcp-server-tasks-subscriptions.mdx":{"mtime_secs":1767910209,"mtime_nanos":959858012,"lines":36},"src/fastmcp/utilities/components.py":{"mtime_secs":1767910209,"mtime_nanos":990875679,"lines":184},"tests/server/test_providers.py":{"mtime_secs":1768273914,"mtime_nanos":460651113,"lines":439},"examples/smart_home/src/smart_home/lights/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":973384390,"lines":0},"docs/python-sdk/fastmcp-utilities-json_schema.mdx":{"mtime_secs":1768065435,"mtime_nanos":781402780,"lines":79},"docs/python-sdk/fastmcp-server-tasks-requests.mdx":{"mtime_secs":1768065435,"mtime_nanos":780910525,"lines":89},"docs/python-sdk/fastmcp-utilities-lifespan.mdx":{"mtime_secs":1768065435,"mtime_nanos":781585199,"lines":36},"examples/sampling/tool_use.py":{"mtime_secs":1767910209,"mtime_nanos":972577174,"lines":125},"docs/docs.json":{"mtime_secs":1768273914,"mtime_nanos":434228823,"lines":589},"src/fastmcp/tools/function_tool.py":{"mtime_secs":1768275412,"mtime_nanos":805442386,"lines":411},"docs/servers/icons.mdx":{"mtime_secs":1767910209,"mtime_nanos":963789508,"lines":139},"tests/server/test_event_store.py":{"mtime_secs":1767910210,"mtime_nanos":10499114,"lines":238},"tests/utilities/openapi/test_nullable_fields.py":{"mtime_secs":1768275583,"mtime_nanos":811283856,"lines":385},"examples/in_memory_proxy_example.py":{"mtime_secs":1767910209,"mtime_nanos":971520789,"lines":82},".git/ORIG_HEAD":{"mtime_secs":1768274985,"mtime_nanos":262791434,"lines":1},"docs/python-sdk/fastmcp-server-auth-providers-jwt.mdx":{"mtime_secs":1767910209,"mtime_nanos":956058268,"lines":146},"src/fastmcp/server/providers/filesystem.py":{"mtime_secs":1768273914,"mtime_nanos":445181492,"lines":217},"examples/desktop.py":{"mtime_secs":1767910209,"mtime_nanos":970601530,"lines":32},"examples/fastmcp_config/env_interpolation_example.json":{"mtime_secs":1767910209,"mtime_nanos":970774407,"lines":20},".github/workflows/auto-close-duplicates.yml":{"mtime_secs":1767910209,"mtime_nanos":925481022,"lines":36},"examples/atproto_mcp/src/atproto_mcp/settings.py":{"mtime_secs":1767910209,"mtime_nanos":967610419,"lines":17},".git/objects/97/17b81aa0fc9fc8be7b60ee9ff6ac6648013f59":{"mtime_secs":1768005421,"mtime_nanos":578957736,"lines":1},"examples/auth/mounted/server.py":{"mtime_secs":1767910209,"mtime_nanos":969713022,"lines":121},"src/fastmcp/cli/run.py":{"mtime_secs":1768065435,"mtime_nanos":786414458,"lines":366},"src/fastmcp/experimental/sampling/handlers/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":980731209,"lines":5},".github/ISSUE_TEMPLATE/config.yml":{"mtime_secs":1767910209,"mtime_nanos":925086685,"lines":8},"src/fastmcp/server/auth/providers/auth0.py":{"mtime_secs":1767910209,"mtime_nanos":983168148,"lines":123},"tests/server/tasks/test_task_ttl.py":{"mtime_secs":1767910210,"mtime_nanos":9979234,"lines":87},".git/description":{"mtime_secs":1767910208,"mtime_nanos":707959400,"lines":1},"src/fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py":{"mtime_secs":1767910209,"mtime_nanos":992457361,"lines":216},"examples/auth/aws_oauth/server.py":{"mtime_secs":1767910209,"mtime_nanos":968288884,"lines":59},"docs/development/v3-notes/auth-provider-env-vars.mdx":{"mtime_secs":1767910209,"mtime_nanos":943921779,"lines":73},"src/fastmcp/settings.py":{"mtime_secs":1768273914,"mtime_nanos":449438692,"lines":367},"docs/python-sdk/fastmcp-server-auth-providers-google.mdx":{"mtime_secs":1767910209,"mtime_nanos":955794266,"lines":66},"src/fastmcp/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":975938122,"lines":35},"docs/python-sdk/fastmcp-client-auth-oauth.mdx":{"mtime_secs":1768065435,"mtime_nanos":775104506,"lines":104},"docs/python-sdk/fastmcp-server-providers-proxy.mdx":{"mtime_secs":1768065435,"mtime_nanos":779964599,"lines":322},".git/refs/heads/extract-parsedfunction":{"mtime_secs":1768275805,"mtime_nanos":78366630,"lines":1},"src/fastmcp/utilities/openapi/README.md":{"mtime_secs":1767910209,"mtime_nanos":992630446,"lines":211},"docs/python-sdk/fastmcp-resources-__init__.mdx":{"mtime_secs":1767910209,"mtime_nanos":954184334,"lines":8},"docs/python-sdk/fastmcp-server-auth-providers-introspection.mdx":{"mtime_secs":1767910209,"mtime_nanos":955961976,"lines":71},".git/objects/0f/ddc85a07fa514936c4ed9a20887cb9ae922732":{"mtime_secs":1768259365,"mtime_nanos":685505137,"lines":2},".git/cursor/crepe/627c6cdad436cc6d81c268d8fc2c869e1b7e01da/metadata.json":{"mtime_secs":1767914918,"mtime_nanos":561926124,"lines":1},"tests/server/providers/proxy/__init__.py":{"mtime_secs":1768065435,"mtime_nanos":795461095,"lines":0},"v3-notes/task-meta-parameter.md":{"mtime_secs":1767910210,"mtime_nanos":15531745,"lines":109},".git/objects/f3/2b73b2d88cb8d9b63dff0ad0a62463ae0fe444":{"mtime_secs":1768077943,"mtime_nanos":871964525,"lines":3},"tests/deprecated/server/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":999134215,"lines":1},".git/FETCH_HEAD":{"mtime_secs":1768307169,"mtime_nanos":51648273,"lines":69},"docs/python-sdk/fastmcp-server-event_store.mdx":{"mtime_secs":1767910209,"mtime_nanos":956825151,"lines":78},".git/objects/56/7083454d0c2ae5576f93b6a7dda7815b76e22b":{"mtime_secs":1768273914,"mtime_nanos":360682000,"lines":2},"tests/server/auth/test_oidc_proxy.py":{"mtime_secs":1767910210,"mtime_nanos":3595049,"lines":878},"examples/auth/google_oauth/client.py":{"mtime_secs":1767910209,"mtime_nanos":969343435,"lines":32},"docs/python-sdk/fastmcp-server-tasks-handlers.mdx":{"mtime_secs":1767910209,"mtime_nanos":959588677,"lines":41},"docs/python-sdk/fastmcp-prompts-prompt.mdx":{"mtime_secs":1767910209,"mtime_nanos":954120417,"lines":200},".git/objects/45/6100882d3e34e2f18d02b4b6a11874acaac864":{"mtime_secs":1767927080,"mtime_nanos":440059296,"lines":1},".git/objects/d8/d221a3d2234d9b5e70131969c4064a2d457606":{"mtime_secs":1768257849,"mtime_nanos":528083117,"lines":2},".github/workflows/auto-close-needs-mre.yml":{"mtime_secs":1767910209,"mtime_nanos":925534731,"lines":36},"tests/server/http/test_http_auth_middleware.py":{"mtime_secs":1767910210,"mtime_nanos":4197680,"lines":93},"src/fastmcp/experimental/utilities/openapi/__init__.py":{"mtime_secs":1767910209,"mtime_nanos":981096046,"lines":37},"tests/server/providers/proxy/test_proxy_server.py":{"mtime_secs":1768275439,"mtime_nanos":343715291,"lines":729},"src/fastmcp/server/auth/jwt_issuer.py":{"mtime_secs":1767910209,"mtime_nanos":982524476,"lines":236},"src/fastmcp/contrib/bulk_tool_caller/bulk_tool_caller.py":{"mtime_secs":1767910209,"mtime_nanos":979308612,"lines":133},".git/hooks/commit-msg.sample":{"mtime_secs":1767910208,"mtime_nanos":708589864,"lines":24},".git/objects/84/c11b074fc53b632c87cdf0c18461ea23bd8f19":{"mtime_secs":1767927045,"mtime_nanos":190585270,"lines":2}}} \ No newline at end of file diff --git a/README.md b/README.md index c1384c638..91593ea73 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ > > **For production MCP applications, install FastMCP:** `pip install fastmcp` +> [!Important] +> FastMCP 3.0 is in development and may include breaking changes. To avoid unexpected issues, pin your dependency to v2: `fastmcp<3` + --- **FastMCP is the standard framework for building MCP applications**, providing the fastest path from idea to production. diff --git a/docs/changelog.mdx b/docs/changelog.mdx index eec90a81d..f31615659 100644 --- a/docs/changelog.mdx +++ b/docs/changelog.mdx @@ -4,6 +4,256 @@ icon: "list-check" rss: true --- + + +**[v2.14.5: Sealed Docket](https://github.com/jlowin/fastmcp/releases/tag/v2.14.5)** + +Fixes a memory leak in the memory:// docket broker where cancelled tasks accumulated instead of being cleaned up. Bumps pydocket to ≥0.17.2. + +## What's Changed +### Enhancements 🔧 +* Bump pydocket to 0.17.2 (memory leak fix) by [@chrisguidry](https://github.com/chrisguidry) in [#2992](https://github.com/jlowin/fastmcp/pull/2992) + +**Full Changelog**: [v2.14.4...v2.14.5](https://github.com/jlowin/fastmcp/compare/v2.14.4...v2.14.5) + + + + + +**[v2.14.4: Package Deal](https://github.com/jlowin/fastmcp/releases/tag/v2.14.4)** + +Fixes a fresh install bug where the packaging library was missing as a direct dependency, plus backports from 3.x for $ref dereferencing in tool schemas and a task capabilities location fix. + +## What's Changed +### Enhancements 🔧 +* Add release notes for v2.14.2 and v2.14.3 by [@jlowin](https://github.com/jlowin) in [#2851](https://github.com/jlowin/fastmcp/pull/2851) +### Fixes 🐞 +* Backport: Dereference $ref in tool schemas for MCP client compatibility by [@jlowin](https://github.com/jlowin) in [#2861](https://github.com/jlowin/fastmcp/pull/2861) +* Fix task capabilities location (issue #2870) by [@jlowin](https://github.com/jlowin) in [#2874](https://github.com/jlowin/fastmcp/pull/2874) +* Add missing packaging dependency by [@jlowin](https://github.com/jlowin) in [#2989](https://github.com/jlowin/fastmcp/pull/2989) + +**Full Changelog**: [v2.14.3...v2.14.4](https://github.com/jlowin/fastmcp/compare/v2.14.3...v2.14.4) + + + + + +**[v2.14.3: Time After Timeout](https://github.com/jlowin/fastmcp/releases/tag/v2.14.3)** + +Sometimes five seconds just isn't enough. This release fixes an HTTP transport bug that was cutting connections short, along with OAuth and Redis fixes, better ASGI support, and CLI update notifications so you never miss a beat. + +## What's Changed +### Enhancements 🔧 +* Add debug logging for OAuth token expiry diagnostics by [@jlowin](https://github.com/jlowin) in [#2789](https://github.com/jlowin/fastmcp/pull/2789) +* Add CLI update notifications by [@jlowin](https://github.com/jlowin) in [#2839](https://github.com/jlowin/fastmcp/pull/2839) +* Use pip instead of uv pip in upgrade instructions by [@jlowin](https://github.com/jlowin) in [#2841](https://github.com/jlowin/fastmcp/pull/2841) +### Fixes 🐞 +* Backport OAuth token storage TTL fix to release/2.x by [@jlowin](https://github.com/jlowin) in [#2798](https://github.com/jlowin/fastmcp/pull/2798) +* Prefix Redis keys with docket name for ACL isolation (2.x backport) by [@chrisguidry](https://github.com/chrisguidry) in [#2812](https://github.com/jlowin/fastmcp/pull/2812) +* Fix ContextVar propagation for ASGI-mounted servers with tasks by [@chrisguidry](https://github.com/chrisguidry) in [#2843](https://github.com/jlowin/fastmcp/pull/2843) +* Fix HTTP transport timeout defaulting to 5 seconds by [@jlowin](https://github.com/jlowin) in [#2848](https://github.com/jlowin/fastmcp/pull/2848) + +**Full Changelog**: [v2.14.2...v2.14.3](https://github.com/jlowin/fastmcp/compare/v2.14.2...v2.14.3) + + + + + +**[v2.14.2: Port Authority](https://github.com/jlowin/fastmcp/releases/tag/v2.14.2)** + +FastMCP 2.14.2 brings a wave of community contributions safely into the 2.x line. A variety of important fixes backported from 3.0 work improve OpenAPI 3.1 compatibility, MCP spec compliance for output schemas and elicitation, and correct a subtle base_url fallback issue. The CLI now gently reminds you that FastMCP 3.0 is on the horizon. + +## What's Changed +### Enhancements 🔧 +* Pin MCP under 2.x by [@jlowin](https://github.com/jlowin) in [#2709](https://github.com/jlowin/fastmcp/pull/2709) +* Add auth_route parameter to SupabaseProvider by [@EloiZalczer](https://github.com/EloiZalczer) in [#2760](https://github.com/jlowin/fastmcp/pull/2760) +* Update CLI banner with FastMCP 3.0 notice by [@jlowin](https://github.com/jlowin) in [#2765](https://github.com/jlowin/fastmcp/pull/2765) +### Fixes 🐞 +* Let FastMCPError propagate unchanged from managers by [@jlowin](https://github.com/jlowin) in [#2697](https://github.com/jlowin/fastmcp/pull/2697) +* Fix test cleanup for uvicorn 0.39+ context isolation by [@jlowin](https://github.com/jlowin) in [#2696](https://github.com/jlowin/fastmcp/pull/2696) +* Bump pydocket to 0.16.3 to fix worker cleanup race condition by [@chrisguidry](https://github.com/chrisguidry) in [#2700](https://github.com/jlowin/fastmcp/pull/2700) +* Fix Prefect website URL in docs footer by [@mgoldsborough](https://github.com/mgoldsborough) in [#2705](https://github.com/jlowin/fastmcp/pull/2705) +* Fix: resolve root-level $ref in outputSchema for MCP spec compliance by [@majiayu000](https://github.com/majiayu000) in [#2727](https://github.com/jlowin/fastmcp/pull/2727) +* Fix OAuth Proxy resource parameter validation by [@jlowin](https://github.com/jlowin) in [#2763](https://github.com/jlowin/fastmcp/pull/2763) +* Fix openapi_version check to include 3.1 by [@deeleeramone](https://github.com/deeleeramone) in [#2769](https://github.com/jlowin/fastmcp/pull/2769) +* Fix titled enum elicitation schema to comply with MCP spec by [@jlowin](https://github.com/jlowin) in [#2774](https://github.com/jlowin/fastmcp/pull/2774) +* Fix base_url fallback when url is not set by [@bhbs](https://github.com/bhbs) in [#2782](https://github.com/jlowin/fastmcp/pull/2782) +* Lazy import DiskStore to avoid sqlite3 dependency on import by [@jlowin](https://github.com/jlowin) in [#2785](https://github.com/jlowin/fastmcp/pull/2785) +### Docs 📚 +* Add v3 breaking changes notice to README and docs by [@jlowin](https://github.com/jlowin) in [#2713](https://github.com/jlowin/fastmcp/pull/2713) +* Add changelog entries for v2.13.1 through v2.14.1 by [@jlowin](https://github.com/jlowin) in [#2724](https://github.com/jlowin/fastmcp/pull/2724) +* conference to 2.x branch by [@aaazzam](https://github.com/aaazzam) in [#2787](https://github.com/jlowin/fastmcp/pull/2787) + +**Full Changelog**: [v2.14.1...v2.14.2](https://github.com/jlowin/fastmcp/compare/v2.14.1...v2.14.2) + + + + + +**[v2.14.1: 'Tis a Gift to Be Sample](https://github.com/jlowin/fastmcp/releases/tag/v2.14.1)** + +FastMCP 2.14.1 introduces sampling with tools (SEP-1577), enabling servers to pass tools to `ctx.sample()` for agentic workflows where the LLM can automatically execute tool calls in a loop. The new `ctx.sample_step()` method provides single LLM calls that return `SampleStep` objects for custom control flow, while `result_type` enables structured outputs via validated Pydantic models. + +🤖 **AnthropicSamplingHandler** joins the existing OpenAI handler, providing multi-provider sampling support out of the box. + +⚡ **OpenAISamplingHandler promoted** from experimental status—sampling handlers are now production-ready with a unified API. + +## What's Changed +### New Features 🎉 +* Sampling with tools by [@jlowin](https://github.com/jlowin) in [#2538](https://github.com/jlowin/fastmcp/pull/2538) +* Add AnthropicSamplingHandler by [@jlowin](https://github.com/jlowin) in [#2677](https://github.com/jlowin/fastmcp/pull/2677) +### Enhancements 🔧 +* Add Python 3.13 to ubuntu CI by [@jlowin](https://github.com/jlowin) in [#2648](https://github.com/jlowin/fastmcp/pull/2648) +* Remove legacy task initialization workaround by [@jlowin](https://github.com/jlowin) in [#2649](https://github.com/jlowin/fastmcp/pull/2649) +* Consolidate session state reset logic by [@jlowin](https://github.com/jlowin) in [#2651](https://github.com/jlowin/fastmcp/pull/2651) +* Unify SamplingHandler; promote OpenAI from experimental by [@jlowin](https://github.com/jlowin) in [#2656](https://github.com/jlowin/fastmcp/pull/2656) +* Add `tool_names` parameter to mount() for name customization by [@jlowin](https://github.com/jlowin) in [#2660](https://github.com/jlowin/fastmcp/pull/2660) +* Use streamable HTTP client API from MCP SDK by [@jlowin](https://github.com/jlowin) in [#2678](https://github.com/jlowin/fastmcp/pull/2678) +* Deprecate `exclude_args` in favor of Depends() by [@jlowin](https://github.com/jlowin) in [#2693](https://github.com/jlowin/fastmcp/pull/2693) +### Fixes 🐞 +* Fix prompt tasks to return mcp.types.PromptMessage by [@jlowin](https://github.com/jlowin) in [#2650](https://github.com/jlowin/fastmcp/pull/2650) +* Fix Windows test warnings by [@jlowin](https://github.com/jlowin) in [#2653](https://github.com/jlowin/fastmcp/pull/2653) +* Cleanup cancelled connection startup by [@jlowin](https://github.com/jlowin) in [#2679](https://github.com/jlowin/fastmcp/pull/2679) +* Fix tool choice bug in sampling examples by [@shawnthapa](https://github.com/shawnthapa) in [#2686](https://github.com/jlowin/fastmcp/pull/2686) +### Docs 📚 +* Simplify Docket tip wording by [@chrisguidry](https://github.com/chrisguidry) in [#2662](https://github.com/jlowin/fastmcp/pull/2662) +### Other Changes 🦾 +* Bump pydocket to ≥0.15.5 by [@jlowin](https://github.com/jlowin) in [#2694](https://github.com/jlowin/fastmcp/pull/2694) + +## New Contributors +* [@shawnthapa](https://github.com/shawnthapa) made their first contribution in [#2686](https://github.com/jlowin/fastmcp/pull/2686) + +**Full Changelog**: [v2.14.0...v2.14.1](https://github.com/jlowin/fastmcp/compare/v2.14.0...v2.14.1) + + + + + +**[v2.14.0: Task and You Shall Receive](https://github.com/jlowin/fastmcp/releases/tag/v2.14.0)** + +FastMCP 2.14 begins adopting the MCP 2025-11-25 specification, introducing protocol-native background tasks (SEP-1686) that enable long-running operations to report progress without blocking clients. The experimental OpenAPI parser graduates to standard, the `OpenAISamplingHandler` is promoted from experimental, and deprecated APIs accumulated across the 2.x series are removed. + +⏳ **Background Tasks** let you add `task=True` to any async tool decorator to run operations in the background with progress tracking. Powered by [Docket](https://github.com/chrisguidry/docket), an enterprise task scheduler handling millions of concurrent tasks daily—in-memory backends work out-of-the-box, and Redis URLs enable persistence and horizontal scaling. + +🔧 **OpenAPI Parser Promoted** from experimental to standard with improved performance through single-pass schema processing and cleaner abstractions. + +📋 **MCP 2025-11-25 Specification Support** including SSE polling and event resumability (SEP-1699), multi-select enum elicitation schemas (SEP-1330), default values for elicitation (SEP-1034), and tool name validation at registration time (SEP-986). + +## Breaking Changes +- Docket is always enabled; task execution is forbidden through proxies +- Task protocol enabled by default +- Removed deprecated settings, imports, and methods accumulated across 2.x series + +## What's Changed +### New Features 🎉 +* OpenAPI parser is now the default by [@jlowin](https://github.com/jlowin) in [#2583](https://github.com/jlowin/fastmcp/pull/2583) +* Implement SEP-1686: Background Tasks by [@jlowin](https://github.com/jlowin) in [#2550](https://github.com/jlowin/fastmcp/pull/2550) +### Enhancements 🔧 +* Expose InitializeResult in middleware by [@jlowin](https://github.com/jlowin) in [#2562](https://github.com/jlowin/fastmcp/pull/2562) +* Update MCP SDK auth compatibility by [@jlowin](https://github.com/jlowin) in [#2574](https://github.com/jlowin/fastmcp/pull/2574) +* Validate tool names at registration (SEP-986) by [@jlowin](https://github.com/jlowin) in [#2588](https://github.com/jlowin/fastmcp/pull/2588) +* Support SEP-1034 and SEP-1330 for elicitation by [@jlowin](https://github.com/jlowin) in [#2595](https://github.com/jlowin/fastmcp/pull/2595) +* Implement SSE polling (SEP-1699) by [@jlowin](https://github.com/jlowin) in [#2612](https://github.com/jlowin/fastmcp/pull/2612) +* Expose session ID callback by [@jlowin](https://github.com/jlowin) in [#2628](https://github.com/jlowin/fastmcp/pull/2628) +### Fixes 🐞 +* Fix OAuth metadata discovery by [@jlowin](https://github.com/jlowin) in [#2565](https://github.com/jlowin/fastmcp/pull/2565) +* Fix fastapi.cli package structure by [@jlowin](https://github.com/jlowin) in [#2570](https://github.com/jlowin/fastmcp/pull/2570) +* Correct OAuth error codes by [@jlowin](https://github.com/jlowin) in [#2578](https://github.com/jlowin/fastmcp/pull/2578) +* Prevent function signature modification by [@jlowin](https://github.com/jlowin) in [#2590](https://github.com/jlowin/fastmcp/pull/2590) +* Fix proxy client kwargs by [@jlowin](https://github.com/jlowin) in [#2605](https://github.com/jlowin/fastmcp/pull/2605) +* Fix nested server routing by [@jlowin](https://github.com/jlowin) in [#2618](https://github.com/jlowin/fastmcp/pull/2618) +* Use access token expiry fallback by [@jlowin](https://github.com/jlowin) in [#2635](https://github.com/jlowin/fastmcp/pull/2635) +* Handle transport cleanup exceptions by [@jlowin](https://github.com/jlowin) in [#2642](https://github.com/jlowin/fastmcp/pull/2642) +### Docs 📚 +* Add OCI and Supabase integration docs by [@jlowin](https://github.com/jlowin) in [#2580](https://github.com/jlowin/fastmcp/pull/2580) +* Add v2.14.0 upgrade guide by [@jlowin](https://github.com/jlowin) in [#2598](https://github.com/jlowin/fastmcp/pull/2598) +* Rewrite background tasks documentation by [@jlowin](https://github.com/jlowin) in [#2620](https://github.com/jlowin/fastmcp/pull/2620) +* Document read-only tool patterns by [@jlowin](https://github.com/jlowin) in [#2632](https://github.com/jlowin/fastmcp/pull/2632) + +## New Contributors +11 total contributors including 7 first-time participants. + +**Full Changelog**: [v2.13.3...v2.14.0](https://github.com/jlowin/fastmcp/compare/v2.13.3...v2.14.0) + + + + + +**[v2.13.3: Pin-ish Line](https://github.com/jlowin/fastmcp/releases/tag/v2.13.3)** + +FastMCP 2.13.3 pins `mcp<1.23` as a precautionary measure. MCP SDK 1.23 introduced changes related to the November 25, 2025 MCP protocol update that break certain FastMCP patches and workarounds, particularly around OAuth implementation details. FastMCP 2.14 introduces proper support for the updated protocol and requires `mcp>=1.23`. + +## What's Changed +### Fixes 🐞 +* Pin MCP SDK below 1.23 by [@jlowin](https://github.com/jlowin) in [#2545](https://github.com/jlowin/fastmcp/pull/2545) + +**Full Changelog**: [v2.13.2...v2.13.3](https://github.com/jlowin/fastmcp/compare/v2.13.2...v2.13.3) + + + + + +**[v2.13.2: Refreshing Changes](https://github.com/jlowin/fastmcp/releases/tag/v2.13.2)** + +FastMCP 2.13.2 polishes the authentication stack with improvements to token refresh, scope handling, and multi-instance deployments. Discord was added as a built-in OAuth provider, Azure and Google token handling became more reliable, and proxy classes now properly forward icons and titles. + +## What's Changed +### New Features 🎉 +* Add Discord OAuth provider by [@jlowin](https://github.com/jlowin) in [#2480](https://github.com/jlowin/fastmcp/pull/2480) +### Enhancements 🔧 +* Descope Provider updates for new well-known URLs by [@anvibanga](https://github.com/anvibanga) in [#2465](https://github.com/jlowin/fastmcp/pull/2465) +* Scalekit provider improvements by [@jlowin](https://github.com/jlowin) in [#2472](https://github.com/jlowin/fastmcp/pull/2472) +* Add CSP customization for consent screens by [@jlowin](https://github.com/jlowin) in [#2488](https://github.com/jlowin/fastmcp/pull/2488) +* Add icon support to proxy classes by [@jlowin](https://github.com/jlowin) in [#2495](https://github.com/jlowin/fastmcp/pull/2495) +### Fixes 🐞 +* Google Provider now defaults to refresh token support by [@jlowin](https://github.com/jlowin) in [#2468](https://github.com/jlowin/fastmcp/pull/2468) +* Fix Azure OAuth token refresh with unprefixed scopes by [@jlowin](https://github.com/jlowin) in [#2475](https://github.com/jlowin/fastmcp/pull/2475) +* Prevent `$defs` mutation during tool transforms by [@jlowin](https://github.com/jlowin) in [#2482](https://github.com/jlowin/fastmcp/pull/2482) +* Fix OAuth proxy refresh token storage for multi-instance deployments by [@jlowin](https://github.com/jlowin) in [#2490](https://github.com/jlowin/fastmcp/pull/2490) +* Fix stale token issue after OAuth refresh by [@jlowin](https://github.com/jlowin) in [#2498](https://github.com/jlowin/fastmcp/pull/2498) +* Fix Azure provider OIDC scope handling by [@jlowin](https://github.com/jlowin) in [#2505](https://github.com/jlowin/fastmcp/pull/2505) + +## New Contributors +7 new contributors made their first FastMCP contributions in this release. + +**Full Changelog**: [v2.13.1...v2.13.2](https://github.com/jlowin/fastmcp/compare/v2.13.1...v2.13.2) + + + + + +**[v2.13.1: Heavy Meta](https://github.com/jlowin/fastmcp/releases/tag/v2.13.1)** + +FastMCP 2.13.1 introduces meta parameter support for `ToolResult`, enabling tools to return supplementary metadata alongside results. This supports emerging use cases like OpenAI's Apps SDK. The release also brings improved OAuth functionality with custom token verifiers including a new DebugTokenVerifier, and adds OCI and Supabase authentication providers. + +🏷️ **Meta parameters for ToolResult** enable tools to return supplementary metadata alongside results, supporting patterns like OpenAI's Apps SDK integration. + +🔐 **Custom token verifiers** with DebugTokenVerifier for development, plus Azure Government support through a `base_authority` parameter and Supabase authentication algorithm configuration. + +🔒 **Security fixes** address CVE-2025-61920 through authlib updates and validate Cursor deeplink URLs using safer Windows APIs. + +## What's Changed +### New Features 🎉 +* Add meta parameter support for ToolResult by [@jlowin](https://github.com/jlowin) in [#2350](https://github.com/jlowin/fastmcp/pull/2350) +* Add OCI authentication provider by [@jlowin](https://github.com/jlowin) in [#2365](https://github.com/jlowin/fastmcp/pull/2365) +* Add Supabase authentication provider by [@jlowin](https://github.com/jlowin) in [#2378](https://github.com/jlowin/fastmcp/pull/2378) +### Enhancements 🔧 +* Add custom token verifier support to OIDCProxy by [@jlowin](https://github.com/jlowin) in [#2355](https://github.com/jlowin/fastmcp/pull/2355) +* Add DebugTokenVerifier for development by [@jlowin](https://github.com/jlowin) in [#2362](https://github.com/jlowin/fastmcp/pull/2362) +* Add Azure Government support via base_authority parameter by [@jlowin](https://github.com/jlowin) in [#2385](https://github.com/jlowin/fastmcp/pull/2385) +* Add Supabase authentication algorithm configuration by [@jlowin](https://github.com/jlowin) in [#2392](https://github.com/jlowin/fastmcp/pull/2392) +### Fixes 🐞 +* Security: Update authlib for CVE-2025-61920 by [@jlowin](https://github.com/jlowin) in [#2398](https://github.com/jlowin/fastmcp/pull/2398) +* Validate Cursor deeplink URLs using safer Windows APIs by [@jlowin](https://github.com/jlowin) in [#2405](https://github.com/jlowin/fastmcp/pull/2405) +* Exclude MCP SDK 1.21.1 due to integration test failures by [@jlowin](https://github.com/jlowin) in [#2422](https://github.com/jlowin/fastmcp/pull/2422) + +## New Contributors +18 new contributors joined in this release across 70+ pull requests. + +**Full Changelog**: [v2.13.0...v2.13.1](https://github.com/jlowin/fastmcp/compare/v2.13.0...v2.13.1) + + + **[v2.13.0: Cache Me If You Can](https://github.com/jlowin/fastmcp/releases/tag/v2.13.0)** diff --git a/docs/docs.json b/docs/docs.json index 889e8f37b..f51daeeea 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -12,7 +12,7 @@ "decoration": "gradient" }, "banner": { - "content": "Join the [FastMCP community](https://discord.gg/uu8dJCgttd)!" + "content": "Join us at the inaugural PyAI Conf in San Francisco on March 10th! [Learn More](https://pyai.events?utm_source=gofastmcp)" }, "colors": { "dark": "#f72585", @@ -41,7 +41,7 @@ "socials": { "discord": "https://discord.gg/uu8dJCgttd", "github": "https://github.com/jlowin/fastmcp", - "website": "https://prefect.ai", + "website": "https://www.prefect.io", "x": "https://x.com/fastmcp" } }, diff --git a/docs/getting-started/installation.mdx b/docs/getting-started/installation.mdx index edcaa5983..ff9bb929a 100644 --- a/docs/getting-started/installation.mdx +++ b/docs/getting-started/installation.mdx @@ -23,6 +23,10 @@ Alternatively, you can install it directly with `pip` or `uv pip`: ``` + +**FastMCP 3.0** is in development and may include breaking changes. To avoid unexpected issues, pin your dependency to v2: `fastmcp<3` + + ### Verify Installation To verify that FastMCP is installed correctly, you can run the following command: diff --git a/docs/getting-started/welcome.mdx b/docs/getting-started/welcome.mdx index f424497ee..bbba67535 100644 --- a/docs/getting-started/welcome.mdx +++ b/docs/getting-started/welcome.mdx @@ -35,7 +35,6 @@ if __name__ == "__main__": mcp.run() ``` - ## Beyond Basic MCP FastMCP pioneered Python MCP development, and FastMCP 1.0 was incorporated into the [official MCP SDK](https://github.com/modelcontextprotocol/python-sdk) in 2024. @@ -46,6 +45,10 @@ Ready to build? Start with our [installation guide](/getting-started/installatio FastMCP is made with 💙 by [Prefect](https://www.prefect.io/). + +**FastMCP 3.0** is in development and may include breaking changes. To avoid unexpected issues, pin your dependency to v2: `fastmcp<3` + + ## What is MCP? The Model Context Protocol lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. It is often described as "the USB-C port for AI", providing a uniform way to connect LLMs to resources they can use. It may be easier to think of it as an API, but specifically designed for LLM interactions. MCP servers can: diff --git a/docs/integrations/supabase.mdx b/docs/integrations/supabase.mdx index cbaa48920..b98c9cb7d 100644 --- a/docs/integrations/supabase.mdx +++ b/docs/integrations/supabase.mdx @@ -17,7 +17,7 @@ This guide shows you how to secure your FastMCP server using **Supabase Auth**. ### Prerequisites Before you begin, you will need: -1. A **[Supabase Account](https://supabase.com/)** with a project +1. A **[Supabase Account](https://supabase.com/)** with a project or a self-hosted **Supabase Auth** instance 2. Your FastMCP server's URL (can be localhost for development, e.g., `http://localhost:8000`) ### Step 1: Get Supabase Project URL @@ -37,7 +37,8 @@ from fastmcp.server.auth.providers.supabase import SupabaseProvider # Configure Supabase Auth auth = SupabaseProvider( project_url="https://abc123.supabase.co", - base_url="http://localhost:8000" + base_url="http://localhost:8000", + auth_route="/my/auth/route" # if self-hosting and using custom routes ) mcp = FastMCP("Supabase Protected Server", auth=auth) @@ -117,6 +118,10 @@ Your Supabase project URL (e.g., `https://abc123.supabase.co`) Public URL of your FastMCP server (e.g., `https://your-server.com` or `http://localhost:8000` for development) + +Your Supabase auth route (e.g., `/auth/v1`) + + Comma-, space-, or JSON-separated list of required OAuth scopes (e.g., `openid email` or `["openid", "email"]`) diff --git a/docs/servers/tools.mdx b/docs/servers/tools.mdx index 2a5be722c..b443e8e77 100644 --- a/docs/servers/tools.mdx +++ b/docs/servers/tools.mdx @@ -173,6 +173,10 @@ def my_tool() -> None: By default, FastMCP converts Python functions into MCP tools by inspecting the function's signature and type annotations. This allows you to use standard Python type annotations for your tools. In general, the framework strives to "just work": idiomatic Python behaviors like parameter defaults and type annotations are automatically translated into MCP schemas. However, there are a number of ways to customize the behavior of your tools. + +FastMCP automatically dereferences `$ref` entries in tool schemas to ensure compatibility with MCP clients that don't fully support JSON Schema references (e.g., VS Code Copilot, Claude Desktop). This means complex Pydantic models with shared types are inlined in the schema rather than using `$defs` references. + + ### Type Annotations MCP tools have typed arguments, and FastMCP uses type annotations to determine those types. Therefore, you should use standard Python type annotations for tool arguments: diff --git a/docs/updates.mdx b/docs/updates.mdx index ac65c4ff4..a846a6ba6 100644 --- a/docs/updates.mdx +++ b/docs/updates.mdx @@ -5,6 +5,118 @@ icon: "sparkles" tag: NEW --- + + +Fixes a memory leak in the memory:// docket broker where cancelled tasks accumulated instead of being cleaned up. Bumps pydocket to ≥0.17.2. + + + + + +Fixes a fresh install bug where the packaging library was missing as a direct dependency, plus backports $ref dereferencing in tool schemas and a task capabilities location fix. + + + + + +Sometimes five seconds just isn't enough. This release fixes an HTTP transport bug that was cutting connections short, along with OAuth and Redis fixes, better ASGI support, and CLI update notifications so you never miss a beat. + + + + + +FastMCP 2.14.2 brings a wave of community contributions safely into the 2.x line. A variety of important fixes backported from 3.0 work improve OpenAPI 3.1 compatibility, MCP spec compliance for output schemas and elicitation, and correct a subtle base_url fallback issue. The CLI now gently reminds you that FastMCP 3.0 is on the horizon. + + + + + +FastMCP 2.14.1 introduces sampling with tools (SEP-1577), enabling servers to pass tools to `ctx.sample()` for agentic workflows where the LLM can automatically execute tool calls in a loop. + +🤖 **Sampling with tools** lets servers leverage client LLM capabilities for multi-step agentic workflows. The new `ctx.sample_step()` method provides single LLM calls with tool inspection, while `result_type` enables structured outputs via validated Pydantic models. + +🔧 **AnthropicSamplingHandler** joins the existing OpenAI handler, and both are now promoted from experimental to production-ready status with a unified API. + + + + + +FastMCP 2.14 begins adopting the MCP 2025-11-25 specification, introducing protocol-native background tasks that enable long-running operations to report progress without blocking clients. + +⏳ **Background Tasks (SEP-1686)** let you add `task=True` to any async tool decorator. Powered by [Docket](https://github.com/chrisguidry/docket) for enterprise task scheduling—in-memory backends work out-of-the-box, Redis enables persistence and horizontal scaling. + +🔧 **OpenAPI Parser Promoted** from experimental to standard with improved performance through single-pass schema processing. + +📋 **MCP Spec Updates** including SSE polling (SEP-1699), multi-select elicitation (SEP-1330), and tool name validation (SEP-986). Also removes deprecated APIs accumulated across 2.x. + + + + + +Pins `mcp<1.23` as a precaution due to MCP SDK changes related to the 11/25/25 protocol update that break certain FastMCP patches and workarounds. FastMCP 2.14 introduces proper support for the updated protocol. + + + + + +Polishes the authentication stack with improvements to token refresh, scope handling, and multi-instance deployments. + +🎮 **Discord OAuth provider** added as a built-in authentication option. + +🔄 **Token refresh fixes** for Azure and Google providers, plus OAuth proxy improvements for multi-instance deployments. + +🎨 **Icon support** added to proxy classes for richer UX. + + + + + +Introduces meta parameter support for `ToolResult`, enabling tools to return supplementary metadata alongside results for patterns like OpenAI's Apps SDK. + +🏷️ **Meta parameters** let tools return supplementary metadata alongside results. + +🔐 **New auth providers** for OCI and Supabase, plus custom token verifiers with DebugTokenVerifier for development. + +🔒 **Security fixes** for CVE-2025-61920 and safer Cursor deeplink URL validation on Windows. + + + =1.1.0", "exceptiongroup>=1.2.2", "httpx>=0.28.1", - "mcp>=1.24.0", + "mcp>=1.24.0,<2.0", "openapi-pydantic>=0.5.1", "platformdirs>=4.0.0", - "pydocket>=0.15.5", + "pydocket>=0.17.2", + "fakeredis[lua]<2.35.0", "rich>=13.9.4", "cyclopts>=4.0.0", "authlib>=1.6.5", @@ -20,6 +21,8 @@ dependencies = [ "uvicorn>=0.35", "websockets>=15.0.1", "jsonschema-path>=0.3.4", + "jsonref>=1.1.0", + "packaging>=20.0", ] requires-python = ">=3.10" @@ -101,7 +104,6 @@ style = "pep440" bump = true fallback-version = "0.0.0" - [tool.pytest.ini_options] asyncio_mode = "auto" # filterwarnings = ["error::DeprecationWarning"] diff --git a/src/fastmcp/cli/cli.py b/src/fastmcp/cli/cli.py index a5e5d6698..495aa2dce 100644 --- a/src/fastmcp/cli/cli.py +++ b/src/fastmcp/cli/cli.py @@ -28,6 +28,7 @@ from fastmcp.utilities.inspect import ( ) from fastmcp.utilities.logging import get_logger from fastmcp.utilities.mcp_server_config import MCPServerConfig +from fastmcp.utilities.version_check import check_for_newer_version logger = get_logger("cli") console = Console() @@ -122,6 +123,14 @@ def version( else: console.print(g) + # Check for updates (not included in --copy output) + if newer_version := check_for_newer_version(): + console.print() + console.print( + f"[bold]🎉 FastMCP update available:[/bold] [green]{newer_version}[/green]" + ) + console.print("[dim]Run: pip install --upgrade fastmcp[/dim]") + @app.command async def dev( diff --git a/src/fastmcp/client/auth/oauth.py b/src/fastmcp/client/auth/oauth.py index 2190b9a43..47e95843a 100644 --- a/src/fastmcp/client/auth/oauth.py +++ b/src/fastmcp/client/auth/oauth.py @@ -105,10 +105,13 @@ class TokenStorageAdapter(TokenStorage): @override async def set_tokens(self, tokens: OAuthToken) -> None: + # Don't set TTL based on access token expiry - the refresh token may be + # valid much longer. Use 1 year as a reasonable upper bound; the OAuth + # provider handles actual token expiry/refresh logic. await self._storage_oauth_token.put( key=self._get_token_cache_key(), value=tokens, - ttl=tokens.expires_in, + ttl=60 * 60 * 24 * 365, # 1 year ) @override diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index 2eaede2a1..62c7f1e21 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -25,7 +25,7 @@ from mcp.client.sse import sse_client from mcp.client.stdio import stdio_client from mcp.client.streamable_http import streamable_http_client from mcp.server.fastmcp import FastMCP as FastMCP1Server -from mcp.shared._httpx_utils import McpHttpClientFactory +from mcp.shared._httpx_utils import McpHttpClientFactory, create_mcp_http_client from mcp.shared.memory import create_client_server_memory_streams from pydantic import AnyUrl from typing_extensions import TypedDict, Unpack @@ -36,7 +36,6 @@ from fastmcp.client.auth.oauth import OAuth from fastmcp.mcp_config import MCPConfig, infer_transport_type_from_url from fastmcp.server.dependencies import get_http_headers from fastmcp.server.server import FastMCP -from fastmcp.server.tasks.capabilities import get_task_capabilities from fastmcp.utilities.logging import get_logger from fastmcp.utilities.mcp_server_config.v1.environments.uv import UVEnvironment @@ -284,25 +283,31 @@ class StreamableHttpTransport(ClientTransport): # need to be forwarded to the remote server. headers = get_http_headers() | self.headers - # Build httpx client configuration - httpx_client_kwargs: dict[str, Any] = { - "headers": headers, - "auth": self.auth, - "follow_redirects": True, - } - - # Configure timeout if provided (convert timedelta to seconds for httpx) + # Configure timeout if provided, preserving MCP's 30s connect default + timeout: httpx.Timeout | None = None if session_kwargs.get("read_timeout_seconds") is not None: read_timeout_seconds = cast( datetime.timedelta, session_kwargs.get("read_timeout_seconds") ) - httpx_client_kwargs["timeout"] = read_timeout_seconds.total_seconds() + timeout = httpx.Timeout(30.0, read=read_timeout_seconds.total_seconds()) - # Create httpx client from factory or use default + # Create httpx client from factory or use default with MCP-appropriate timeouts + # create_mcp_http_client uses 30s connect/5min read timeout by default, + # and always enables follow_redirects if self.httpx_client_factory is not None: - http_client = self.httpx_client_factory(**httpx_client_kwargs) + # Factory clients get the full kwargs for backwards compatibility + http_client = self.httpx_client_factory( + headers=headers, + auth=self.auth, + follow_redirects=True, + **({"timeout": timeout} if timeout else {}), + ) else: - http_client = httpx.AsyncClient(**httpx_client_kwargs) + http_client = create_mcp_http_client( + headers=headers, + timeout=timeout, + auth=self.auth, + ) # Ensure httpx client is closed after use async with ( @@ -894,16 +899,11 @@ class FastMCPTransport(ClientTransport): anyio.create_task_group() as tg, _enter_server_lifespan(server=self.server), ): - # Build experimental capabilities - experimental_capabilities = get_task_capabilities() - tg.start_soon( lambda: self.server._mcp_server.run( server_read, server_write, - self.server._mcp_server.create_initialization_options( - experimental_capabilities=experimental_capabilities - ), + self.server._mcp_server.create_initialization_options(), raise_exceptions=self.raise_exceptions, ) ) diff --git a/src/fastmcp/prompts/prompt_manager.py b/src/fastmcp/prompts/prompt_manager.py index 1563a1831..e66c1e878 100644 --- a/src/fastmcp/prompts/prompt_manager.py +++ b/src/fastmcp/prompts/prompt_manager.py @@ -7,7 +7,7 @@ from typing import Any from mcp import GetPromptResult from fastmcp import settings -from fastmcp.exceptions import NotFoundError, PromptError +from fastmcp.exceptions import FastMCPError, NotFoundError, PromptError from fastmcp.prompts.prompt import FunctionPrompt, Prompt, PromptResult from fastmcp.settings import DuplicateBehavior from fastmcp.utilities.logging import get_logger @@ -107,9 +107,8 @@ class PromptManager: try: messages = await prompt.render(arguments) return GetPromptResult(description=prompt.description, messages=messages) - except PromptError as e: - logger.exception(f"Error rendering prompt {name!r}") - raise e + except FastMCPError: + raise except Exception as e: logger.exception(f"Error rendering prompt {name!r}") if self.mask_error_details: diff --git a/src/fastmcp/resources/resource_manager.py b/src/fastmcp/resources/resource_manager.py index 7367fb3e9..28416a692 100644 --- a/src/fastmcp/resources/resource_manager.py +++ b/src/fastmcp/resources/resource_manager.py @@ -10,7 +10,7 @@ from typing import Any from pydantic import AnyUrl from fastmcp import settings -from fastmcp.exceptions import NotFoundError, ResourceError +from fastmcp.exceptions import FastMCPError, NotFoundError, ResourceError from fastmcp.resources.resource import Resource from fastmcp.resources.template import ( ResourceTemplate, @@ -268,10 +268,9 @@ class ResourceManager: uri_str, params=params, ) - # Pass through ResourceErrors as-is - except ResourceError as e: - logger.error(f"Error creating resource from template: {e}") - raise e + # Pass through FastMCPErrors as-is + except FastMCPError: + raise # Handle other exceptions except Exception as e: logger.error(f"Error creating resource from template: {e}") @@ -299,10 +298,9 @@ class ResourceManager: try: return await resource.read() - # raise ResourceErrors as-is - except ResourceError as e: - logger.exception(f"Error reading resource {uri_str!r}") - raise e + # raise FastMCPErrors as-is + except FastMCPError: + raise # Handle other exceptions except Exception as e: @@ -322,11 +320,8 @@ class ResourceManager: try: resource = await template.create_resource(uri_str, params=params) return await resource.read() - except ResourceError as e: - logger.exception( - f"Error reading resource from template {uri_str!r}" - ) - raise e + except FastMCPError: + raise except Exception as e: logger.exception( f"Error reading resource from template {uri_str!r}" diff --git a/src/fastmcp/server/auth/auth.py b/src/fastmcp/server/auth/auth.py index bc255b394..c3bbd1b17 100644 --- a/src/fastmcp/server/auth/auth.py +++ b/src/fastmcp/server/auth/auth.py @@ -114,6 +114,8 @@ class AuthProvider(TokenVerifierProtocol): base_url = AnyHttpUrl(base_url) self.base_url = base_url self.required_scopes = required_scopes or [] + self._mcp_path: str | None = None + self._resource_url: AnyHttpUrl | None = None async def verify_token(self, token: str) -> AccessToken | None: """Verify a bearer token and return access info if valid. @@ -128,6 +130,20 @@ class AuthProvider(TokenVerifierProtocol): """ raise NotImplementedError("Subclasses must implement verify_token") + def set_mcp_path(self, mcp_path: str | None) -> None: + """Set the MCP endpoint path and compute resource URL. + + This method is called by get_routes() to configure the expected + resource URL before route creation. Subclasses can override to + perform additional initialization that depends on knowing the + MCP endpoint path. + + Args: + mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp") + """ + self._mcp_path = mcp_path + self._resource_url = self._get_resource_url(mcp_path) + def get_routes( self, mcp_path: str | None = None, @@ -407,6 +423,8 @@ class OAuthProvider( Returns: List of OAuth routes """ + # Configure resource URL before creating routes + self.set_mcp_path(mcp_path) # Create standard OAuth authorization server routes # Pass base_url as issuer_url to ensure metadata declares endpoints where @@ -451,11 +469,8 @@ class OAuthProvider( else: oauth_routes.append(route) - # Get the resource URL based on the MCP path - resource_url = self._get_resource_url(mcp_path) - # Add protected resource routes if this server is also acting as a resource server - if resource_url: + if self._resource_url: supported_scopes = ( self.client_registration_options.valid_scopes if self.client_registration_options @@ -463,7 +478,7 @@ class OAuthProvider( else self.required_scopes ) protected_routes = create_protected_resource_routes( - resource_url=resource_url, + resource_url=self._resource_url, authorization_servers=[cast(AnyHttpUrl, self.issuer_url)], scopes_supported=supported_scopes, ) diff --git a/src/fastmcp/server/auth/oauth_proxy.py b/src/fastmcp/server/auth/oauth_proxy.py index b802754f0..4f84a825a 100644 --- a/src/fastmcp/server/auth/oauth_proxy.py +++ b/src/fastmcp/server/auth/oauth_proxy.py @@ -34,7 +34,6 @@ from authlib.integrations.httpx_client import AsyncOAuth2Client from cryptography.fernet import Fernet from key_value.aio.adapters.pydantic import PydanticAdapter from key_value.aio.protocols import AsyncKeyValue -from key_value.aio.stores.disk import DiskStore from key_value.aio.wrappers.encryption import FernetEncryptionWrapper from mcp.server.auth.provider import ( AccessToken, @@ -805,14 +804,16 @@ class OAuthProxy(OAuthProvider): salt="fastmcp-jwt-signing-key", ) - self._jwt_issuer: JWTIssuer = JWTIssuer( - issuer=str(self.base_url), - audience=f"{str(self.base_url).rstrip('/')}/mcp", - signing_key=jwt_signing_key, - ) + # Store JWT signing key for deferred JWTIssuer creation in set_mcp_path() + self._jwt_signing_key: bytes = jwt_signing_key + # JWTIssuer will be created in set_mcp_path() with correct audience + self._jwt_issuer: JWTIssuer | None = None # If the user does not provide a store, we will provide an encrypted disk store if client_storage is None: + # Import lazily to avoid sqlite3 dependency when not using OAuthProxy + from key_value.aio.stores.disk import DiskStore + storage_encryption_key = derive_jwt_key( high_entropy_material=jwt_signing_key.decode(), salt="fastmcp-storage-encryption-key", @@ -897,6 +898,47 @@ class OAuthProxy(OAuthProvider): self._upstream_authorization_endpoint, ) + # ------------------------------------------------------------------------- + # MCP Path Configuration + # ------------------------------------------------------------------------- + + def set_mcp_path(self, mcp_path: str | None) -> None: + """Set the MCP endpoint path and create JWTIssuer with correct audience. + + This method is called by get_routes() to configure the resource URL + and create the JWTIssuer. The JWT audience is set to the full resource + URL (e.g., http://localhost:8000/mcp) to ensure tokens are bound to + this specific MCP endpoint. + + Args: + mcp_path: The path where the MCP endpoint is mounted (e.g., "/mcp") + """ + super().set_mcp_path(mcp_path) + + # Create JWT issuer with correct audience based on actual MCP path + # This ensures tokens are bound to the specific resource URL + self._jwt_issuer = JWTIssuer( + issuer=str(self.base_url), + audience=str(self._resource_url), + signing_key=self._jwt_signing_key, + ) + + logger.debug("Configured OAuth proxy for resource URL: %s", self._resource_url) + + @property + def jwt_issuer(self) -> JWTIssuer: + """Get the JWT issuer, ensuring it has been initialized. + + The JWT issuer is created when set_mcp_path() is called (via get_routes()). + This property ensures a clear error if used before initialization. + """ + if self._jwt_issuer is None: + raise RuntimeError( + "JWT issuer not initialized. Ensure get_routes() is called " + "before token operations." + ) + return self._jwt_issuer + # ------------------------------------------------------------------------- # PKCE Helper Methods # ------------------------------------------------------------------------- @@ -998,13 +1040,29 @@ class OAuthProxy(OAuthProvider): """Start OAuth transaction and route through consent interstitial. Flow: - 1. Store transaction with client details and PKCE (if forwarding) - 2. Return local /consent URL; browser visits consent first - 3. Consent handler redirects to upstream IdP if approved/already approved + 1. Validate client's resource matches server's resource URL (security check) + 2. Store transaction with client details and PKCE (if forwarding) + 3. Return local /consent URL; browser visits consent first + 4. Consent handler redirects to upstream IdP if approved/already approved If consent is disabled (require_authorization_consent=False), skip the consent screen and redirect directly to the upstream IdP. """ + # Security check: validate client's requested resource matches this server + # This prevents tokens intended for one server from being used on another + client_resource = getattr(params, "resource", None) + if client_resource and self._resource_url: + if str(client_resource) != str(self._resource_url): + logger.warning( + "Resource mismatch: client requested %s but server is %s", + client_resource, + self._resource_url, + ) + raise AuthorizeError( + error="invalid_target", # type: ignore[arg-type] + error_description="Resource does not match this server", + ) + # Generate transaction ID for this authorization request txn_id = secrets.token_urlsafe(32) @@ -1163,12 +1221,24 @@ class OAuthProxy(OAuthProvider): # - 1 year if no refresh token (likely API-key-style token like GitHub OAuth Apps) if "expires_in" in idp_tokens: expires_in = int(idp_tokens["expires_in"]) + logger.debug( + "Access token TTL: %d seconds (from IdP expires_in)", expires_in + ) elif self._fallback_access_token_expiry_seconds is not None: expires_in = self._fallback_access_token_expiry_seconds + logger.debug( + "Access token TTL: %d seconds (using configured fallback)", expires_in + ) elif idp_tokens.get("refresh_token"): expires_in = DEFAULT_ACCESS_TOKEN_EXPIRY_SECONDS + logger.debug( + "Access token TTL: %d seconds (default, has refresh token)", expires_in + ) else: expires_in = DEFAULT_ACCESS_TOKEN_EXPIRY_NO_REFRESH_SECONDS + logger.debug( + "Access token TTL: %d seconds (default, no refresh token)", expires_in + ) # Calculate refresh token expiry if provided by upstream # Some providers include refresh_expires_in, some don't @@ -1208,15 +1278,16 @@ class OAuthProxy(OAuthProvider): await self._upstream_token_store.put( key=upstream_token_id, value=upstream_token_set, - ttl=refresh_expires_in - or expires_in, # Auto-expire when refresh token, or access token expires + ttl=max( + refresh_expires_in or 0, expires_in, 1 + ), # Keep until longest-lived token expires (min 1s for safety) ) logger.debug("Stored encrypted upstream tokens (jti=%s)", access_jti[:8]) # Issue minimal FastMCP access token (just a reference via JTI) if client.client_id is None: raise TokenError("invalid_client", "Client ID is required") - fastmcp_access_token = self._jwt_issuer.issue_access_token( + fastmcp_access_token = self.jwt_issuer.issue_access_token( client_id=client.client_id, scopes=authorization_code.scopes, jti=access_jti, @@ -1227,7 +1298,7 @@ class OAuthProxy(OAuthProvider): # Use upstream refresh token expiry to align lifetimes fastmcp_refresh_token = None if refresh_jti and refresh_expires_in: - fastmcp_refresh_token = self._jwt_issuer.issue_refresh_token( + fastmcp_refresh_token = self.jwt_issuer.issue_refresh_token( client_id=client.client_id, scopes=authorization_code.scopes, jti=refresh_jti, @@ -1352,7 +1423,7 @@ class OAuthProxy(OAuthProvider): """ # Verify FastMCP refresh token try: - refresh_payload = self._jwt_issuer.verify_token(refresh_token.token) + refresh_payload = self.jwt_issuer.verify_token(refresh_token.token) refresh_jti = refresh_payload["jti"] except Exception as e: logger.debug("FastMCP refresh token validation failed: %s", e) @@ -1409,10 +1480,21 @@ class OAuthProxy(OAuthProvider): # (user override still applies if set) if "expires_in" in token_response: new_expires_in = int(token_response["expires_in"]) + logger.debug( + "Refreshed access token TTL: %d seconds (from IdP expires_in)", + new_expires_in, + ) elif self._fallback_access_token_expiry_seconds is not None: new_expires_in = self._fallback_access_token_expiry_seconds + logger.debug( + "Refreshed access token TTL: %d seconds (using configured fallback)", + new_expires_in, + ) else: new_expires_in = DEFAULT_ACCESS_TOKEN_EXPIRY_SECONDS + logger.debug( + "Refreshed access token TTL: %d seconds (default)", new_expires_in + ) upstream_token_set.access_token = token_response["access_token"] upstream_token_set.expires_at = time.time() + new_expires_in @@ -1446,22 +1528,25 @@ class OAuthProxy(OAuthProvider): ) upstream_token_set.raw_token_data = token_response + # Calculate refresh TTL for storage + refresh_ttl = new_refresh_expires_in or ( + int(upstream_token_set.refresh_token_expires_at - time.time()) + if upstream_token_set.refresh_token_expires_at + else 60 * 60 * 24 * 30 # Default to 30 days if unknown + ) await self._upstream_token_store.put( key=upstream_token_set.upstream_token_id, value=upstream_token_set, - ttl=new_refresh_expires_in - or ( - int(upstream_token_set.refresh_token_expires_at - time.time()) - if upstream_token_set.refresh_token_expires_at - else 60 * 60 * 24 * 30 # Default to 30 days if unknown - ), # Auto-expire when refresh token expires + ttl=max( + refresh_ttl, new_expires_in, 1 + ), # Keep until longest-lived token expires (min 1s for safety) ) # Issue new minimal FastMCP access token (just a reference via JTI) if client.client_id is None: raise TokenError("invalid_client", "Client ID is required") new_access_jti = secrets.token_urlsafe(32) - new_fastmcp_access = self._jwt_issuer.issue_access_token( + new_fastmcp_access = self.jwt_issuer.issue_access_token( client_id=client.client_id, scopes=scopes, jti=new_access_jti, @@ -1482,7 +1567,7 @@ class OAuthProxy(OAuthProvider): # Issue NEW minimal FastMCP refresh token (rotation for security) # Use upstream refresh token expiry to align lifetimes new_refresh_jti = secrets.token_urlsafe(32) - new_fastmcp_refresh = self._jwt_issuer.issue_refresh_token( + new_fastmcp_refresh = self.jwt_issuer.issue_refresh_token( client_id=client.client_id, scopes=scopes, jti=new_refresh_jti, @@ -1491,7 +1576,7 @@ class OAuthProxy(OAuthProvider): ) # Store new refresh token JTI mapping with aligned expiry - refresh_ttl = new_refresh_expires_in or 60 * 60 * 24 * 30 + # (reuse refresh_ttl calculated above for upstream token store) await self._jti_mapping_store.put( key=new_refresh_jti, value=JTIMapping( @@ -1558,13 +1643,16 @@ class OAuthProxy(OAuthProvider): """ try: # 1. Verify FastMCP JWT signature and claims - payload = self._jwt_issuer.verify_token(token) + payload = self.jwt_issuer.verify_token(token) jti = payload["jti"] # 2. Look up upstream token via JTI mapping jti_mapping = await self._jti_mapping_store.get(key=jti) if not jti_mapping: - logger.debug("JTI mapping not found: %s", jti) + logger.info( + "JTI mapping not found (token may have expired): jti=%s...", + jti[:16], + ) return None upstream_token_set = await self._upstream_token_store.get( @@ -1807,6 +1895,11 @@ class OAuthProxy(OAuthProvider): logger.debug( f"Successfully exchanged IdP code for tokens (transaction: {txn_id}, PKCE: {bool(proxy_code_verifier)})" ) + logger.debug( + "IdP token response: expires_in=%s, has_refresh_token=%s", + idp_tokens.get("expires_in"), + "refresh_token" in idp_tokens, + ) except Exception as e: logger.error("IdP token exchange failed: %s", e) diff --git a/src/fastmcp/server/auth/providers/supabase.py b/src/fastmcp/server/auth/providers/supabase.py index 418831d48..50bb3be08 100644 --- a/src/fastmcp/server/auth/providers/supabase.py +++ b/src/fastmcp/server/auth/providers/supabase.py @@ -34,6 +34,7 @@ class SupabaseProviderSettings(BaseSettings): project_url: AnyHttpUrl base_url: AnyHttpUrl + auth_route: str = "/auth/v1" algorithm: Literal["HS256", "RS256", "ES256"] = "ES256" required_scopes: list[str] | None = None @@ -59,8 +60,8 @@ class SupabaseProvider(RemoteAuthProvider): - Asymmetric keys (RS256/ES256) are recommended for production 2. JWT Verification: - - FastMCP verifies JWTs using the JWKS endpoint at {project_url}/auth/v1/.well-known/jwks.json - - JWTs are issued by {project_url}/auth/v1 + - FastMCP verifies JWTs using the JWKS endpoint at {project_url}{auth_route}/.well-known/jwks.json + - JWTs are issued by {project_url}{auth_route} - Tokens are cached for up to 10 minutes by Supabase's edge servers - Algorithm must match your Supabase Auth configuration @@ -93,6 +94,7 @@ class SupabaseProvider(RemoteAuthProvider): *, project_url: AnyHttpUrl | str | NotSetT = NotSet, base_url: AnyHttpUrl | str | NotSetT = NotSet, + auth_route: str | NotSetT = NotSet, algorithm: Literal["HS256", "RS256", "ES256"] | NotSetT = NotSet, required_scopes: list[str] | NotSetT | None = NotSet, token_verifier: TokenVerifier | None = None, @@ -102,6 +104,7 @@ class SupabaseProvider(RemoteAuthProvider): Args: project_url: Your Supabase project URL (e.g., "https://abc123.supabase.co") base_url: Public URL of this FastMCP server + auth_route: Supabase Auth route. Defaults to "/auth/v1". algorithm: JWT signing algorithm (HS256, RS256, or ES256). Must match your Supabase Auth configuration. Defaults to ES256. required_scopes: Optional list of scopes to require for all requests. @@ -115,6 +118,7 @@ class SupabaseProvider(RemoteAuthProvider): for k, v in { "project_url": project_url, "base_url": base_url, + "auth_route": auth_route, "algorithm": algorithm, "required_scopes": required_scopes, }.items() @@ -124,12 +128,13 @@ class SupabaseProvider(RemoteAuthProvider): self.project_url = str(settings.project_url).rstrip("/") self.base_url = AnyHttpUrl(str(settings.base_url).rstrip("/")) + self.auth_route = settings.auth_route.strip("/") # Create default JWT verifier if none provided if token_verifier is None: token_verifier = JWTVerifier( - jwks_uri=f"{self.project_url}/auth/v1/.well-known/jwks.json", - issuer=f"{self.project_url}/auth/v1", + jwks_uri=f"{self.project_url}/{self.auth_route}/.well-known/jwks.json", + issuer=f"{self.project_url}/{self.auth_route}", algorithm=settings.algorithm, required_scopes=settings.required_scopes, ) @@ -137,7 +142,7 @@ class SupabaseProvider(RemoteAuthProvider): # Initialize RemoteAuthProvider with Supabase as the authorization server super().__init__( token_verifier=token_verifier, - authorization_servers=[AnyHttpUrl(f"{self.project_url}/auth/v1")], + authorization_servers=[AnyHttpUrl(f"{self.project_url}/{self.auth_route}")], base_url=self.base_url, ) @@ -162,7 +167,7 @@ class SupabaseProvider(RemoteAuthProvider): try: async with httpx.AsyncClient() as client: response = await client.get( - f"{self.project_url}/auth/v1/.well-known/oauth-authorization-server" + f"{self.project_url}/{self.auth_route}/.well-known/oauth-authorization-server" ) response.raise_for_status() metadata = response.json() diff --git a/src/fastmcp/server/context.py b/src/fastmcp/server/context.py index 8cd4f7307..5370f0469 100644 --- a/src/fastmcp/server/context.py +++ b/src/fastmcp/server/context.py @@ -185,10 +185,24 @@ class Context: self._tokens.append(token) # Set current server for dependency injection (use weakref to avoid reference cycles) - from fastmcp.server.dependencies import _current_server + from fastmcp.server.dependencies import ( + _current_docket, + _current_server, + _current_worker, + ) self._server_token = _current_server.set(weakref.ref(self.fastmcp)) + # Set docket/worker from server instance for this request's context. + # This ensures ContextVars work even in environments (like Lambda) where + # lifespan ContextVars don't propagate to request handlers. + server = self.fastmcp + if server._docket is not None: + self._docket_token = _current_docket.set(server._docket) + + if server._worker is not None: + self._worker_token = _current_worker.set(server._worker) + return self async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: @@ -196,10 +210,20 @@ class Context: # Flush any remaining notifications before exiting await self._flush_notifications() - # Reset server token - if hasattr(self, "_server_token"): - from fastmcp.server.dependencies import _current_server + # Reset server/docket/worker tokens + from fastmcp.server.dependencies import ( + _current_docket, + _current_server, + _current_worker, + ) + if hasattr(self, "_worker_token"): + _current_worker.reset(self._worker_token) + delattr(self, "_worker_token") + if hasattr(self, "_docket_token"): + _current_docket.reset(self._docket_token) + delattr(self, "_docket_token") + if hasattr(self, "_server_token"): _current_server.reset(self._server_token) delattr(self, "_server_token") diff --git a/src/fastmcp/server/dependencies.py b/src/fastmcp/server/dependencies.py index 31b170c79..3671ba42b 100644 --- a/src/fastmcp/server/dependencies.py +++ b/src/fastmcp/server/dependencies.py @@ -21,6 +21,7 @@ from mcp.server.auth.provider import ( from mcp.server.lowlevel.server import request_ctx from starlette.requests import Request +from fastmcp.exceptions import FastMCPError from fastmcp.server.auth import AccessToken from fastmcp.server.http import _current_http_request from fastmcp.utilities.types import is_class_member_of_type @@ -188,6 +189,10 @@ async def _resolve_fastmcp_dependencies( resolved[parameter] = await stack.enter_async_context( dependency ) + except FastMCPError: + # Let FastMCPError subclasses (ToolError, ResourceError, etc.) + # propagate unchanged so they can be handled appropriately + raise except Exception as error: fn_name = getattr(fn, "__name__", repr(fn)) raise RuntimeError( diff --git a/src/fastmcp/server/elicitation.py b/src/fastmcp/server/elicitation.py index c2a93a1f7..61fe1ca5b 100644 --- a/src/fastmcp/server/elicitation.py +++ b/src/fastmcp/server/elicitation.py @@ -304,15 +304,19 @@ def _dict_to_enum_schema( multi_select: If True, use anyOf pattern; if False, use oneOf pattern Returns: - {"oneOf": [{"const": "low", "title": "Low Priority"}, ...]} for single-select - {"anyOf": [{"const": "low", "title": "Low Priority"}, ...]} for multi-select + {"type": "string", "oneOf": [...]} for single-select + {"anyOf": [...]} for multi-select (used as array items) """ pattern_key = "anyOf" if multi_select else "oneOf" pattern = [] for value, metadata in enum_dict.items(): title = metadata.get("title", value) pattern.append({"const": value, "title": title}) - return {pattern_key: pattern} + + result: dict[str, Any] = {pattern_key: pattern} + if not multi_select: + result["type"] = "string" + return result def get_elicitation_schema(response_type: type[T]) -> dict[str, Any]: diff --git a/src/fastmcp/server/http.py b/src/fastmcp/server/http.py index 825fcd0aa..6bd6f6d40 100644 --- a/src/fastmcp/server/http.py +++ b/src/fastmcp/server/http.py @@ -21,7 +21,6 @@ from starlette.types import Lifespan, Receive, Scope, Send from fastmcp.server.auth import AuthProvider from fastmcp.server.auth.middleware import RequireAuthMiddleware -from fastmcp.server.tasks.capabilities import get_task_capabilities from fastmcp.utilities.logging import get_logger if TYPE_CHECKING: @@ -160,15 +159,10 @@ def create_sse_app( # Create handler for SSE connections async def handle_sse(scope: Scope, receive: Receive, send: Send) -> Response: async with sse.connect_sse(scope, receive, send) as streams: - # Build experimental capabilities - experimental_capabilities = get_task_capabilities() - await server._mcp_server.run( streams[0], streams[1], - server._mcp_server.create_initialization_options( - experimental_capabilities=experimental_capabilities - ), + server._mcp_server.create_initialization_options(), ) return Response() diff --git a/src/fastmcp/server/low_level.py b/src/fastmcp/server/low_level.py index 536b6aa1f..fcc9cfb78 100644 --- a/src/fastmcp/server/low_level.py +++ b/src/fastmcp/server/low_level.py @@ -163,6 +163,31 @@ class LowLevelServer(_Server[LifespanResultT, RequestT]): **kwargs, ) + def get_capabilities( + self, + notification_options: NotificationOptions, + experimental_capabilities: dict[str, dict[str, Any]], + ) -> mcp.types.ServerCapabilities: + """Override to set capabilities.tasks as a first-class field per SEP-1686. + + This ensures task capabilities appear in capabilities.tasks instead of + capabilities.experimental.tasks, which is required by the MCP spec and + enables proper task detection by clients like VS Code Copilot 1.107+. + """ + from fastmcp.server.tasks.capabilities import get_task_capabilities + + # Get base capabilities from SDK (pass empty dict for experimental) + # since we'll set tasks as a first-class field instead + capabilities = super().get_capabilities( + notification_options, + experimental_capabilities or {}, + ) + + # Set tasks as a first-class field (not experimental) per SEP-1686 + capabilities.tasks = get_task_capabilities() + + return capabilities + async def run( self, read_stream: MemoryObjectReceiveStream[SessionMessage | Exception], diff --git a/src/fastmcp/server/middleware/error_handling.py b/src/fastmcp/server/middleware/error_handling.py index 7cb730d90..fc0e2a1c8 100644 --- a/src/fastmcp/server/middleware/error_handling.py +++ b/src/fastmcp/server/middleware/error_handling.py @@ -87,7 +87,7 @@ class ErrorHandlingMiddleware(Middleware): return error # Map common exceptions to appropriate MCP error codes - error_type = type(error) + error_type = type(error.__cause__) if error.__cause__ else type(error) if error_type in (ValueError, TypeError): return McpError( diff --git a/src/fastmcp/server/openapi/components.py b/src/fastmcp/server/openapi/components.py index 40577cfa7..496a23327 100644 --- a/src/fastmcp/server/openapi/components.py +++ b/src/fastmcp/server/openapi/components.py @@ -64,10 +64,8 @@ class OpenAPITool(Tool): try: # Get base URL from client base_url = ( - str(self._client.base_url) - if hasattr(self._client, "base_url") and self._client.base_url - else "http://localhost" - ) + str(self._client.base_url) if hasattr(self._client, "base_url") else "" + ) or "http://localhost" # Get Headers from client cli_headers = ( diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index 5a34a32f7..14d918397 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -75,7 +75,6 @@ from fastmcp.server.http import ( ) from fastmcp.server.low_level import LowLevelServer from fastmcp.server.middleware import Middleware, MiddlewareContext -from fastmcp.server.tasks.capabilities import get_task_capabilities from fastmcp.server.tasks.config import TaskConfig from fastmcp.server.tasks.handlers import ( handle_prompt_as_task, @@ -105,23 +104,6 @@ if TYPE_CHECKING: logger = get_logger(__name__) -def _create_named_fn_wrapper(fn: Callable[..., Any], name: str) -> Callable[..., Any]: - """Create a wrapper function with a custom __name__ for Docket registration. - - Docket uses fn.__name__ as the key for function registration and lookup. - When mounting servers, we need unique names to avoid collisions between - mounted servers that have identically-named functions. - """ - import functools - - @functools.wraps(fn) - async def wrapper(*args: Any, **kwargs: Any) -> Any: - return await fn(*args, **kwargs) - - wrapper.__name__ = name - return wrapper - - DuplicateBehavior = Literal["warn", "error", "replace", "ignore"] Transport = Literal["stdio", "http", "sse", "streamable-http"] @@ -214,8 +196,9 @@ class FastMCP(Generic[LifespanResultT]): # Resolve server default for background task support self._support_tasks_by_default: bool = tasks if tasks is not None else False - # Docket instance (set during lifespan for cross-task access) + # Docket and Worker instances (set during lifespan for cross-task access) self._docket = None + self._worker = None self._additional_http_routes: list[BaseRoute] = [] self._mounted_servers: list[MountedServer] = [] @@ -437,7 +420,7 @@ class FastMCP(Generic[LifespanResultT]): isinstance(tool, FunctionTool) and tool.task_config.mode != "forbidden" ): - docket.register(tool.fn) + docket.register(tool.fn, names=[tool.key]) for prompt in self._prompt_manager._prompts.values(): if ( @@ -445,21 +428,24 @@ class FastMCP(Generic[LifespanResultT]): and prompt.task_config.mode != "forbidden" ): # task execution requires async fn (validated at creation time) - docket.register(cast(Callable[..., Awaitable[Any]], prompt.fn)) + docket.register( + cast(Callable[..., Awaitable[Any]], prompt.fn), + names=[prompt.key], + ) for resource in self._resource_manager._resources.values(): if ( isinstance(resource, FunctionResource) and resource.task_config.mode != "forbidden" ): - docket.register(resource.fn) + docket.register(resource.fn, names=[resource.name]) for template in self._resource_manager._templates.values(): if ( isinstance(template, FunctionResourceTemplate) and template.task_config.mode != "forbidden" ): - docket.register(template.fn) + docket.register(template.fn, names=[template.name]) # Also register functions from mounted servers so tasks can # execute in the parent's Docket context @@ -482,6 +468,8 @@ class FastMCP(Generic[LifespanResultT]): # Create and start Worker async with Worker(docket, **worker_kwargs) as worker: # type: ignore[arg-type] + # Store on server instance for cross-context access + self._worker = worker # Set Worker in ContextVar so CurrentWorker can access it worker_token = _current_worker.set(worker) try: @@ -489,21 +477,16 @@ class FastMCP(Generic[LifespanResultT]): try: yield finally: - # Cancel worker task on exit with timeout to prevent hanging worker_task.cancel() - with suppress( - asyncio.CancelledError, asyncio.TimeoutError - ): - await asyncio.wait_for(worker_task, timeout=2.0) + with suppress(asyncio.CancelledError): + await worker_task finally: _current_worker.reset(worker_token) + self._worker = None finally: - # Reset ContextVar _current_docket.reset(docket_token) - # Clear instance attribute self._docket = None finally: - # Reset server ContextVar _current_server.reset(server_token) async def _register_mounted_server_functions( @@ -535,8 +518,7 @@ class FastMCP(Generic[LifespanResultT]): fn_name = f"{prefix}_{tool.key}" else: fn_name = tool.key - named_fn = _create_named_fn_wrapper(tool.fn, fn_name) - docket.register(named_fn) + docket.register(tool.fn, names=[fn_name]) # Register prompts with prefixed names for prompt in server._prompt_manager._prompts.values(): @@ -545,10 +527,10 @@ class FastMCP(Generic[LifespanResultT]): and prompt.task_config.mode != "forbidden" ): fn_name = f"{prefix}_{prompt.key}" if prefix else prompt.key - named_fn = _create_named_fn_wrapper( - cast(Callable[..., Awaitable[Any]], prompt.fn), fn_name + docket.register( + cast(Callable[..., Awaitable[Any]], prompt.fn), + names=[fn_name], ) - docket.register(named_fn) # Register resources with prefixed names (use name, not key/URI) for resource in server._resource_manager._resources.values(): @@ -557,8 +539,7 @@ class FastMCP(Generic[LifespanResultT]): and resource.task_config.mode != "forbidden" ): fn_name = f"{prefix}_{resource.name}" if prefix else resource.name - named_fn = _create_named_fn_wrapper(resource.fn, fn_name) - docket.register(named_fn) + docket.register(resource.fn, names=[fn_name]) # Register resource templates with prefixed names (use name, not key/URI) for template in server._resource_manager._templates.values(): @@ -567,8 +548,7 @@ class FastMCP(Generic[LifespanResultT]): and template.task_config.mode != "forbidden" ): fn_name = f"{prefix}_{template.name}" if prefix else template.name - named_fn = _create_named_fn_wrapper(template.fn, fn_name) - docket.register(named_fn) + docket.register(template.fn, names=[fn_name]) # Recursively register from nested mounted servers with accumulated prefix for nested in server._mounted_servers: @@ -584,6 +564,8 @@ class FastMCP(Generic[LifespanResultT]): @asynccontextmanager async def _lifespan_manager(self) -> AsyncIterator[None]: if self._lifespan_result_set: + # Lifespan already ran - ContextVars will be set by Context.__aenter__ + # at request time, so we just yield here. yield return @@ -2516,10 +2498,7 @@ class FastMCP(Generic[LifespanResultT]): """ # Display server banner if show_banner: - log_server_banner( - server=self, - transport="stdio", - ) + log_server_banner(server=self) with temporary_log_level(log_level): async with self._lifespan_manager(): @@ -2528,9 +2507,6 @@ class FastMCP(Generic[LifespanResultT]): f"Starting MCP server {self.name!r} with transport 'stdio'" ) - # Build experimental capabilities - experimental_capabilities = get_task_capabilities() - await self._mcp_server.run( read_stream, write_stream, @@ -2538,7 +2514,6 @@ class FastMCP(Generic[LifespanResultT]): notification_options=NotificationOptions( tools_changed=True ), - experimental_capabilities=experimental_capabilities, ), ) @@ -2582,22 +2557,9 @@ class FastMCP(Generic[LifespanResultT]): stateless_http=stateless_http, ) - # Get the path for the server URL - server_path = ( - app.state.path.lstrip("/") - if hasattr(app, "state") and hasattr(app.state, "path") - else path or "" - ) - # Display server banner if show_banner: - log_server_banner( - server=self, - transport=transport, - host=host, - port=port, - path=server_path, - ) + log_server_banner(server=self) uvicorn_config_from_user = uvicorn_config or {} config_kwargs: dict[str, Any] = { diff --git a/src/fastmcp/server/tasks/capabilities.py b/src/fastmcp/server/tasks/capabilities.py index c08371eca..b1a5d97fb 100644 --- a/src/fastmcp/server/tasks/capabilities.py +++ b/src/fastmcp/server/tasks/capabilities.py @@ -1,22 +1,30 @@ """SEP-1686 task capabilities declaration.""" -from typing import Any +from mcp.types import ( + ServerTasksCapability, + ServerTasksRequestsCapability, + TasksCallCapability, + TasksCancelCapability, + TasksListCapability, + TasksToolsCapability, +) -def get_task_capabilities() -> dict[str, Any]: - """Return the SEP-1686 task capabilities structure. +def get_task_capabilities() -> ServerTasksCapability: + """Return the SEP-1686 task capabilities. - This is the standard capabilities map advertised to clients, - declaring support for list, cancel, and request operations. + Returns task capabilities as a first-class ServerCapabilities field, + declaring support for list, cancel, and request operations per SEP-1686. + + Note: prompts/resources are passed via extra_data since the SDK types + don't include them yet (FastMCP supports them ahead of the spec). """ - return { - "tasks": { - "list": {}, - "cancel": {}, - "requests": { - "tools": {"call": {}}, - "prompts": {"get": {}}, - "resources": {"read": {}}, - }, - } - } + return ServerTasksCapability( + list=TasksListCapability(), + cancel=TasksCancelCapability(), + requests=ServerTasksRequestsCapability( + tools=TasksToolsCapability(call=TasksCallCapability()), + prompts={"get": {}}, # type: ignore[call-arg] # extra_data for forward compat + resources={"read": {}}, # type: ignore[call-arg] # extra_data for forward compat + ), + ) diff --git a/src/fastmcp/server/tasks/handlers.py b/src/fastmcp/server/tasks/handlers.py index 569330449..ecfa0c808 100644 --- a/src/fastmcp/server/tasks/handlers.py +++ b/src/fastmcp/server/tasks/handlers.py @@ -56,6 +56,7 @@ async def handle_tool_as_task( ctx = get_context() session_id = ctx.session_id + # Get Docket from ContextVar (set by Context.__aenter__ at request time) docket = _current_docket.get() if docket is None: raise McpError( @@ -72,13 +73,15 @@ async def handle_tool_as_task( tool = await server.get_tool(tool_name) # Store task key mapping and creation timestamp in Redis for protocol handlers - redis_key = f"fastmcp:task:{session_id}:{server_task_id}" - created_at_key = f"fastmcp:task:{session_id}:{server_task_id}:created_at" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{server_task_id}") + created_at_key = docket.key( + f"fastmcp:task:{session_id}:{server_task_id}:created_at" + ) ttl_seconds = int( docket.execution_ttl.total_seconds() + TASK_MAPPING_TTL_BUFFER_SECONDS ) async with docket.redis() as redis: - await redis.set(redis_key, task_key, ex=ttl_seconds) + await redis.set(task_meta_key, task_key, ex=ttl_seconds) await redis.set(created_at_key, created_at, ex=ttl_seconds) # Send notifications/tasks/created per SEP-1686 (mandatory) @@ -165,6 +168,7 @@ async def handle_prompt_as_task( ctx = get_context() session_id = ctx.session_id + # Get Docket from ContextVar (set by Context.__aenter__ at request time) docket = _current_docket.get() if docket is None: raise McpError( @@ -181,13 +185,15 @@ async def handle_prompt_as_task( prompt = await server.get_prompt(prompt_name) # Store task key mapping and creation timestamp in Redis for protocol handlers - redis_key = f"fastmcp:task:{session_id}:{server_task_id}" - created_at_key = f"fastmcp:task:{session_id}:{server_task_id}:created_at" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{server_task_id}") + created_at_key = docket.key( + f"fastmcp:task:{session_id}:{server_task_id}:created_at" + ) ttl_seconds = int( docket.execution_ttl.total_seconds() + TASK_MAPPING_TTL_BUFFER_SECONDS ) async with docket.redis() as redis: - await redis.set(redis_key, task_key, ex=ttl_seconds) + await redis.set(task_meta_key, task_key, ex=ttl_seconds) await redis.set(created_at_key, created_at, ex=ttl_seconds) # Send notifications/tasks/created per SEP-1686 (mandatory) @@ -272,12 +278,13 @@ async def handle_resource_as_task( ctx = get_context() session_id = ctx.session_id + # Get Docket from ContextVar (set by Context.__aenter__ at request time) docket = _current_docket.get() if docket is None: raise McpError( ErrorData( code=INTERNAL_ERROR, - message="Background tasks require Docket", + message="Background tasks require a running FastMCP server context", ) ) @@ -285,13 +292,15 @@ async def handle_resource_as_task( task_key = build_task_key(session_id, server_task_id, "resource", str(uri)) # Store task key mapping and creation timestamp in Redis for protocol handlers - redis_key = f"fastmcp:task:{session_id}:{server_task_id}" - created_at_key = f"fastmcp:task:{session_id}:{server_task_id}:created_at" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{server_task_id}") + created_at_key = docket.key( + f"fastmcp:task:{session_id}:{server_task_id}:created_at" + ) ttl_seconds = int( docket.execution_ttl.total_seconds() + TASK_MAPPING_TTL_BUFFER_SECONDS ) async with docket.redis() as redis: - await redis.set(redis_key, task_key, ex=ttl_seconds) + await redis.set(task_meta_key, task_key, ex=ttl_seconds) await redis.set(created_at_key, created_at, ex=ttl_seconds) # Send notifications/tasks/created per SEP-1686 (mandatory) diff --git a/src/fastmcp/server/tasks/protocol.py b/src/fastmcp/server/tasks/protocol.py index 902947862..8ada3749e 100644 --- a/src/fastmcp/server/tasks/protocol.py +++ b/src/fastmcp/server/tasks/protocol.py @@ -77,10 +77,12 @@ async def tasks_get_handler(server: FastMCP, params: dict[str, Any]) -> GetTaskR ) # Look up full task key and creation timestamp from Redis - redis_key = f"fastmcp:task:{session_id}:{client_task_id}" - created_at_key = f"fastmcp:task:{session_id}:{client_task_id}:created_at" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{client_task_id}") + created_at_key = docket.key( + f"fastmcp:task:{session_id}:{client_task_id}:created_at" + ) async with docket.redis() as redis: - task_key_bytes = await redis.get(redis_key) + task_key_bytes = await redis.get(task_meta_key) created_at_bytes = await redis.get(created_at_key) task_key = None if task_key_bytes is None else task_key_bytes.decode("utf-8") @@ -176,9 +178,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: ) # Look up full task key from Redis - redis_key = f"fastmcp:task:{session_id}:{client_task_id}" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{client_task_id}") async with docket.redis() as redis: - task_key_bytes = await redis.get(redis_key) + task_key_bytes = await redis.get(task_meta_key) task_key = None if task_key_bytes is None else task_key_bytes.decode("utf-8") @@ -309,10 +311,12 @@ async def tasks_cancel_handler( ) # Look up full task key and creation timestamp from Redis - redis_key = f"fastmcp:task:{session_id}:{client_task_id}" - created_at_key = f"fastmcp:task:{session_id}:{client_task_id}:created_at" + task_meta_key = docket.key(f"fastmcp:task:{session_id}:{client_task_id}") + created_at_key = docket.key( + f"fastmcp:task:{session_id}:{client_task_id}:created_at" + ) async with docket.redis() as redis: - task_key_bytes = await redis.get(redis_key) + task_key_bytes = await redis.get(task_meta_key) created_at_bytes = await redis.get(created_at_key) task_key = None if task_key_bytes is None else task_key_bytes.decode("utf-8") diff --git a/src/fastmcp/server/tasks/subscriptions.py b/src/fastmcp/server/tasks/subscriptions.py index dae18675b..c22e88f04 100644 --- a/src/fastmcp/server/tasks/subscriptions.py +++ b/src/fastmcp/server/tasks/subscriptions.py @@ -70,7 +70,7 @@ async def subscribe_to_task_updates( ) except Exception as e: - logger.warning(f"Subscription task failed for {task_id}: {e}", exc_info=True) + logger.error(f"subscribe_to_task_updates failed for {task_id}: {e}") async def _send_status_notification( @@ -101,8 +101,7 @@ async def _send_status_notification( key_parts = parse_task_key(task_key) session_id = key_parts["session_id"] - # Retrieve createdAt timestamp from Redis - created_at_key = f"fastmcp:task:{session_id}:{task_id}:created_at" + created_at_key = docket.key(f"fastmcp:task:{session_id}:{task_id}:created_at") async with docket.redis() as redis: created_at_bytes = await redis.get(created_at_key) @@ -175,8 +174,7 @@ async def _send_progress_notification( key_parts = parse_task_key(task_key) session_id = key_parts["session_id"] - # Retrieve createdAt timestamp from Redis - created_at_key = f"fastmcp:task:{session_id}:{task_id}:created_at" + created_at_key = docket.key(f"fastmcp:task:{session_id}:{task_id}:created_at") async with docket.redis() as redis: created_at_bytes = await redis.get(created_at_key) diff --git a/src/fastmcp/settings.py b/src/fastmcp/settings.py index fc1940321..898d580dc 100644 --- a/src/fastmcp/settings.py +++ b/src/fastmcp/settings.py @@ -392,6 +392,21 @@ class Settings(BaseSettings): ), ] = True + check_for_updates: Annotated[ + Literal["stable", "prerelease", "off"], + Field( + description=inspect.cleandoc( + """ + Controls update checking when displaying the CLI banner. + - "stable": Check for stable releases only (default) + - "prerelease": Also check for pre-release versions (alpha, beta, rc) + - "off": Disable update checking entirely + Set via FASTMCP_CHECK_FOR_UPDATES environment variable. + """ + ), + ), + ] = "stable" + @property def server_auth_class(self) -> AuthProvider | None: from fastmcp.utilities.types import get_cached_typeadapter diff --git a/src/fastmcp/tools/tool.py b/src/fastmcp/tools/tool.py index 191f4737a..d6e123631 100644 --- a/src/fastmcp/tools/tool.py +++ b/src/fastmcp/tools/tool.py @@ -32,7 +32,7 @@ import fastmcp from fastmcp.server.dependencies import get_context, without_injected_parameters from fastmcp.server.tasks.config import TaskConfig from fastmcp.utilities.components import FastMCPComponent -from fastmcp.utilities.json_schema import compress_schema +from fastmcp.utilities.json_schema import compress_schema, resolve_root_ref from fastmcp.utilities.logging import get_logger from fastmcp.utilities.types import ( Audio, @@ -323,7 +323,7 @@ class FunctionTool(Tool): warnings.warn( "The `exclude_args` parameter is deprecated as of FastMCP 2.14. " "Use dependency injection with `Depends()` instead for better lifecycle management. " - "See https://gofastmcp.com/servers/dependencies for examples.", + "See https://gofastmcp.com/v2/servers/context#using-depends for examples.", DeprecationWarning, stacklevel=2, ) @@ -497,7 +497,10 @@ class ParsedFunction: # Compress and handle exclude_args prune_params = list(exclude_args) if exclude_args else None input_schema = compress_schema( - input_schema, prune_params=prune_params, prune_titles=True + input_schema, + prune_params=prune_params, + prune_titles=True, + dereference=True, ) output_schema = None @@ -557,7 +560,13 @@ class ParsedFunction: else: output_schema = base_schema - output_schema = compress_schema(output_schema, prune_titles=True) + output_schema = compress_schema( + output_schema, prune_titles=True, dereference=True + ) + + # Resolve root-level $ref to meet MCP spec requirement for type: object + # Self-referential Pydantic models generate schemas with $ref at root + output_schema = resolve_root_ref(output_schema) except PydanticSchemaGenerationError as e: if "_UnserializableType" not in str(e): diff --git a/src/fastmcp/tools/tool_manager.py b/src/fastmcp/tools/tool_manager.py index 9c6ee8860..e1d1f5c31 100644 --- a/src/fastmcp/tools/tool_manager.py +++ b/src/fastmcp/tools/tool_manager.py @@ -8,7 +8,7 @@ from mcp.types import ToolAnnotations from pydantic import ValidationError from fastmcp import settings -from fastmcp.exceptions import NotFoundError, ToolError +from fastmcp.exceptions import FastMCPError, NotFoundError, ToolError from fastmcp.settings import DuplicateBehavior from fastmcp.tools.tool import Tool, ToolResult from fastmcp.tools.tool_transform import ( @@ -158,12 +158,10 @@ class ToolManager: tool = await self.get_tool(key) try: return await tool.run(arguments) - except ValidationError as e: - logger.exception(f"Error validating tool {key!r}: {e}") - raise e - except ToolError as e: - logger.exception(f"Error calling tool {key!r}") - raise e + except FastMCPError: + raise + except ValidationError: + raise except Exception as e: logger.exception(f"Error calling tool {key!r}") if self.mask_error_details: diff --git a/src/fastmcp/utilities/cli.py b/src/fastmcp/utilities/cli.py index 24a776616..13d496587 100644 --- a/src/fastmcp/utilities/cli.py +++ b/src/fastmcp/utilities/cli.py @@ -3,7 +3,7 @@ from __future__ import annotations import json import os from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any from pydantic import ValidationError from rich.align import Align @@ -17,6 +17,7 @@ from fastmcp.utilities.logging import get_logger from fastmcp.utilities.mcp_server_config import MCPServerConfig from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.types import get_cached_typeadapter +from fastmcp.utilities.version_check import check_for_newer_version if TYPE_CHECKING: from fastmcp import FastMCP @@ -197,23 +198,11 @@ LOGO_ASCII_4 = ( ) -def log_server_banner( - server: FastMCP[Any], - transport: Literal["stdio", "http", "sse", "streamable-http"], - *, - host: str | None = None, - port: int | None = None, - path: str | None = None, -) -> None: - """Creates and logs a formatted banner with server information and logo. +def log_server_banner(server: FastMCP[Any]) -> None: + """Creates and logs a formatted banner with server information and logo.""" - Args: - transport: The transport protocol being used - server_name: Optional server name to display - host: Host address (for HTTP transports) - port: Port number (for HTTP transports) - path: Server path (for HTTP transports) - """ + # Check for updates (non-blocking, fails silently) + newer_version = check_for_newer_version() # Create the logo text # Use Text with no_wrap and markup disabled to preserve ANSI escape codes @@ -228,39 +217,36 @@ def log_server_banner( info_table.add_column(style="cyan", justify="left") # Label column info_table.add_column(style="dim", justify="left") # Value column - match transport: - case "http" | "streamable-http": - display_transport = "HTTP" - case "sse": - display_transport = "SSE" - case "stdio": - display_transport = "STDIO" - - info_table.add_row("🖥", "Server name:", Text(server.name + "\n", style="bold blue")) - info_table.add_row("📦", "Transport:", display_transport) - - # Show connection info based on transport - if transport in ("http", "streamable-http", "sse") and host and port: - server_url = f"http://{host}:{port}" - if path: - server_url += f"/{path.lstrip('/')}" - info_table.add_row("🔗", "Server URL:", server_url) - - # Add documentation link - info_table.add_row("", "", "") - info_table.add_row("📚", "Docs:", "https://gofastmcp.com") - info_table.add_row("🚀", "Hosting:", "https://fastmcp.cloud") + info_table.add_row("🖥", "Server:", Text(server.name, style="dim")) + info_table.add_row("🚀", "Deploy free:", "https://fastmcp.cloud") # Create panel with logo, title, and information using Group + docs_url = Text("https://gofastmcp.com", style="dim") panel_content = Group( + "", Align.center(logo_text), "", - Align.center(title_text), "", + Align.center(title_text), + Align.center(docs_url), "", Align.center(info_table), ) + # v3 notice banner (shown below main panel) + v3_line1 = Text("✨ FastMCP 3.0 is coming!", style="bold") + v3_line2 = Text.assemble( + ("Pin ", "dim"), + ("`fastmcp < 3`", "dim bold"), + (" in production, then upgrade when you're ready.", "dim"), + ) + v3_notice = Panel( + Group(Align.center(v3_line1), Align.center(v3_line2)), + border_style="blue", + padding=(0, 2), + width=80, + ) + panel = Panel( panel_content, border_style="dim", @@ -270,5 +256,26 @@ def log_server_banner( ) console = Console(stderr=True) - # Center the panel itself - console.print(Group("\n", Align.center(panel), "\n")) + + # Build output elements + output_elements: list[Align | Panel | str] = ["\n", Align.center(panel)] + output_elements.append(Align.center(v3_notice)) + + # Add update notice if a newer version is available (shown last for visibility) + if newer_version: + update_line1 = Text.assemble( + ("🎉 Update available: ", "bold"), + (newer_version, "bold green"), + ) + update_line2 = Text("Run: pip install --upgrade fastmcp", style="dim") + update_notice = Panel( + Group(Align.center(update_line1), Align.center(update_line2)), + border_style="blue", + padding=(0, 2), + width=80, + ) + output_elements.append(Align.center(update_notice)) + + output_elements.append("\n") + + console.print(Group(*output_elements)) diff --git a/src/fastmcp/utilities/json_schema.py b/src/fastmcp/utilities/json_schema.py index b6364757f..9f23b141f 100644 --- a/src/fastmcp/utilities/json_schema.py +++ b/src/fastmcp/utilities/json_schema.py @@ -1,9 +1,196 @@ from __future__ import annotations from collections import defaultdict +from typing import Any + +from jsonref import JsonRefError, replace_refs -def _prune_param(schema: dict, param: str) -> dict: +def dereference_refs(schema: dict[str, Any]) -> dict[str, Any]: + """Resolve all $ref references in a JSON schema by inlining definitions. + + This function resolves $ref references that point to $defs, replacing them + with the actual definition content while preserving sibling keywords (like + description, default, examples) that Pydantic places alongside $ref. + + This is necessary because some MCP clients (e.g., VS Code Copilot) don't + properly handle $ref in tool input schemas. + + For self-referencing/circular schemas where full dereferencing is not possible, + this function falls back to resolving only the root-level $ref while preserving + $defs for nested references. + + Args: + schema: JSON schema dict that may contain $ref references + + Returns: + A new schema dict with $ref resolved where possible and $defs removed + when no longer needed + + Example: + >>> schema = { + ... "$defs": {"Category": {"enum": ["a", "b"], "type": "string"}}, + ... "properties": {"cat": {"$ref": "#/$defs/Category", "default": "a"}} + ... } + >>> resolved = dereference_refs(schema) + >>> # Result: {"properties": {"cat": {"enum": ["a", "b"], "type": "string", "default": "a"}}} + """ + try: + # Use jsonref to resolve all $ref references + # proxies=False returns plain dicts (not proxy objects) + # lazy_load=False resolves immediately + dereferenced = replace_refs(schema, proxies=False, lazy_load=False) + + # Merge sibling keywords that were lost during dereferencing + # Pydantic puts description, default, examples as siblings to $ref + defs = schema.get("$defs", {}) + merged = _merge_ref_siblings(schema, dereferenced, defs) + # Type assertion: top-level schema is always a dict + assert isinstance(merged, dict) + dereferenced = merged + + # Remove $defs since all references have been resolved + if "$defs" in dereferenced: + dereferenced = {k: v for k, v in dereferenced.items() if k != "$defs"} + + # Remove discriminator.mapping entries that referenced $defs + _strip_discriminator_mappings(dereferenced) + + return dereferenced + + except JsonRefError: + # Self-referencing/circular schemas can't be fully dereferenced + # Fall back to resolving only root-level $ref (for MCP spec compliance) + return resolve_root_ref(schema) + + +def _strip_discriminator_mappings(schema: Any, depth: int = 0) -> None: + """Remove discriminator.mapping entries whose values are $defs references. + + Pydantic emits discriminator.mapping with plain-string references like + ``"#/$defs/Cat"`` that become dangling after $defs are removed by + dereference_refs(). The oneOf/anyOf variants already carry their own + const fields, so the mapping is redundant once refs are inlined. + """ + if depth > 50 or not isinstance(schema, dict): + return + if "discriminator" in schema and isinstance(schema["discriminator"], dict): + schema["discriminator"].pop("mapping", None) + for value in schema.values(): + if isinstance(value, dict): + _strip_discriminator_mappings(value, depth + 1) + elif isinstance(value, list): + for item in value: + _strip_discriminator_mappings(item, depth + 1) + + +def _merge_ref_siblings( + original: Any, + dereferenced: Any, + defs: dict[str, Any], + visited: set[str] | None = None, +) -> Any: + """Merge sibling keywords from original $ref nodes into dereferenced schema. + + When jsonref resolves $ref, it replaces the entire node with the referenced + definition, losing any sibling keywords like description, default, or examples. + This function walks both trees in parallel and merges those siblings back. + + Args: + original: The original schema with $ref and potential siblings + dereferenced: The schema after jsonref processing + defs: The $defs from the original schema, for looking up referenced definitions + visited: Set of definition names already being processed (prevents cycles) + + Returns: + The dereferenced schema with sibling keywords restored + """ + if visited is None: + visited = set() + + if isinstance(original, dict) and isinstance(dereferenced, dict): + # Check if original had a $ref + if "$ref" in original: + ref = original["$ref"] + siblings = {k: v for k, v in original.items() if k not in ("$ref", "$defs")} + + # Look up the referenced definition to process its nested siblings + if isinstance(ref, str) and ref.startswith("#/$defs/"): + def_name = ref.split("/")[-1] + # Prevent infinite recursion on circular references + if def_name in defs and def_name not in visited: + # Recursively process the definition's content for nested siblings + dereferenced = _merge_ref_siblings( + defs[def_name], dereferenced, defs, visited | {def_name} + ) + + if siblings: + # Merge local siblings, which take precedence + merged = dict(dereferenced) + merged.update(siblings) + return merged + return dereferenced + + # Recurse into nested structures + result = {} + for key, value in dereferenced.items(): + if key in original: + result[key] = _merge_ref_siblings(original[key], value, defs, visited) + else: + result[key] = value + return result + + elif isinstance(original, list) and isinstance(dereferenced, list): + # Process list items in parallel + min_len = min(len(original), len(dereferenced)) + return [ + _merge_ref_siblings(o, d, defs, visited) + for o, d in zip(original[:min_len], dereferenced[:min_len], strict=False) + ] + dereferenced[min_len:] + + return dereferenced + + +def resolve_root_ref(schema: dict[str, Any]) -> dict[str, Any]: + """Resolve $ref at root level to meet MCP spec requirements. + + MCP specification requires outputSchema to have "type": "object" at the root level. + When Pydantic generates schemas for self-referential models, it uses $ref at the + root level pointing to $defs. This function resolves such references by inlining + the referenced definition while preserving $defs for nested references. + + Args: + schema: JSON schema dict that may have $ref at root level + + Returns: + A new schema dict with root-level $ref resolved, or the original schema + if no resolution is needed + + Example: + >>> schema = { + ... "$defs": {"Node": {"type": "object", "properties": {...}}}, + ... "$ref": "#/$defs/Node" + ... } + >>> resolved = resolve_root_ref(schema) + >>> # Result: {"type": "object", "properties": {...}, "$defs": {...}} + """ + # Only resolve if we have $ref at root level with $defs but no explicit type + if "$ref" in schema and "$defs" in schema and "type" not in schema: + ref = schema["$ref"] + # Only handle local $defs references + if isinstance(ref, str) and ref.startswith("#/$defs/"): + def_name = ref.split("/")[-1] + defs = schema["$defs"] + if def_name in defs: + # Create a new schema by copying the referenced definition + resolved = dict(defs[def_name]) + # Preserve $defs for nested references (other fields may still use them) + resolved["$defs"] = defs + return resolved + return schema + + +def _prune_param(schema: dict[str, Any], param: str) -> dict[str, Any]: """Return a new schema with *param* removed from `properties`, `required`, and (if no longer referenced) `$defs`. """ @@ -25,11 +212,11 @@ def _prune_param(schema: dict, param: str) -> dict: def _single_pass_optimize( - schema: dict, + schema: dict[str, Any], prune_titles: bool = False, prune_additional_properties: bool = False, prune_defs: bool = True, -) -> dict: +) -> dict[str, Any]: """ Optimize JSON schemas in a single traversal for better performance. @@ -198,12 +385,13 @@ def _single_pass_optimize( def compress_schema( - schema: dict, + schema: dict[str, Any], prune_params: list[str] | None = None, prune_defs: bool = True, prune_additional_properties: bool = True, prune_titles: bool = False, -) -> dict: + dereference: bool = False, +) -> dict[str, Any]: """ Remove the given parameters from the schema. @@ -213,6 +401,7 @@ def compress_schema( prune_defs: Whether to remove unused definitions prune_additional_properties: Whether to remove additionalProperties: false prune_titles: Whether to remove title fields from the schema + dereference: Whether to inline $ref references for client compatibility """ # Remove specific parameters if requested for param in prune_params or []: @@ -227,4 +416,8 @@ def compress_schema( prune_defs=prune_defs, ) + # Inline $ref references for MCP clients that don't handle them + if dereference: + schema = dereference_refs(schema) + return schema diff --git a/src/fastmcp/utilities/openapi/schemas.py b/src/fastmcp/utilities/openapi/schemas.py index 679fe2397..5142f12a5 100644 --- a/src/fastmcp/utilities/openapi/schemas.py +++ b/src/fastmcp/utilities/openapi/schemas.py @@ -539,9 +539,9 @@ def extract_output_schema_from_responses( # Replace $ref with the actual schema definition output_schema = _replace_ref_with_defs(schema_definitions[schema_name]) - # Convert OpenAPI schema to JSON Schema format - # Only needed for OpenAPI 3.0 - 3.1 uses standard JSON Schema null types - if openapi_version and openapi_version.startswith("3.0"): + if openapi_version and openapi_version.startswith("3"): + # Convert OpenAPI 3.x schema to JSON Schema format for proper handling + # of constructs like oneOf, anyOf, and nullable fields from .json_schema_converter import convert_openapi_schema_to_json_schema output_schema = convert_openapi_schema_to_json_schema( @@ -570,7 +570,7 @@ def extract_output_schema_from_responses( processed_defs[name] = _replace_ref_with_defs(schema) # Convert OpenAPI schema definitions to JSON Schema format if needed - if openapi_version and openapi_version.startswith("3.0"): + if openapi_version and openapi_version.startswith("3"): from .json_schema_converter import convert_openapi_schema_to_json_schema for def_name in list(processed_defs.keys()): diff --git a/src/fastmcp/utilities/version_check.py b/src/fastmcp/utilities/version_check.py new file mode 100644 index 000000000..5c3aba634 --- /dev/null +++ b/src/fastmcp/utilities/version_check.py @@ -0,0 +1,153 @@ +"""Version checking utilities for FastMCP.""" + +from __future__ import annotations + +import json +import time +from pathlib import Path + +import httpx +from packaging.version import Version + +from fastmcp.utilities.logging import get_logger + +logger = get_logger(__name__) + +PYPI_URL = "https://pypi.org/pypi/fastmcp/json" +CACHE_TTL_SECONDS = 60 * 60 * 12 # 12 hours +REQUEST_TIMEOUT_SECONDS = 2.0 + + +def _get_cache_path(include_prereleases: bool = False) -> Path: + """Get the path to the version cache file.""" + import fastmcp + + suffix = "_prerelease" if include_prereleases else "" + return fastmcp.settings.home / f"version_cache{suffix}.json" + + +def _read_cache(include_prereleases: bool = False) -> tuple[str | None, float]: + """Read cached version info. + + Returns: + Tuple of (cached_version, cache_timestamp) or (None, 0) if no cache. + """ + cache_path = _get_cache_path(include_prereleases) + if not cache_path.exists(): + return None, 0 + + try: + data = json.loads(cache_path.read_text()) + return data.get("latest_version"), data.get("timestamp", 0) + except (json.JSONDecodeError, OSError): + return None, 0 + + +def _write_cache(latest_version: str, include_prereleases: bool = False) -> None: + """Write version info to cache.""" + cache_path = _get_cache_path(include_prereleases) + try: + cache_path.parent.mkdir(parents=True, exist_ok=True) + cache_path.write_text( + json.dumps({"latest_version": latest_version, "timestamp": time.time()}) + ) + except OSError: + # Silently ignore cache write failures + pass + + +def _fetch_latest_version(include_prereleases: bool = False) -> str | None: + """Fetch the latest version from PyPI. + + Args: + include_prereleases: If True, include pre-release versions (alpha, beta, rc). + + Returns: + The latest version string, or None if the fetch failed. + """ + try: + response = httpx.get(PYPI_URL, timeout=REQUEST_TIMEOUT_SECONDS) + response.raise_for_status() + data = response.json() + + releases = data.get("releases", {}) + if not releases: + return None + + versions = [] + for version_str in releases: + try: + v = Version(version_str) + # Skip prereleases if not requested + if not include_prereleases and v.is_prerelease: + continue + versions.append(v) + except ValueError: + logger.debug(f"Skipping invalid version string: {version_str}") + continue + + if not versions: + return None + + return str(max(versions)) + + except (httpx.HTTPError, json.JSONDecodeError, KeyError): + return None + + +def get_latest_version(include_prereleases: bool = False) -> str | None: + """Get the latest version of FastMCP from PyPI, using cache when available. + + Args: + include_prereleases: If True, include pre-release versions. + + Returns: + The latest version string, or None if unavailable. + """ + # Check cache first + cached_version, cache_timestamp = _read_cache(include_prereleases) + if cached_version and (time.time() - cache_timestamp) < CACHE_TTL_SECONDS: + return cached_version + + # Fetch from PyPI + latest_version = _fetch_latest_version(include_prereleases) + + # Update cache if we got a valid version + if latest_version: + _write_cache(latest_version, include_prereleases) + return latest_version + + # Return stale cache if available + return cached_version + + +def check_for_newer_version() -> str | None: + """Check if a newer version of FastMCP is available. + + Returns: + The latest version string if newer than current, None otherwise. + """ + import fastmcp + + setting = fastmcp.settings.check_for_updates + if setting == "off": + return None + + include_prereleases = setting == "prerelease" + latest_version = get_latest_version(include_prereleases) + if not latest_version: + return None + + try: + current = Version(fastmcp.__version__) + latest = Version(latest_version) + + if latest > current: + return latest_version + except ValueError: + logger.debug( + f"Could not compare versions: current={fastmcp.__version__!r}, " + f"latest={latest_version!r}" + ) + + return None diff --git a/tests/client/auth/test_oauth_client.py b/tests/client/auth/test_oauth_client.py index 832d7ce02..cc6d6e373 100644 --- a/tests/client/auth/test_oauth_client.py +++ b/tests/client/auth/test_oauth_client.py @@ -171,3 +171,81 @@ class TestOAuthClientUrlHandling: # Token storage should key by the full URL, not just the host assert oauth.token_storage_adapter._server_url == mcp_url + + +class TestTokenStorageTTL: + """Tests for client token storage TTL behavior (issue #2670). + + The token storage TTL should NOT be based on access token expiry, because + the refresh token may be valid much longer. Using access token expiry would + cause both tokens to be deleted when the access token expires, preventing + refresh. + """ + + async def test_token_storage_uses_long_ttl(self): + """Token storage should use a long TTL, not access token expiry. + + This is the ianw case: IdP returns expires_in=300 (5 min access token) + but the refresh token is valid for much longer. The entire token entry + should NOT be deleted after 5 minutes. + """ + from key_value.aio.stores.memory import MemoryStore + from mcp.shared.auth import OAuthToken + + from fastmcp.client.auth.oauth import TokenStorageAdapter + + # Create storage adapter + storage = MemoryStore() + adapter = TokenStorageAdapter( + async_key_value=storage, server_url="https://test" + ) + + # Create a token with short access expiry (5 minutes) + token = OAuthToken( + access_token="test-access-token", + token_type="Bearer", + expires_in=300, # 5 minutes - but we should NOT use this as storage TTL! + refresh_token="test-refresh-token", + scope="read write", + ) + + # Store the token + await adapter.set_tokens(token) + + # Verify token is stored + stored = await adapter.get_tokens() + assert stored is not None + assert stored.access_token == "test-access-token" + assert stored.refresh_token == "test-refresh-token" + + # The key assertion: the TTL should be 1 year (365 days), not 300 seconds + # We verify this by checking the raw storage entry + raw = await storage.get(collection="mcp-oauth-token", key="https://test/tokens") + assert raw is not None + + async def test_token_storage_preserves_refresh_token(self): + """Refresh token should not be lost when access token would expire.""" + from key_value.aio.stores.memory import MemoryStore + from mcp.shared.auth import OAuthToken + + from fastmcp.client.auth.oauth import TokenStorageAdapter + + storage = MemoryStore() + adapter = TokenStorageAdapter( + async_key_value=storage, server_url="https://test" + ) + + # Store token with short access expiry + token = OAuthToken( + access_token="access", + token_type="Bearer", + expires_in=300, + refresh_token="refresh-token-should-survive", + scope="read", + ) + await adapter.set_tokens(token) + + # Retrieve and verify refresh token is present + stored = await adapter.get_tokens() + assert stored is not None + assert stored.refresh_token == "refresh-token-should-survive" diff --git a/tests/client/test_elicitation.py b/tests/client/test_elicitation.py index b762eef26..47e962cd7 100644 --- a/tests/client/test_elicitation.py +++ b/tests/client/test_elicitation.py @@ -742,6 +742,17 @@ async def test_dict_based_titled_single_select(): return "declined" async def elicitation_handler(message, response_type, params, ctx): + # Verify schema follows SEP-1330 pattern with type: "string" + schema = params.requestedSchema + assert schema["type"] == "object" + assert "value" in schema["properties"] + value_schema = schema["properties"]["value"] + assert value_schema["type"] == "string" + assert "oneOf" in value_schema + one_of = value_schema["oneOf"] + assert {"const": "low", "title": "Low Priority"} in one_of + assert {"const": "high", "title": "High Priority"} in one_of + return ElicitResult(action="accept", content={"value": "low"}) async with Client(mcp, elicitation_handler=elicitation_handler) as client: @@ -800,14 +811,15 @@ async def test_list_dict_multi_select_titled(): return "declined" async def elicitation_handler(message, response_type, params, ctx): - # Verify schema has array with anyOf pattern + # Verify schema has array with SEP-1330 compliant items (anyOf pattern) schema = params.requestedSchema assert schema["type"] == "object" assert "value" in schema["properties"] value_schema = schema["properties"]["value"] assert value_schema["type"] == "array" - assert "anyOf" in value_schema["items"] - any_of = value_schema["items"]["anyOf"] + items_schema = value_schema["items"] + assert "anyOf" in items_schema + any_of = items_schema["anyOf"] assert {"const": "low", "title": "Low Priority"} in any_of assert {"const": "high", "title": "High Priority"} in any_of @@ -895,7 +907,7 @@ async def test_validation_allows_enum_arrays(): async def test_validation_allows_enum_arrays_with_anyof(): - """Test validation accepts arrays with anyOf enum pattern.""" + """Test validation accepts arrays with anyOf enum pattern (SEP-1330 compliant).""" schema = { "type": "object", "properties": { diff --git a/tests/client/test_sse.py b/tests/client/test_sse.py index b0c9199b2..91530cf0d 100644 --- a/tests/client/test_sse.py +++ b/tests/client/test_sse.py @@ -117,17 +117,16 @@ async def nested_sse_server(): ws="websockets-sansio", ) - server_task = asyncio.create_task(uvicorn.Server(config).serve()) + uvicorn_server = uvicorn.Server(config) + server_task = asyncio.create_task(uvicorn_server.serve()) await asyncio.sleep(0.1) try: yield f"http://127.0.0.1:{port}/nest-outer/nest-inner/mcp/sse/" finally: - server_task.cancel() - try: - await server_task - except asyncio.CancelledError: - pass + # Graceful shutdown - required for uvicorn 0.39+ due to context isolation + uvicorn_server.should_exit = True + await server_task async def test_run_server_on_path(sse_server_custom_path: str): diff --git a/tests/client/test_streamable_http.py b/tests/client/test_streamable_http.py index 5c1753d9a..d64844b6a 100644 --- a/tests/client/test_streamable_http.py +++ b/tests/client/test_streamable_http.py @@ -1,6 +1,7 @@ import asyncio import json import sys +from contextlib import suppress from unittest.mock import AsyncMock, call import pytest @@ -106,8 +107,8 @@ async def nested_server(): from fastmcp.utilities.http import find_available_port - server = create_test_server() - mcp_app = server.http_app(path="/final/mcp") + mcp_server = create_test_server() + mcp_app = mcp_server.http_app(path="/final/mcp") # Nest the app under multiple mounts to test URL resolution inner = Starlette(routes=[Mount("/nest-inner", app=mcp_app)]) @@ -124,20 +125,19 @@ async def nested_server(): port=port, log_level="critical", ws="websockets-sansio", + timeout_graceful_shutdown=0, ) - # Use the simple asyncio pattern - server_task = asyncio.create_task(uvicorn.Server(config).serve()) + uvicorn_server = uvicorn.Server(config) + server_task = asyncio.create_task(uvicorn_server.serve()) await asyncio.sleep(0.1) yield f"http://127.0.0.1:{port}/nest-outer/nest-inner/final/mcp" - # Cleanup - server_task.cancel() - try: - await server_task - except asyncio.CancelledError: - pass + # Cleanup: signal uvicorn to shutdown, then cancel the task + uvicorn_server.should_exit = True + with suppress(asyncio.CancelledError, asyncio.TimeoutError): + await asyncio.wait_for(server_task, timeout=2.0) async def test_ping(streamable_http_server: str): diff --git a/tests/client/transports/test_uv_transport.py b/tests/client/transports/test_uv_transport.py index 020f084ba..2917045b8 100644 --- a/tests/client/transports/test_uv_transport.py +++ b/tests/client/transports/test_uv_transport.py @@ -5,10 +5,15 @@ from pathlib import Path import pytest +import fastmcp from fastmcp.client import Client from fastmcp.client.client import CallToolResult -from fastmcp.client.transports import ( - UvStdioTransport, +from fastmcp.client.transports import StdioTransport, UvStdioTransport + +# Detect if running from dev install to use local source instead of PyPI +_is_dev_install = "dev" in fastmcp.__version__ +_fastmcp_src_dir = ( + Path(__file__).parent.parent.parent.parent if _is_dev_install else None ) @@ -79,15 +84,32 @@ async def test_uv_transport_module(): main_file = module_dir / "__main__.py" _ = main_file.write_text(main_script) - client: Client[UvStdioTransport] = Client( - transport=UvStdioTransport( + # In dev installs, use --with-editable to install local source. + # In releases, use --with to install from PyPI. + if _is_dev_install and _fastmcp_src_dir: + transport: StdioTransport = StdioTransport( + command="uv", + args=[ + "run", + "--directory", + tmpdir, + "--with-editable", + str(_fastmcp_src_dir), + "--module", + "my_module", + ], + keep_alive=False, + ) + else: + transport = UvStdioTransport( with_packages=["fastmcp"], command="my_module", module=True, project_directory=Path(tmpdir), keep_alive=False, ) - ) + + client: Client[StdioTransport] = Client(transport=transport) async with client: result: CallToolResult = await client.call_tool("add", {"x": 1, "y": 2}) diff --git a/tests/integration_tests/test_timeout_fix.py b/tests/integration_tests/test_timeout_fix.py new file mode 100644 index 000000000..27776c1ba --- /dev/null +++ b/tests/integration_tests/test_timeout_fix.py @@ -0,0 +1,51 @@ +"""Test that verifies the timeout fix for issue #2842 and #2845.""" + +import asyncio + +import pytest + +from fastmcp import FastMCP +from fastmcp.client import Client +from fastmcp.client.transports import StreamableHttpTransport +from fastmcp.utilities.tests import run_server_async + + +def create_test_server() -> FastMCP: + """Create a FastMCP server with a slow tool.""" + server = FastMCP("TestServer") + + @server.tool + async def slow_tool(duration: int = 6) -> str: + """A tool that takes some time to complete.""" + await asyncio.sleep(duration) + return f"Completed in {duration} seconds" + + return server + + +@pytest.fixture +async def streamable_http_server(): + """Start a test server and return its URL.""" + server = create_test_server() + async with run_server_async(server) as url: + yield url + + +@pytest.mark.integration +@pytest.mark.timeout(15) +async def test_slow_tool_with_http_transport(streamable_http_server: str): + """Test that tools taking >5 seconds work correctly with HTTP transport. + + This test verifies the fix for: + - Issue #2842: Client can't get result after upgrading to 2.14.2 + - Issue #2845: Server doesn't return results when tool takes >5 seconds + + The root cause was that the httpx client was created without explicit + timeout configuration, defaulting to httpx's 5-second timeout. + """ + async with Client( + transport=StreamableHttpTransport(streamable_http_server) + ) as client: + # This should NOT timeout since we fixed the default timeout + result = await client.call_tool("slow_tool", {"duration": 6}) + assert result.data == "Completed in 6 seconds" diff --git a/tests/server/auth/providers/test_supabase.py b/tests/server/auth/providers/test_supabase.py index 7cdc08130..5944fcfbb 100644 --- a/tests/server/auth/providers/test_supabase.py +++ b/tests/server/auth/providers/test_supabase.py @@ -40,12 +40,14 @@ class TestSupabaseProvider: { "FASTMCP_SERVER_AUTH_SUPABASE_PROJECT_URL": "https://env123.supabase.co", "FASTMCP_SERVER_AUTH_SUPABASE_BASE_URL": "https://envserver.com", + "FASTMCP_SERVER_AUTH_SUPABASE_AUTH_ROUTE": "/custom/auth/route", }, ): provider = SupabaseProvider() assert provider.project_url == "https://env123.supabase.co" assert str(provider.base_url) == "https://envserver.com/" + assert provider.auth_route == "custom/auth/route" def test_environment_variable_loading(self): """Test that environment variables are loaded correctly.""" @@ -150,6 +152,28 @@ class TestSupabaseProvider: assert provider.token_verifier.algorithm == "RS256" # type: ignore[attr-defined] + def test_custom_auth_route(self): + provider = SupabaseProvider( + project_url="https://abc123.supabase.co", + base_url="https://myserver.com", + auth_route="/custom/auth/route", + ) + + assert provider.auth_route == "custom/auth/route" + assert ( + provider.token_verifier.jwks_uri + == "https://abc123.supabase.co/custom/auth/route/.well-known/jwks.json" + ) # type: ignore[attr-defined] + + def test_custom_auth_route_trailing_slash(self): + provider = SupabaseProvider( + project_url="https://abc123.supabase.co", + base_url="https://myserver.com", + auth_route="/custom/auth/route/", + ) + + assert provider.auth_route == "custom/auth/route" + def run_mcp_server(host: str, port: int) -> None: mcp = FastMCP( diff --git a/tests/server/auth/test_enhanced_error_responses.py b/tests/server/auth/test_enhanced_error_responses.py index cec425ecc..910e957f5 100644 --- a/tests/server/auth/test_enhanced_error_responses.py +++ b/tests/server/auth/test_enhanced_error_responses.py @@ -8,6 +8,7 @@ This test suite covers: """ import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.shared.auth import OAuthClientInformationFull from pydantic import AnyUrl from starlette.applications import Starlette @@ -42,6 +43,7 @@ class TestEnhancedAuthorizationHandler: ), base_url="https://myserver.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) def test_unregistered_client_returns_html_for_browser(self, oauth_proxy): @@ -303,6 +305,7 @@ class TestContentNegotiation: ), base_url="https://myserver.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) def test_html_preferred_when_both_accepted(self, oauth_proxy): diff --git a/tests/server/auth/test_oauth_mounting.py b/tests/server/auth/test_oauth_mounting.py index 1729c5979..93f4a2308 100644 --- a/tests/server/auth/test_oauth_mounting.py +++ b/tests/server/auth/test_oauth_mounting.py @@ -8,6 +8,7 @@ The fix uses MCP SDK 1.17+ which implements RFC 9728 path-scoped well-known URLs import httpx import pytest +from key_value.aio.stores.memory import MemoryStore from pydantic import AnyHttpUrl from starlette.applications import Starlette from starlette.routing import Mount @@ -220,6 +221,7 @@ class TestOAuthMounting: token_verifier=token_verifier, base_url="https://api.example.com/api", # Includes mount prefix issuer_url="https://api.example.com", # Root level + client_storage=MemoryStore(), ) mcp = FastMCP("test-server", auth=auth_provider) @@ -290,6 +292,7 @@ class TestOAuthMounting: upstream_client_secret="test-client-secret", token_verifier=token_verifier, base_url="https://api.example.com/api", # Has path, no explicit issuer_url + client_storage=MemoryStore(), ) mcp = FastMCP("test-server", auth=auth_provider) @@ -366,6 +369,7 @@ class TestOAuthMounting: token_verifier=token_verifier, base_url="https://api.example.com/api", issuer_url="https://api.example.com", # Explicitly root + client_storage=MemoryStore(), ) well_known_routes = auth_provider.get_well_known_routes(mcp_path="/mcp") diff --git a/tests/server/auth/test_oauth_proxy.py b/tests/server/auth/test_oauth_proxy.py index 8c214ca20..22d5a7c5b 100644 --- a/tests/server/auth/test_oauth_proxy.py +++ b/tests/server/auth/test_oauth_proxy.py @@ -641,6 +641,9 @@ class TestOAuthProxyTokenEndpointAuth: jwt_signing_key="test-secret", ) + # Initialize JWT issuer before token operations + proxy.set_mcp_path("/mcp") + # First, create a valid FastMCP token via full OAuth flow client = OAuthClientInformationFull( client_id="test-client", @@ -816,6 +819,9 @@ class TestOAuthProxyE2E: jwt_signing_key="test-secret", ) + # Initialize JWT issuer before token operations + proxy.set_mcp_path("/mcp") + client = OAuthClientInformationFull( client_id="test-client", client_secret="test-secret", @@ -1468,3 +1474,392 @@ class TestFallbackAccessTokenExpiry: ) assert provider._fallback_access_token_expiry_seconds is None + + +class TestResourceURLValidation: + """Tests for OAuth Proxy resource URL validation (GHSA-5h2m-4q8j-pqpj fix).""" + + @pytest.fixture + def proxy_with_resource_url(self, jwt_verifier): + """Create an OAuthProxy with set_mcp_path called.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://oauth.example.com/authorize", + upstream_token_endpoint="https://oauth.example.com/token", + upstream_client_id="upstream-client", + upstream_client_secret="upstream-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret", + ) + # Use non-default path to prove fix isn't relying on old hardcoded /mcp + proxy.set_mcp_path("/api/v2/mcp") + return proxy + + async def test_authorize_rejects_mismatched_resource(self, proxy_with_resource_url): + """Test that authorization rejects requests with mismatched resource.""" + from mcp.server.auth.provider import AuthorizeError + + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + + await proxy_with_resource_url.register_client(client) + + # Client requests a different resource than the server's + params = AuthorizationParams( + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + state="client-state", + code_challenge="challenge", + scopes=["read"], + resource="https://malicious-server.com/mcp", # Wrong resource + ) + + with pytest.raises(AuthorizeError) as exc_info: + await proxy_with_resource_url.authorize(client, params) + + assert exc_info.value.error == "invalid_target" + assert "Resource does not match" in exc_info.value.error_description + + async def test_authorize_accepts_matching_resource(self, proxy_with_resource_url): + """Test that authorization accepts requests with matching resource.""" + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + + await proxy_with_resource_url.register_client(client) + + # Client requests the correct resource (must match /api/v2/mcp path) + params = AuthorizationParams( + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + state="client-state", + code_challenge="challenge", + scopes=["read"], + resource="https://proxy.example.com/api/v2/mcp", # Correct resource + ) + + # Should succeed (redirect to consent page) + redirect_url = await proxy_with_resource_url.authorize(client, params) + assert "/consent" in redirect_url + + async def test_authorize_rejects_old_hardcoded_mcp_path( + self, proxy_with_resource_url + ): + """Test that old hardcoded /mcp path is rejected when server uses different path.""" + from mcp.server.auth.provider import AuthorizeError + + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + + await proxy_with_resource_url.register_client(client) + + # Client requests the old hardcoded /mcp path (would have worked before fix) + params = AuthorizationParams( + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + state="client-state", + code_challenge="challenge", + scopes=["read"], + resource="https://proxy.example.com/mcp", # Old hardcoded path + ) + + # Should fail because server is at /api/v2/mcp, not /mcp + with pytest.raises(AuthorizeError) as exc_info: + await proxy_with_resource_url.authorize(client, params) + + assert exc_info.value.error == "invalid_target" + + async def test_authorize_accepts_no_resource(self, proxy_with_resource_url): + """Test that authorization accepts requests without resource parameter.""" + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + + await proxy_with_resource_url.register_client(client) + + # Client doesn't specify resource + params = AuthorizationParams( + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + state="client-state", + code_challenge="challenge", + scopes=["read"], + # No resource parameter + ) + + # Should succeed (no resource check needed) + redirect_url = await proxy_with_resource_url.authorize(client, params) + assert "/consent" in redirect_url + + def test_set_mcp_path_creates_jwt_issuer_with_correct_audience(self, jwt_verifier): + """Test that set_mcp_path creates JWTIssuer with correct audience.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://oauth.example.com/authorize", + upstream_token_endpoint="https://oauth.example.com/token", + upstream_client_id="upstream-client", + upstream_client_secret="upstream-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret", + ) + + # Before set_mcp_path, _jwt_issuer is None + assert proxy._jwt_issuer is None + + # Call set_mcp_path with custom path + proxy.set_mcp_path("/custom/mcp") + + # After set_mcp_path, _jwt_issuer should be created + assert proxy._jwt_issuer is not None + assert proxy.jwt_issuer.audience == "https://proxy.example.com/custom/mcp" + assert proxy.jwt_issuer.issuer == "https://proxy.example.com/" + + def test_set_mcp_path_uses_base_url_if_no_path(self, jwt_verifier): + """Test that set_mcp_path uses base_url as audience if no path provided.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://oauth.example.com/authorize", + upstream_token_endpoint="https://oauth.example.com/token", + upstream_client_id="upstream-client", + upstream_client_secret="upstream-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret", + ) + + proxy.set_mcp_path(None) + + assert proxy.jwt_issuer.audience == "https://proxy.example.com/" + + def test_jwt_issuer_property_raises_if_not_initialized(self, jwt_verifier): + """Test that jwt_issuer property raises if set_mcp_path not called.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://oauth.example.com/authorize", + upstream_token_endpoint="https://oauth.example.com/token", + upstream_client_id="upstream-client", + upstream_client_secret="upstream-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret", + ) + + with pytest.raises(RuntimeError) as exc_info: + _ = proxy.jwt_issuer + + assert "JWT issuer not initialized" in str(exc_info.value) + + def test_get_routes_calls_set_mcp_path(self, jwt_verifier): + """Test that get_routes() calls set_mcp_path() to initialize JWT issuer.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://oauth.example.com/authorize", + upstream_token_endpoint="https://oauth.example.com/token", + upstream_client_id="upstream-client", + upstream_client_secret="upstream-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret", + ) + + # Before get_routes, _jwt_issuer is None + assert proxy._jwt_issuer is None + + # get_routes should call set_mcp_path internally + proxy.get_routes("/api/mcp") + + # After get_routes, _jwt_issuer should be created with correct audience + assert proxy._jwt_issuer is not None + assert proxy.jwt_issuer.audience == "https://proxy.example.com/api/mcp" + + +class TestUpstreamTokenStorageTTL: + """Tests for upstream token storage TTL calculation (issue #2670). + + The TTL should use max(refresh_expires_in, expires_in) to handle cases where + the refresh token has a shorter lifetime than the access token (e.g., Keycloak + with sliding session windows). + """ + + @pytest.fixture + def jwt_verifier(self): + """Create a mock JWT verifier.""" + verifier = Mock(spec=TokenVerifier) + verifier.required_scopes = ["read", "write"] + verifier.verify_token = AsyncMock(return_value=None) + return verifier + + @pytest.fixture + def proxy(self, jwt_verifier): + """Create an OAuth proxy for testing.""" + proxy = OAuthProxy( + upstream_authorization_endpoint="https://idp.example.com/authorize", + upstream_token_endpoint="https://idp.example.com/token", + upstream_client_id="test-client", + upstream_client_secret="test-secret", + token_verifier=jwt_verifier, + base_url="https://proxy.example.com", + jwt_signing_key="test-secret-key", + ) + proxy.set_mcp_path("/mcp") + return proxy + + async def test_ttl_uses_max_when_refresh_shorter_than_access(self, proxy): + """TTL should use access token expiry when refresh is shorter. + + This is the xsreality case: Keycloak returns refresh_expires_in=120 (2 min) + but expires_in=28800 (8 hours). The upstream tokens should persist for + 8 hours (the access token lifetime), not 2 minutes. + """ + from fastmcp.server.auth.oauth_proxy import ClientCode + + # Register client + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + await proxy.register_client(client) + + # Simulate xsreality's Keycloak setup: short refresh, long access + client_code = ClientCode( + code="test-auth-code", + client_id="test-client", + redirect_uri="http://localhost:12345/callback", + code_challenge="test-challenge", + code_challenge_method="S256", + scopes=["read", "write"], + idp_tokens={ + "access_token": "upstream-access-token", + "refresh_token": "upstream-refresh-token", + "expires_in": 28800, # 8 hours (access token) + "refresh_expires_in": 120, # 2 minutes (refresh token) - SHORTER! + "token_type": "Bearer", + }, + expires_at=time.time() + 300, + created_at=time.time(), + ) + await proxy._code_store.put(key=client_code.code, value=client_code) + + # Exchange the code + from mcp.server.auth.provider import AuthorizationCode + + auth_code = AuthorizationCode( + code="test-auth-code", + scopes=["read", "write"], + expires_at=time.time() + 300, + client_id="test-client", + code_challenge="test-challenge", + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + ) + + result = await proxy.exchange_authorization_code( + client=client, + authorization_code=auth_code, + ) + + # Verify tokens were issued + assert result.access_token is not None + assert result.refresh_token is not None + + # The key test: verify upstream tokens are stored with TTL=max(120, 28800)=28800 + # We can verify this by checking the tokens are still accessible after 2 minutes + # would have passed (if TTL was incorrectly set to 120) + # + # Since we can't easily time-travel in tests, we verify the storage directly + # by checking that we can still look up the tokens for refresh purposes. + # + # Extract the JTI from the refresh token to look up the mapping + refresh_payload = proxy.jwt_issuer.verify_token(result.refresh_token) + refresh_jti = refresh_payload["jti"] + + # The JTI mapping should exist + jti_mapping = await proxy._jti_mapping_store.get(key=refresh_jti) + assert jti_mapping is not None + + # The upstream tokens should exist + upstream_tokens = await proxy._upstream_token_store.get( + key=jti_mapping.upstream_token_id + ) + assert upstream_tokens is not None + assert upstream_tokens.access_token == "upstream-access-token" + assert upstream_tokens.refresh_token == "upstream-refresh-token" + + async def test_ttl_uses_refresh_when_refresh_longer_than_access(self, proxy): + """TTL should use refresh token expiry when refresh is longer. + + This is the ianw case: IdP returns expires_in=300 (5 min) but + refresh_expires_in=32318 (9 hours). The upstream tokens should persist + for 9 hours (the refresh token lifetime). + """ + from fastmcp.server.auth.oauth_proxy import ClientCode + + # Register client + client = OAuthClientInformationFull( + client_id="test-client", + client_secret="test-secret", + redirect_uris=[AnyUrl("http://localhost:12345/callback")], + ) + await proxy.register_client(client) + + # Simulate ianw's setup: short access, long refresh (typical) + client_code = ClientCode( + code="test-auth-code-2", + client_id="test-client", + redirect_uri="http://localhost:12345/callback", + code_challenge="test-challenge", + code_challenge_method="S256", + scopes=["read", "write"], + idp_tokens={ + "access_token": "upstream-access-token-2", + "refresh_token": "upstream-refresh-token-2", + "expires_in": 300, # 5 minutes (access token) + "refresh_expires_in": 32318, # 9 hours (refresh token) - LONGER + "token_type": "Bearer", + }, + expires_at=time.time() + 300, + created_at=time.time(), + ) + await proxy._code_store.put(key=client_code.code, value=client_code) + + # Exchange the code + from mcp.server.auth.provider import AuthorizationCode + + auth_code = AuthorizationCode( + code="test-auth-code-2", + scopes=["read", "write"], + expires_at=time.time() + 300, + client_id="test-client", + code_challenge="test-challenge", + redirect_uri=AnyUrl("http://localhost:12345/callback"), + redirect_uri_provided_explicitly=True, + ) + + result = await proxy.exchange_authorization_code( + client=client, + authorization_code=auth_code, + ) + + # Verify tokens were issued + assert result.access_token is not None + assert result.refresh_token is not None + + # Verify upstream tokens are accessible + refresh_payload = proxy.jwt_issuer.verify_token(result.refresh_token) + refresh_jti = refresh_payload["jti"] + + jti_mapping = await proxy._jti_mapping_store.get(key=refresh_jti) + assert jti_mapping is not None + + upstream_tokens = await proxy._upstream_token_store.get( + key=jti_mapping.upstream_token_id + ) + assert upstream_tokens is not None diff --git a/tests/server/auth/test_oauth_proxy_redirect_validation.py b/tests/server/auth/test_oauth_proxy_redirect_validation.py index 4e00648a0..51cb69543 100644 --- a/tests/server/auth/test_oauth_proxy_redirect_validation.py +++ b/tests/server/auth/test_oauth_proxy_redirect_validation.py @@ -1,6 +1,7 @@ """Tests for OAuth proxy redirect URI validation.""" import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.shared.auth import InvalidRedirectUriError from pydantic import AnyUrl @@ -111,6 +112,7 @@ class TestOAuthProxyRedirectValidation: token_verifier=MockTokenVerifier(), base_url="http://localhost:8000", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # The proxy should store None for default (allow all) @@ -129,6 +131,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._allowed_client_redirect_uris == custom_patterns @@ -144,6 +147,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=[], jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._allowed_client_redirect_uris == [] @@ -161,6 +165,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Register a client @@ -194,6 +199,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Get an unregistered client diff --git a/tests/server/middleware/test_error_handling.py b/tests/server/middleware/test_error_handling.py index 1ed534709..7da8c3119 100644 --- a/tests/server/middleware/test_error_handling.py +++ b/tests/server/middleware/test_error_handling.py @@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, MagicMock import pytest from mcp import McpError -from fastmcp.exceptions import NotFoundError +from fastmcp.exceptions import NotFoundError, ToolError from fastmcp.server.middleware.error_handling import ( ErrorHandlingMiddleware, RetryMiddleware, @@ -215,6 +215,23 @@ class TestErrorHandlingMiddleware: assert "Invalid params: test error" in exc_info.value.error.message assert "Error in test_method: ValueError: test error" in caplog.text + async def test_on_message_error_transform_tool_error(self, mock_context, caplog): + """Test error handling with transformation and cause type.""" + middleware = ErrorHandlingMiddleware() + tool_error = ToolError("test error") + tool_error.__cause__ = ValueError() + mock_call_next = AsyncMock(side_effect=tool_error) + + with caplog_for_fastmcp(caplog): + with caplog.at_level(logging.ERROR): + with pytest.raises(McpError) as exc_info: + await middleware.on_message(mock_context, mock_call_next) + + assert isinstance(exc_info.value, McpError) + assert exc_info.value.error.code == -32602 + assert "Invalid params: test error" in exc_info.value.error.message + assert "Error in test_method: ToolError: test error" in caplog.text + def test_get_error_stats(self, mock_context): """Test getting error statistics.""" middleware = ErrorHandlingMiddleware() diff --git a/tests/server/middleware/test_tool_injection.py b/tests/server/middleware/test_tool_injection.py index 3e0599414..09906e2eb 100644 --- a/tests/server/middleware/test_tool_injection.py +++ b/tests/server/middleware/test_tool_injection.py @@ -477,6 +477,8 @@ class TestResourceToolMiddleware: async def test_read_resource_tool_works(self, server_with_resources: FastMCP): """Test that the read_resource tool can be called.""" + import json + middleware = ResourceToolMiddleware() server_with_resources.add_middleware(middleware) @@ -485,14 +487,18 @@ class TestResourceToolMiddleware: name="read_resource", arguments={"uri": "file://config.txt"} ) - assert result.content == snapshot( - [ - TextContent( - type="text", - text='[{"content":"debug=true","mime_type":"text/plain"}]', - ) - ] - ) - assert result.structured_content == snapshot( - {"result": [{"content": "debug=true", "mime_type": "text/plain"}]} - ) + # Parse the JSON text content to check the actual values + # (MCP 1.26+ includes "meta":null in serialization, earlier versions don't) + assert len(result.content) == 1 + assert result.content[0].type == "text" + parsed = json.loads(result.content[0].text) # type: ignore[attr-defined] + assert len(parsed) == 1 + assert parsed[0]["content"] == "debug=true" + assert parsed[0]["mime_type"] == "text/plain" + + # Check structured content similarly + assert result.structured_content is not None + assert "result" in result.structured_content + assert len(result.structured_content["result"]) == 1 + assert result.structured_content["result"][0]["content"] == "debug=true" + assert result.structured_content["result"][0]["mime_type"] == "text/plain" diff --git a/tests/server/openapi/test_comprehensive.py b/tests/server/openapi/test_comprehensive.py index e7602c06c..777cd54c8 100644 --- a/tests/server/openapi/test_comprehensive.py +++ b/tests/server/openapi/test_comprehensive.py @@ -498,6 +498,38 @@ class TestOpenAPIComprehensive: assert "123" in str(request.url) assert "users/123" in str(request.url) + async def test_request_uses_localhost_fallback_when_no_base_url( + self, comprehensive_openapi_spec + ): + """Test that tool uses localhost fallback when client has no base_url.""" + mock_client = Mock(spec=httpx.AsyncClient) + mock_client.base_url = httpx.URL("") # Empty URL, same as httpx default + mock_client.headers = None + + mock_response = Mock(spec=Response) + mock_response.status_code = 200 + mock_response.json.return_value = { + "id": 123, + "name": "Test User", + "email": "test@example.com", + } + mock_response.raise_for_status = Mock() + + mock_client.send = AsyncMock(return_value=mock_response) + + server = FastMCPOpenAPI( + openapi_spec=comprehensive_openapi_spec, + client=mock_client, + ) + + async with Client(server) as mcp_client: + await mcp_client.call_tool("get_user", {"id": 123}) + + # Verify request was made to localhost fallback + mock_client.send.assert_called_once() + request = mock_client.send.call_args[0][0] + assert str(request.url).startswith("http://localhost") + async def test_complex_request_with_body_and_parameters( self, comprehensive_openapi_spec ): diff --git a/tests/server/tasks/test_server_tasks_parameter.py b/tests/server/tasks/test_server_tasks_parameter.py index c46e42ada..073de120a 100644 --- a/tests/server/tasks/test_server_tasks_parameter.py +++ b/tests/server/tasks/test_server_tasks_parameter.py @@ -308,3 +308,29 @@ async def test_multiple_components_same_name_different_tasks(): # Prompt inheriting False (mode="forbidden") raises McpError with pytest.raises(McpError): await client.get_prompt("shared_name_prompt", task=True) + + +async def test_task_with_custom_tool_name(): + """Tools with custom names work correctly as tasks (issue #2642). + + When a tool is registered with a custom name different from the function + name, task execution should use the custom name for Docket lookup. + """ + mcp = FastMCP("test", tasks=True) + + async def my_function() -> str: + return "result from custom-named tool" + + mcp.tool(my_function, name="custom-tool-name") + + async with Client(mcp) as client: + # Verify the tool is registered with its custom name in Docket + docket = mcp.docket + assert docket is not None + assert "custom-tool-name" in docket.tasks + + # Call the tool as a task using its custom name + task = await client.call_tool("custom-tool-name", task=True) + assert not task.returned_immediately + result = await task + assert result.data == "result from custom-named tool" diff --git a/tests/server/tasks/test_task_capabilities.py b/tests/server/tasks/test_task_capabilities.py index 2bafcb707..146d80d41 100644 --- a/tests/server/tasks/test_task_capabilities.py +++ b/tests/server/tasks/test_task_capabilities.py @@ -11,7 +11,7 @@ from fastmcp.server.tasks import get_task_capabilities async def test_capabilities_include_tasks(): - """Server capabilities always include tasks.""" + """Server capabilities always include tasks in first-class field (SEP-1686).""" mcp = FastMCP("capability-test") @mcp.tool() @@ -22,11 +22,11 @@ async def test_capabilities_include_tasks(): # Get server initialization result which includes capabilities init_result = client.initialize_result - # Verify tasks capability is present - assert init_result.capabilities.experimental is not None - assert "tasks" in init_result.capabilities.experimental - tasks_cap = init_result.capabilities.experimental["tasks"] - assert tasks_cap == get_task_capabilities()["tasks"] + # Verify tasks capability is present as a first-class field (not experimental) + assert init_result.capabilities.tasks is not None + assert init_result.capabilities.tasks == get_task_capabilities() + # Verify it's NOT in experimental + assert "tasks" not in (init_result.capabilities.experimental or {}) async def test_client_uses_task_capable_session(): diff --git a/tests/server/tasks/test_task_methods.py b/tests/server/tasks/test_task_methods.py index ba8b6e64d..ee971b2e7 100644 --- a/tests/server/tasks/test_task_methods.py +++ b/tests/server/tasks/test_task_methods.py @@ -172,3 +172,46 @@ async def test_task_cancellation_workflow(endpoint_server): # Task should be in cancelled state status = await task.status() assert status.status == "cancelled" + + +async def test_task_cancellation_interrupts_running_coroutine(endpoint_server): + """Task cancellation actually interrupts the running coroutine. + + This verifies that when a task is cancelled, the underlying asyncio + coroutine receives CancelledError rather than continuing to completion. + Requires pydocket >= 0.16.2. + + See: https://github.com/jlowin/fastmcp/issues/2679 + """ + started = asyncio.Event() + was_interrupted = asyncio.Event() + completed_normally = asyncio.Event() + + @endpoint_server.tool(task=True) + async def interruptible_tool() -> str: + started.set() + try: + await asyncio.sleep(60) + completed_normally.set() + return "completed" + except asyncio.CancelledError: + was_interrupted.set() + raise + + async with Client(endpoint_server) as client: + task = await client.call_tool("interruptible_tool", {}, task=True) + + # Wait for the tool to actually start executing + await asyncio.wait_for(started.wait(), timeout=5.0) + + # Cancel the task + await task.cancel() + + # Wait for cancellation to propagate + await asyncio.wait_for(was_interrupted.wait(), timeout=5.0) + + # The coroutine should have been interrupted, not completed normally + assert was_interrupted.is_set(), "Task was not interrupted by cancellation" + assert not completed_normally.is_set(), ( + "Task completed instead of being cancelled" + ) diff --git a/tests/server/test_dependencies.py b/tests/server/test_dependencies.py index 208ae3106..a05d3d436 100644 --- a/tests/server/test_dependencies.py +++ b/tests/server/test_dependencies.py @@ -733,3 +733,47 @@ async def test_resource_template_uri_cannot_match_dependency_name(mcp: FastMCP): @mcp.resource("auth://{token}/validate") async def validate(token: str = Depends(get_token)) -> str: return f"Validating with: {token}" + + +async def test_toolerror_propagates_from_dependency(mcp: FastMCP): + """ToolError raised in a dependency should propagate unchanged (issue #2633). + + When a dependency raises ToolError, it should not be wrapped in RuntimeError. + This allows developers to use ToolError for validation in dependencies. + """ + from fastmcp.exceptions import ToolError + + def validate_client_id() -> str: + raise ToolError("Client ID is required - select a client first") + + @mcp.tool() + async def my_tool(client_id: str = Depends(validate_client_id)) -> str: + return f"Working with client: {client_id}" + + async with Client(mcp) as client: + # ToolError is converted to an error result by the server + result = await client.call_tool("my_tool", {}, raise_on_error=False) + assert result.is_error + # The original error message should be preserved (not wrapped in RuntimeError) + assert result.content[0].text == "Client ID is required - select a client first" # type: ignore[attr-defined] + + +async def test_validation_error_propagates_from_dependency(mcp: FastMCP): + """ValidationError raised in a dependency should propagate unchanged.""" + from fastmcp.exceptions import ValidationError + + def validate_input() -> str: + raise ValidationError("Invalid input format") + + @mcp.tool() + async def tool_with_validation(val: str = Depends(validate_input)) -> str: + return val + + async with Client(mcp) as client: + # ValidationError is re-raised by the server and becomes an error result + # The original error message should be preserved (not wrapped in RuntimeError) + result = await client.call_tool( + "tool_with_validation", {}, raise_on_error=False + ) + assert result.is_error + assert result.content[0].text == "Invalid input format" # type: ignore[attr-defined] diff --git a/tests/server/test_logging.py b/tests/server/test_logging.py index cfc376c0b..e8810c4a9 100644 --- a/tests/server/test_logging.py +++ b/tests/server/test_logging.py @@ -49,9 +49,14 @@ async def test_uvicorn_logging_default_level( ) mock_server_instance.serve.assert_awaited_once() + # Signal the mock to finish and cancel with timeout + # Required for uvicorn 0.39+ due to context isolation + serve_finished_event.set() server_task.cancel() - with pytest.raises(asyncio.CancelledError): - await server_task + try: + await asyncio.wait_for(server_task, timeout=2.0) + except (asyncio.CancelledError, asyncio.TimeoutError): + pass @patch("fastmcp.server.server.uvicorn.Server") @@ -109,9 +114,14 @@ async def test_uvicorn_logging_with_custom_log_config( ) mock_server_instance.serve.assert_awaited_once() + # Signal the mock to finish and cancel with timeout + # Required for uvicorn 0.39+ due to context isolation + serve_finished_event.set() server_task.cancel() - with pytest.raises(asyncio.CancelledError): - await server_task + try: + await asyncio.wait_for(server_task, timeout=2.0) + except (asyncio.CancelledError, asyncio.TimeoutError): + pass @patch("fastmcp.server.server.uvicorn.Server") @@ -172,6 +182,11 @@ async def test_uvicorn_logging_custom_log_config_overrides_log_level_param( ) mock_server_instance.serve.assert_awaited_once() + # Signal the mock to finish and cancel with timeout + # Required for uvicorn 0.39+ due to context isolation + serve_finished_event.set() server_task.cancel() - with pytest.raises(asyncio.CancelledError): - await server_task + try: + await asyncio.wait_for(server_task, timeout=2.0) + except (asyncio.CancelledError, asyncio.TimeoutError): + pass diff --git a/tests/tools/test_tool.py b/tests/tools/test_tool.py index 1f21ffc77..b9c9ffa1e 100644 --- a/tests/tools/test_tool.py +++ b/tests/tools/test_tool.py @@ -194,18 +194,15 @@ class TestToolFromFunction: "tags": set(), "enabled": True, "parameters": { - "$defs": { - "UserInput": { + "properties": { + "user": { "properties": { "name": {"type": "string"}, "age": {"type": "integer"}, }, "required": ["name", "age"], "type": "object", - } - }, - "properties": { - "user": {"$ref": "#/$defs/UserInput"}, + }, "flag": {"type": "boolean"}, }, "required": ["user", "flag"], @@ -1296,6 +1293,69 @@ class TestAutomaticStructuredContent: "stuff": [{"value": 456, "stuff": []}], } + async def test_self_referencing_pydantic_model_has_type_object_at_root(self): + """Test that self-referencing Pydantic models have type: object at root. + + MCP spec requires outputSchema to have "type": "object" at the root level. + Pydantic generates schemas with $ref at root for self-referential models, + which violates this requirement. FastMCP should resolve the $ref. + + Regression test for issue #2455. + """ + + class Issue(BaseModel): + id: str + title: str + dependencies: list["Issue"] = [] + dependents: list["Issue"] = [] + + def get_issue(issue_id: str) -> Issue: + return Issue(id=issue_id, title="Test") + + tool = Tool.from_function(get_issue) + + # The output schema should have "type": "object" at root, not $ref + assert tool.output_schema is not None + assert tool.output_schema.get("type") == "object" + assert "properties" in tool.output_schema + # Should still have $defs for nested references + assert "$defs" in tool.output_schema + # Should NOT have $ref at root level + assert "$ref" not in tool.output_schema + + async def test_self_referencing_model_outputschema_mcp_compliant(self): + """Test that self-referencing model schemas are MCP spec compliant. + + The MCP spec requires: + - type: "object" at root level + - properties field + - required field (optional) + + This ensures clients can properly validate the schema. + + Regression test for issue #2455. + """ + + class Node(BaseModel): + id: str + children: list["Node"] = [] + + def get_node() -> Node: + return Node(id="1") + + tool = Tool.from_function(get_node) + + # Schema should be MCP-compliant + assert tool.output_schema is not None + assert tool.output_schema.get("type") == "object", ( + "MCP spec requires 'type': 'object' at root" + ) + assert "properties" in tool.output_schema + assert "id" in tool.output_schema["properties"] + assert "children" in tool.output_schema["properties"] + # Required should include 'id' + assert "id" in tool.output_schema.get("required", []) + async def test_int_return_no_structured_content_without_schema(self): """Test that int returns don't create structured content without output schema.""" @@ -1558,28 +1618,18 @@ class TestSerializationAlias: # not the first validation alias 'id' assert tool.output_schema is not None - # For object types, the schema may use $ref at root (self-referencing types) - # or have properties directly. Check both cases. - if "$ref" in tool.output_schema: - # Schema uses $ref - resolve to get the actual definition - assert "$defs" in tool.output_schema - ref_path = tool.output_schema["$ref"].replace("#/$defs/", "") - component_def = tool.output_schema["$defs"][ref_path] - else: - # Schema has properties directly (wrapped case) - assert "properties" in tool.output_schema - assert "result" in tool.output_schema["properties"] - assert "$defs" in tool.output_schema - # Find the Component definition - component_def = list(tool.output_schema["$defs"].values())[0] + # Object schemas have properties directly at root (MCP spec compliance) + # Root-level $refs are resolved to ensure type: object at root + assert "properties" in tool.output_schema + assert tool.output_schema.get("type") == "object" # Should have 'componentId' not 'id' in properties - assert "componentId" in component_def["properties"] - assert "id" not in component_def["properties"] + assert "componentId" in tool.output_schema["properties"] + assert "id" not in tool.output_schema["properties"] # Should require 'componentId' not 'id' - assert "componentId" in component_def["required"] - assert "id" not in component_def.get("required", []) + assert "componentId" in tool.output_schema.get("required", []) + assert "id" not in tool.output_schema.get("required", []) async def test_tool_execution_with_serialization_alias(self): """Test that tool execution works correctly with serialization aliases.""" diff --git a/tests/tools/test_tool_manager.py b/tests/tools/test_tool_manager.py index 128bdea79..9bcdae339 100644 --- a/tests/tools/test_tool_manager.py +++ b/tests/tools/test_tool_manager.py @@ -75,8 +75,9 @@ class TestAddTools: assert tool is not None assert tool.name == "create_user" assert tool.description == "Create a new user." - assert "name" in tool.parameters["$defs"]["UserInput"]["properties"] - assert "age" in tool.parameters["$defs"]["UserInput"]["properties"] + # $refs are dereferenced so UserInput is inlined + assert "name" in tool.parameters["properties"]["user"]["properties"] + assert "age" in tool.parameters["properties"]["user"]["properties"] assert "flag" in tool.parameters["properties"] async def test_callable_object(self): diff --git a/tests/tools/test_tool_transform.py b/tests/tools/test_tool_transform.py index 565e16ad3..9bf47de05 100644 --- a/tests/tools/test_tool_transform.py +++ b/tests/tools/test_tool_transform.py @@ -216,10 +216,14 @@ async def test_hidden_param_prunes_defs(): schema = new_tool.parameters # Only 'a' should be visible assert list(schema["properties"].keys()) == ["a"] - # $defs should only contain VisibleType, not HiddenType - defs = schema.get("$defs", {}) - assert "VisibleType" in defs - assert "HiddenType" not in defs + # $refs are dereferenced so VisibleType is inlined, not in $defs + assert "$defs" not in schema + # VisibleType's structure should be inlined into the property + assert schema["properties"]["a"] == { + "properties": {"x": {"type": "integer"}}, + "required": ["x"], + "type": "object", + } async def test_forward_with_argument_mapping(add_tool): @@ -427,7 +431,8 @@ def test_transform_args_with_parent_defaults(): new_tool = Tool.from_tool(tool) - assert new_tool.parameters["$defs"] == tool.parameters["$defs"] + # $refs are dereferenced, so schemas should match (both inlined) + assert new_tool.parameters == tool.parameters def test_transform_args_validation_unknown_arg(add_tool): @@ -1567,20 +1572,20 @@ class TestInputSchema: complex_tool, transform_args={"unused_param": ArgTransform(hide=True)} ) - assert "UnusedType" not in transformed_tool.parameters["$defs"] + # $refs are dereferenced, so no $defs section + assert "$defs" not in transformed_tool.parameters assert transformed_tool.parameters == snapshot( { "type": "object", - "properties": {"used_param": {"$ref": "#/$defs/UsedType"}}, - "required": ["used_param"], - "$defs": { - "UsedType": { + "properties": { + "used_param": { "properties": {"value": {"type": "string"}}, "required": ["value"], "type": "object", } }, + "required": ["used_param"], } ) @@ -1610,15 +1615,14 @@ class TestInputSchema: assert transformed.parameters == snapshot( { "type": "object", - "properties": {"renamed_input": {"$ref": "#/$defs/InputType"}}, - "required": ["renamed_input"], - "$defs": { - "InputType": { + "properties": { + "renamed_input": { "properties": {"data": {"type": "string"}}, "required": ["data"], "type": "object", } }, + "required": ["renamed_input"], } ) @@ -1648,26 +1652,23 @@ class TestInputSchema: { "type": "object", "properties": { - "param_a": {"$ref": "#/$defs/TypeA"}, - "param_b": {"$ref": "#/$defs/TypeB"}, - }, - "required": IsList("param_b", "param_a", check_order=False), - "$defs": { - "TypeA": { + "param_a": { "properties": {"a": {"type": "string"}}, "required": ["a"], "type": "object", }, - "TypeB": { + "param_b": { "properties": {"b": {"type": "integer"}}, "required": ["b"], "type": "object", }, }, + "required": IsList("param_b", "param_a", check_order=False), } ) - assert "TypeA" in transform1.parameters["$defs"] + # $refs are dereferenced, so TypeA is inlined + assert "$defs" not in transform1.parameters # Second transform: hide param_b transform2 = Tool.from_tool( @@ -1675,19 +1676,18 @@ class TestInputSchema: transform_args={"param_b": ArgTransform(hide=True, default=TypeB(b=42))}, ) - assert "TypeB" not in transform2.parameters["$defs"] + assert "$defs" not in transform2.parameters assert transform2.parameters == snapshot( { "type": "object", - "properties": {"param_a": {"$ref": "#/$defs/TypeA"}}, - "required": ["param_a"], - "$defs": { - "TypeA": { + "properties": { + "param_a": { "properties": {"a": {"type": "string"}}, "required": ["a"], "type": "object", } }, + "required": ["param_a"], } ) diff --git a/tests/utilities/test_json_schema.py b/tests/utilities/test_json_schema.py index 5fce3be68..79e4b1fc5 100644 --- a/tests/utilities/test_json_schema.py +++ b/tests/utilities/test_json_schema.py @@ -1,6 +1,8 @@ from fastmcp.utilities.json_schema import ( _prune_param, compress_schema, + dereference_refs, + resolve_root_ref, ) # Wrapper for backward compatibility with tests @@ -505,3 +507,260 @@ class TestCompressSchema: "title" not in compressed["properties"]["title"]["properties"]["subtitle"] ) assert "title" not in compressed["properties"]["normal_field"] + + +class TestDereferenceRefs: + """Tests for the dereference_refs function.""" + + def test_falls_back_for_circular_refs(self): + """Test that circular references fall back to resolve_root_ref.""" + schema = { + "$defs": { + "Node": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": {"$ref": "#/$defs/Node"}, + } + }, + } + }, + "$ref": "#/$defs/Node", + } + result = dereference_refs(schema) + + # Should fall back to resolve_root_ref behavior + # Root should be resolved but nested refs preserved + assert result.get("type") == "object" + assert "$defs" in result # $defs preserved for circular refs + + def test_preserves_sibling_keywords(self): + """Test that sibling keywords (default, description) are preserved. + + Pydantic places description, default, examples as siblings to $ref. + These should not be lost during dereferencing. + """ + schema = { + "$defs": { + "Status": {"type": "string", "enum": ["active", "inactive"]}, + }, + "properties": { + "status": { + "$ref": "#/$defs/Status", + "default": "active", + "description": "The user status", + }, + }, + "type": "object", + } + result = dereference_refs(schema) + + # $ref should be inlined with siblings preserved + status = result["properties"]["status"] + assert status["type"] == "string" + assert status["enum"] == ["active", "inactive"] + assert status["default"] == "active" + assert status["description"] == "The user status" + # $defs should be removed + assert "$defs" not in result + + def test_preserves_siblings_in_lists(self): + """Test that siblings are preserved for $refs inside lists (allOf, anyOf, etc).""" + schema = { + "$defs": { + "StringType": {"type": "string"}, + "IntType": {"type": "integer"}, + }, + "properties": { + "field": { + "anyOf": [ + {"$ref": "#/$defs/StringType", "description": "As string"}, + {"$ref": "#/$defs/IntType", "description": "As integer"}, + ] + }, + }, + } + result = dereference_refs(schema) + + # Both items in anyOf should have their siblings preserved + any_of = result["properties"]["field"]["anyOf"] + assert any_of[0]["type"] == "string" + assert any_of[0]["description"] == "As string" + assert any_of[1]["type"] == "integer" + assert any_of[1]["description"] == "As integer" + assert "$defs" not in result + + def test_preserves_nested_siblings(self): + """Test that siblings on nested $refs are preserved.""" + schema = { + "$defs": { + "Address": { + "type": "object", + "properties": { + "country": {"$ref": "#/$defs/Country", "default": "US"}, + }, + }, + "Country": {"type": "string", "enum": ["US", "UK", "CA"]}, + }, + "properties": { + "home_address": {"$ref": "#/$defs/Address"}, + }, + } + result = dereference_refs(schema) + + # The nested $ref's sibling (default) should be preserved + country = result["properties"]["home_address"]["properties"]["country"] + assert country["type"] == "string" + assert country["enum"] == ["US", "UK", "CA"] + assert country["default"] == "US" + assert "$defs" not in result + + +class TestResolveRootRef: + """Tests for the resolve_root_ref function. + + This function resolves $ref at root level to meet MCP spec requirements. + MCP specification requires outputSchema to have "type": "object" at root. + """ + + def test_resolves_simple_root_ref(self): + """Test that simple $ref at root is resolved.""" + schema = { + "$defs": { + "Node": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + }, + "required": ["id"], + } + }, + "$ref": "#/$defs/Node", + } + result = resolve_root_ref(schema) + + # Should have type: object at root now + assert result.get("type") == "object" + assert "properties" in result + assert "id" in result["properties"] + assert "name" in result["properties"] + # Should still have $defs for nested references + assert "$defs" in result + # Should NOT have $ref at root + assert "$ref" not in result + + def test_resolves_self_referential_model(self): + """Test resolving schema for self-referential models like Issue.""" + # This is the exact schema Pydantic generates for self-referential models + schema = { + "$defs": { + "Issue": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "title": {"type": "string"}, + "dependencies": { + "type": "array", + "items": {"$ref": "#/$defs/Issue"}, + }, + "dependents": { + "type": "array", + "items": {"$ref": "#/$defs/Issue"}, + }, + }, + "required": ["id", "title"], + } + }, + "$ref": "#/$defs/Issue", + } + result = resolve_root_ref(schema) + + # Should have type: object at root + assert result.get("type") == "object" + assert "properties" in result + assert "id" in result["properties"] + assert "dependencies" in result["properties"] + # Nested $refs should still point to $defs + assert result["properties"]["dependencies"]["items"]["$ref"] == "#/$defs/Issue" + # Should have $defs preserved for nested references + assert "$defs" in result + assert "Issue" in result["$defs"] + + def test_does_not_modify_schema_with_type_at_root(self): + """Test that schemas already having type at root are not modified.""" + schema = { + "type": "object", + "properties": {"id": {"type": "string"}}, + "$defs": {"SomeType": {"type": "string"}}, + "$ref": "#/$defs/SomeType", # This would be unusual but possible + } + result = resolve_root_ref(schema) + + # Schema should be unchanged (returned as-is) + assert result is schema + + def test_does_not_modify_schema_without_ref(self): + """Test that schemas without $ref are not modified.""" + schema = { + "type": "object", + "properties": {"id": {"type": "string"}}, + } + result = resolve_root_ref(schema) + + assert result is schema + + def test_does_not_modify_schema_without_defs(self): + """Test that schemas with $ref but without $defs are not modified.""" + schema = { + "$ref": "#/$defs/Missing", + } + result = resolve_root_ref(schema) + + assert result is schema + + def test_does_not_modify_external_ref(self): + """Test that external $refs (not pointing to $defs) are not resolved.""" + schema = { + "$defs": {"Node": {"type": "object"}}, + "$ref": "https://example.com/schema.json#/definitions/Node", + } + result = resolve_root_ref(schema) + + assert result is schema + + def test_preserves_all_defs_for_nested_references(self): + """Test that $defs are preserved even if multiple definitions exist.""" + schema = { + "$defs": { + "Node": { + "type": "object", + "properties": { + "child": {"$ref": "#/$defs/ChildNode"}, + }, + }, + "ChildNode": { + "type": "object", + "properties": {"value": {"type": "string"}}, + }, + }, + "$ref": "#/$defs/Node", + } + result = resolve_root_ref(schema) + + # Both defs should be preserved + assert "$defs" in result + assert "Node" in result["$defs"] + assert "ChildNode" in result["$defs"] + + def test_handles_missing_def_gracefully(self): + """Test that missing definition in $defs doesn't cause error.""" + schema = { + "$defs": {"OtherType": {"type": "string"}}, + "$ref": "#/$defs/Missing", + } + result = resolve_root_ref(schema) + + # Should return original schema unchanged + assert result is schema diff --git a/tests/utilities/test_version_check.py b/tests/utilities/test_version_check.py new file mode 100644 index 000000000..9cc9aab3b --- /dev/null +++ b/tests/utilities/test_version_check.py @@ -0,0 +1,315 @@ +"""Tests for version checking utilities.""" + +import json +import time +from pathlib import Path +from unittest.mock import MagicMock, patch + +import httpx +import pytest + +from fastmcp.utilities.version_check import ( + CACHE_TTL_SECONDS, + _fetch_latest_version, + _get_cache_path, + _read_cache, + _write_cache, + check_for_newer_version, + get_latest_version, +) + + +class TestCachePath: + def test_cache_path_in_home_directory(self): + """Cache file should be in fastmcp home directory.""" + cache_path = _get_cache_path() + assert cache_path.name == "version_cache.json" + assert "fastmcp" in str(cache_path).lower() + + def test_cache_path_prerelease_suffix(self): + """Prerelease cache uses different file.""" + cache_path = _get_cache_path(include_prereleases=True) + assert cache_path.name == "version_cache_prerelease.json" + + +class TestReadCache: + def test_read_cache_no_file(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + """Reading non-existent cache returns None.""" + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: tmp_path / "nonexistent.json", + ) + version, timestamp = _read_cache() + assert version is None + assert timestamp == 0 + + def test_read_cache_valid(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + """Reading valid cache returns version and timestamp.""" + cache_file = tmp_path / "version_cache.json" + cache_file.write_text( + json.dumps({"latest_version": "2.5.0", "timestamp": 1000}) + ) + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + version, timestamp = _read_cache() + assert version == "2.5.0" + assert timestamp == 1000 + + def test_read_cache_invalid_json( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ): + """Reading invalid JSON returns None.""" + cache_file = tmp_path / "version_cache.json" + cache_file.write_text("not valid json") + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + version, timestamp = _read_cache() + assert version is None + assert timestamp == 0 + + +class TestWriteCache: + def test_write_cache_creates_file( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ): + """Writing cache creates the cache file.""" + cache_file = tmp_path / "subdir" / "version_cache.json" + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + _write_cache("2.6.0") + + assert cache_file.exists() + data = json.loads(cache_file.read_text()) + assert data["latest_version"] == "2.6.0" + assert "timestamp" in data + + +class TestFetchLatestVersion: + def test_fetch_success(self): + """Successful fetch returns highest stable version.""" + mock_response = MagicMock() + mock_response.json.return_value = { + "releases": { + "2.5.0": [], + "2.4.0": [], + "2.6.0b1": [], # prerelease should be skipped + } + } + + with patch("httpx.get", return_value=mock_response) as mock_get: + version = _fetch_latest_version() + assert version == "2.5.0" + mock_get.assert_called_once() + + def test_fetch_network_error(self): + """Network error returns None.""" + with patch("httpx.get", side_effect=httpx.HTTPError("Network error")): + version = _fetch_latest_version() + assert version is None + + def test_fetch_invalid_response(self): + """Invalid response returns None.""" + mock_response = MagicMock() + mock_response.json.return_value = {"unexpected": "format"} + + with patch("httpx.get", return_value=mock_response): + version = _fetch_latest_version() + assert version is None + + def test_fetch_prereleases(self): + """Fetching with prereleases returns highest version.""" + mock_response = MagicMock() + mock_response.json.return_value = { + "info": {"version": "2.5.0"}, + "releases": { + "2.5.0": [], + "2.6.0b1": [], + "2.6.0b2": [], + "2.4.0": [], + }, + } + + with patch("httpx.get", return_value=mock_response): + version = _fetch_latest_version(include_prereleases=True) + assert version == "2.6.0b2" + + def test_fetch_prereleases_stable_is_highest(self): + """Prerelease mode still returns stable if it's highest.""" + mock_response = MagicMock() + mock_response.json.return_value = { + "info": {"version": "2.5.0"}, + "releases": { + "2.5.0": [], + "2.5.0b1": [], + "2.4.0": [], + }, + } + + with patch("httpx.get", return_value=mock_response): + version = _fetch_latest_version(include_prereleases=True) + assert version == "2.5.0" + + +class TestGetLatestVersion: + def test_returns_cached_version_if_fresh( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ): + """Uses cached version if cache is fresh.""" + cache_file = tmp_path / "version_cache.json" + cache_file.write_text( + json.dumps({"latest_version": "2.5.0", "timestamp": time.time()}) + ) + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + with patch( + "fastmcp.utilities.version_check._fetch_latest_version" + ) as mock_fetch: + version = get_latest_version() + assert version == "2.5.0" + mock_fetch.assert_not_called() + + def test_fetches_if_cache_stale( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ): + """Fetches from PyPI if cache is stale.""" + cache_file = tmp_path / "version_cache.json" + old_timestamp = time.time() - CACHE_TTL_SECONDS - 100 + cache_file.write_text( + json.dumps({"latest_version": "2.4.0", "timestamp": old_timestamp}) + ) + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + with patch( + "fastmcp.utilities.version_check._fetch_latest_version", + return_value="2.5.0", + ) as mock_fetch: + version = get_latest_version() + assert version == "2.5.0" + mock_fetch.assert_called_once() + + def test_returns_stale_cache_if_fetch_fails( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ): + """Returns stale cache if fetch fails.""" + cache_file = tmp_path / "version_cache.json" + old_timestamp = time.time() - CACHE_TTL_SECONDS - 100 + cache_file.write_text( + json.dumps({"latest_version": "2.4.0", "timestamp": old_timestamp}) + ) + monkeypatch.setattr( + "fastmcp.utilities.version_check._get_cache_path", + lambda include_prereleases=False: cache_file, + ) + + with patch( + "fastmcp.utilities.version_check._fetch_latest_version", return_value=None + ): + version = get_latest_version() + assert version == "2.4.0" + + +class TestCheckForNewerVersion: + def test_returns_none_if_disabled(self, monkeypatch: pytest.MonkeyPatch): + """Returns None if check_for_updates is off.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "off") + + result = check_for_newer_version() + assert result is None + + def test_returns_none_if_current(self, monkeypatch: pytest.MonkeyPatch): + """Returns None if current version is latest.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "stable") + monkeypatch.setattr(fastmcp, "__version__", "2.5.0") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", return_value="2.5.0" + ): + result = check_for_newer_version() + assert result is None + + def test_returns_version_if_newer(self, monkeypatch: pytest.MonkeyPatch): + """Returns new version if available.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "stable") + monkeypatch.setattr(fastmcp, "__version__", "2.4.0") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", return_value="2.5.0" + ): + result = check_for_newer_version() + assert result == "2.5.0" + + def test_returns_none_if_older_available(self, monkeypatch: pytest.MonkeyPatch): + """Returns None if pypi version is older than current (dev version).""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "stable") + monkeypatch.setattr(fastmcp, "__version__", "3.0.0.dev1") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", return_value="2.5.0" + ): + result = check_for_newer_version() + assert result is None + + def test_handles_invalid_versions(self, monkeypatch: pytest.MonkeyPatch): + """Handles invalid version strings gracefully.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "stable") + monkeypatch.setattr(fastmcp, "__version__", "invalid") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", + return_value="also-invalid", + ): + result = check_for_newer_version() + assert result is None + + def test_prerelease_setting(self, monkeypatch: pytest.MonkeyPatch): + """Prerelease setting passes include_prereleases=True.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "prerelease") + monkeypatch.setattr(fastmcp, "__version__", "2.5.0") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", return_value="2.6.0b1" + ) as mock_get: + result = check_for_newer_version() + assert result == "2.6.0b1" + mock_get.assert_called_once_with(True) + + def test_stable_setting(self, monkeypatch: pytest.MonkeyPatch): + """Stable setting passes include_prereleases=False.""" + import fastmcp + + monkeypatch.setattr(fastmcp.settings, "check_for_updates", "stable") + monkeypatch.setattr(fastmcp, "__version__", "2.4.0") + + with patch( + "fastmcp.utilities.version_check.get_latest_version", return_value="2.5.0" + ) as mock_get: + result = check_for_newer_version() + assert result == "2.5.0" + mock_get.assert_called_once_with(False) diff --git a/uv.lock b/uv.lock index b26901997..17417ef6e 100644 --- a/uv.lock +++ b/uv.lock @@ -26,7 +26,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.75.0" +version = "0.76.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -38,23 +38,23 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/1f/08e95f4b7e2d35205ae5dcbb4ae97e7d477fc521c275c02609e2931ece2d/anthropic-0.75.0.tar.gz", hash = "sha256:e8607422f4ab616db2ea5baacc215dd5f028da99ce2f022e33c7c535b29f3dfb", size = 439565, upload-time = "2025-11-24T20:41:45.28Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/be/d11abafaa15d6304826438170f7574d750218f49a106c54424a40cef4494/anthropic-0.76.0.tar.gz", hash = "sha256:e0cae6a368986d5cf6df743dfbb1b9519e6a9eee9c6c942ad8121c0b34416ffe", size = 495483, upload-time = "2026-01-13T18:41:14.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/1c/1cd02b7ae64302a6e06724bf80a96401d5313708651d277b1458504a1730/anthropic-0.75.0-py3-none-any.whl", hash = "sha256:ea8317271b6c15d80225a9f3c670152746e88805a7a61e14d4a374577164965b", size = 388164, upload-time = "2025-11-24T20:41:43.587Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/7b0fd9c1a738f59d3babe2b4212031c34ab7d0fda4ffef15b58a55c5bcea/anthropic-0.76.0-py3-none-any.whl", hash = "sha256:81efa3113901192af2f0fe977d3ec73fdadb1e691586306c4256cd6d5ccc331c", size = 390309, upload-time = "2026-01-13T18:41:13.483Z" }, ] [[package]] name = "anyio" -version = "4.12.0" +version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] @@ -86,14 +86,14 @@ wheels = [ [[package]] name = "authlib" -version = "1.6.5" +version = "1.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cd/3f/1d3bbd0bf23bdd99276d4def22f29c27a914067b4cf66f753ff9b8bbd0f3/authlib-1.6.5.tar.gz", hash = "sha256:6aaf9c79b7cc96c900f0b284061691c5d4e61221640a948fe690b556a6d6d10b", size = 164553, upload-time = "2025-10-02T13:36:09.489Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" }, + { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, ] [[package]] @@ -116,29 +116,29 @@ wheels = [ [[package]] name = "beartype" -version = "0.22.8" +version = "0.22.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/1d/794ae2acaa67c8b216d91d5919da2606c2bb14086849ffde7f5555f3a3a5/beartype-0.22.8.tar.gz", hash = "sha256:b19b21c9359722ee3f7cc433f063b3e13997b27ae8226551ea5062e621f61165", size = 1602262, upload-time = "2025-12-03T05:11:10.766Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/2a/fbcbf5a025d3e71ddafad7efd43e34ec4362f4d523c3c471b457148fb211/beartype-0.22.8-py3-none-any.whl", hash = "sha256:b832882d04e41a4097bab9f63e6992bc6de58c414ee84cba9b45b67314f5ab2e", size = 1331895, upload-time = "2025-12-03T05:11:08.373Z" }, + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, ] [[package]] name = "cachetools" -version = "6.2.2" +version = "6.2.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571, upload-time = "2025-11-13T17:42:51.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/86/e7/18ea2907d2ca91e9c0697596b8e60cd485b091152eb4109fad1e468e457d/cachetools-6.2.5.tar.gz", hash = "sha256:6d8bfbba1ba94412fb9d9196c4da7a87e9d4928fffc5e93542965dca4740c77f", size = 32168, upload-time = "2026-01-25T14:57:40.349Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503, upload-time = "2025-11-13T17:42:50.232Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/24169d70ec5264b65ba54ba49b3d10f46d6b1ad97e185c94556539b3dfc8/cachetools-6.2.5-py3-none-any.whl", hash = "sha256:db3ae5465e90befb7c74720dd9308d77a09b7cf13433570e07caa0845c30d5fe", size = 11553, upload-time = "2026-01-25T14:57:39.112Z" }, ] [[package]] name = "certifi" -version = "2025.11.12" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, ] [[package]] @@ -344,101 +344,101 @@ wheels = [ [[package]] name = "coverage" -version = "7.13.0" +version = "7.13.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/08/bdd7ccca14096f7eb01412b87ac11e5d16e4cb54b6e328afc9dee8bdaec1/coverage-7.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02d9fb9eccd48f6843c98a37bd6817462f130b86da8660461e8f5e54d4c06070", size = 217979, upload-time = "2025-12-08T13:12:14.505Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/d1302e3416298a28b5663ae1117546a745d9d19fde7e28402b2c5c3e2109/coverage-7.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:367449cf07d33dc216c083f2036bb7d976c6e4903ab31be400ad74ad9f85ce98", size = 218496, upload-time = "2025-12-08T13:12:16.237Z" }, - { url = "https://files.pythonhosted.org/packages/07/26/d36c354c8b2a320819afcea6bffe72839efd004b98d1d166b90801d49d57/coverage-7.13.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cdb3c9f8fef0a954c632f64328a3935988d33a6604ce4bf67ec3e39670f12ae5", size = 245237, upload-time = "2025-12-08T13:12:17.858Z" }, - { url = "https://files.pythonhosted.org/packages/91/52/be5e85631e0eec547873d8b08dd67a5f6b111ecfe89a86e40b89b0c1c61c/coverage-7.13.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d10fd186aac2316f9bbb46ef91977f9d394ded67050ad6d84d94ed6ea2e8e54e", size = 247061, upload-time = "2025-12-08T13:12:19.132Z" }, - { url = "https://files.pythonhosted.org/packages/0f/45/a5e8fa0caf05fbd8fa0402470377bff09cc1f026d21c05c71e01295e55ab/coverage-7.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f88ae3e69df2ab62fb0bc5219a597cb890ba5c438190ffa87490b315190bb33", size = 248928, upload-time = "2025-12-08T13:12:20.702Z" }, - { url = "https://files.pythonhosted.org/packages/f5/42/ffb5069b6fd1b95fae482e02f3fecf380d437dd5a39bae09f16d2e2e7e01/coverage-7.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4be718e51e86f553bcf515305a158a1cd180d23b72f07ae76d6017c3cc5d791", size = 245931, upload-time = "2025-12-08T13:12:22.243Z" }, - { url = "https://files.pythonhosted.org/packages/95/6e/73e809b882c2858f13e55c0c36e94e09ce07e6165d5644588f9517efe333/coverage-7.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a00d3a393207ae12f7c49bb1c113190883b500f48979abb118d8b72b8c95c032", size = 246968, upload-time = "2025-12-08T13:12:23.52Z" }, - { url = "https://files.pythonhosted.org/packages/87/08/64ebd9e64b6adb8b4a4662133d706fbaccecab972e0b3ccc23f64e2678ad/coverage-7.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a7b1cd820e1b6116f92c6128f1188e7afe421c7e1b35fa9836b11444e53ebd9", size = 244972, upload-time = "2025-12-08T13:12:24.781Z" }, - { url = "https://files.pythonhosted.org/packages/12/97/f4d27c6fe0cb375a5eced4aabcaef22de74766fb80a3d5d2015139e54b22/coverage-7.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:37eee4e552a65866f15dedd917d5e5f3d59805994260720821e2c1b51ac3248f", size = 245241, upload-time = "2025-12-08T13:12:28.041Z" }, - { url = "https://files.pythonhosted.org/packages/0c/94/42f8ae7f633bf4c118bf1038d80472f9dade88961a466f290b81250f7ab7/coverage-7.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62d7c4f13102148c78d7353c6052af6d899a7f6df66a32bddcc0c0eb7c5326f8", size = 245847, upload-time = "2025-12-08T13:12:29.337Z" }, - { url = "https://files.pythonhosted.org/packages/a8/2f/6369ca22b6b6d933f4f4d27765d313d8914cc4cce84f82a16436b1a233db/coverage-7.13.0-cp310-cp310-win32.whl", hash = "sha256:24e4e56304fdb56f96f80eabf840eab043b3afea9348b88be680ec5986780a0f", size = 220573, upload-time = "2025-12-08T13:12:30.905Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dc/a6a741e519acceaeccc70a7f4cfe5d030efc4b222595f0677e101af6f1f3/coverage-7.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:74c136e4093627cf04b26a35dab8cbfc9b37c647f0502fc313376e11726ba303", size = 221509, upload-time = "2025-12-08T13:12:32.09Z" }, - { url = "https://files.pythonhosted.org/packages/f1/dc/888bf90d8b1c3d0b4020a40e52b9f80957d75785931ec66c7dfaccc11c7d/coverage-7.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0dfa3855031070058add1a59fdfda0192fd3e8f97e7c81de0596c145dea51820", size = 218104, upload-time = "2025-12-08T13:12:33.333Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ea/069d51372ad9c380214e86717e40d1a743713a2af191cfba30a0911b0a4a/coverage-7.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fdb6f54f38e334db97f72fa0c701e66d8479af0bc3f9bfb5b90f1c30f54500f", size = 218606, upload-time = "2025-12-08T13:12:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/68/09/77b1c3a66c2aa91141b6c4471af98e5b1ed9b9e6d17255da5eb7992299e3/coverage-7.13.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7e442c013447d1d8d195be62852270b78b6e255b79b8675bad8479641e21fd96", size = 248999, upload-time = "2025-12-08T13:12:36.02Z" }, - { url = "https://files.pythonhosted.org/packages/0a/32/2e2f96e9d5691eaf1181d9040f850b8b7ce165ea10810fd8e2afa534cef7/coverage-7.13.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ed5630d946859de835a85e9a43b721123a8a44ec26e2830b296d478c7fd4259", size = 250925, upload-time = "2025-12-08T13:12:37.221Z" }, - { url = "https://files.pythonhosted.org/packages/7b/45/b88ddac1d7978859b9a39a8a50ab323186148f1d64bc068f86fc77706321/coverage-7.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f15a931a668e58087bc39d05d2b4bf4b14ff2875b49c994bbdb1c2217a8daeb", size = 253032, upload-time = "2025-12-08T13:12:38.763Z" }, - { url = "https://files.pythonhosted.org/packages/71/cb/e15513f94c69d4820a34b6bf3d2b1f9f8755fa6021be97c7065442d7d653/coverage-7.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30a3a201a127ea57f7e14ba43c93c9c4be8b7d17a26e03bb49e6966d019eede9", size = 249134, upload-time = "2025-12-08T13:12:40.382Z" }, - { url = "https://files.pythonhosted.org/packages/09/61/d960ff7dc9e902af3310ce632a875aaa7860f36d2bc8fc8b37ee7c1b82a5/coverage-7.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a485ff48fbd231efa32d58f479befce52dcb6bfb2a88bb7bf9a0b89b1bc8030", size = 250731, upload-time = "2025-12-08T13:12:41.992Z" }, - { url = "https://files.pythonhosted.org/packages/98/34/c7c72821794afc7c7c2da1db8f00c2c98353078aa7fb6b5ff36aac834b52/coverage-7.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:22486cdafba4f9e471c816a2a5745337742a617fef68e890d8baf9f3036d7833", size = 248795, upload-time = "2025-12-08T13:12:43.331Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5b/e0f07107987a43b2def9aa041c614ddb38064cbf294a71ef8c67d43a0cdd/coverage-7.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:263c3dbccc78e2e331e59e90115941b5f53e85cfcc6b3b2fbff1fd4e3d2c6ea8", size = 248514, upload-time = "2025-12-08T13:12:44.546Z" }, - { url = "https://files.pythonhosted.org/packages/71/c2/c949c5d3b5e9fc6dd79e1b73cdb86a59ef14f3709b1d72bf7668ae12e000/coverage-7.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e5330fa0cc1f5c3c4c3bb8e101b742025933e7848989370a1d4c8c5e401ea753", size = 249424, upload-time = "2025-12-08T13:12:45.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/f1/bbc009abd6537cec0dffb2cc08c17a7f03de74c970e6302db4342a6e05af/coverage-7.13.0-cp311-cp311-win32.whl", hash = "sha256:0f4872f5d6c54419c94c25dd6ae1d015deeb337d06e448cd890a1e89a8ee7f3b", size = 220597, upload-time = "2025-12-08T13:12:47.378Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f6/d9977f2fb51c10fbaed0718ce3d0a8541185290b981f73b1d27276c12d91/coverage-7.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51a202e0f80f241ccb68e3e26e19ab5b3bf0f813314f2c967642f13ebcf1ddfe", size = 221536, upload-time = "2025-12-08T13:12:48.7Z" }, - { url = "https://files.pythonhosted.org/packages/be/ad/3fcf43fd96fb43e337a3073dea63ff148dcc5c41ba7a14d4c7d34efb2216/coverage-7.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:d2a9d7f1c11487b1c69367ab3ac2d81b9b3721f097aa409a3191c3e90f8f3dd7", size = 220206, upload-time = "2025-12-08T13:12:50.365Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f1/2619559f17f31ba00fc40908efd1fbf1d0a5536eb75dc8341e7d660a08de/coverage-7.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0b3d67d31383c4c68e19a88e28fc4c2e29517580f1b0ebec4a069d502ce1e0bf", size = 218274, upload-time = "2025-12-08T13:12:52.095Z" }, - { url = "https://files.pythonhosted.org/packages/2b/11/30d71ae5d6e949ff93b2a79a2c1b4822e00423116c5c6edfaeef37301396/coverage-7.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:581f086833d24a22c89ae0fe2142cfaa1c92c930adf637ddf122d55083fb5a0f", size = 218638, upload-time = "2025-12-08T13:12:53.418Z" }, - { url = "https://files.pythonhosted.org/packages/79/c2/fce80fc6ded8d77e53207489d6065d0fed75db8951457f9213776615e0f5/coverage-7.13.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0a3a30f0e257df382f5f9534d4ce3d4cf06eafaf5192beb1a7bd066cb10e78fb", size = 250129, upload-time = "2025-12-08T13:12:54.744Z" }, - { url = "https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621", size = 252885, upload-time = "2025-12-08T13:12:56.401Z" }, - { url = "https://files.pythonhosted.org/packages/0d/f8/972a5affea41de798691ab15d023d3530f9f56a72e12e243f35031846ff7/coverage-7.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f5d9bd30756fff3e7216491a0d6d520c448d5124d3d8e8f56446d6412499e74", size = 253974, upload-time = "2025-12-08T13:12:57.718Z" }, - { url = "https://files.pythonhosted.org/packages/8a/56/116513aee860b2c7968aa3506b0f59b22a959261d1dbf3aea7b4450a7520/coverage-7.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a23e5a1f8b982d56fa64f8e442e037f6ce29322f1f9e6c2344cd9e9f4407ee57", size = 250538, upload-time = "2025-12-08T13:12:59.254Z" }, - { url = "https://files.pythonhosted.org/packages/d6/75/074476d64248fbadf16dfafbf93fdcede389ec821f74ca858d7c87d2a98c/coverage-7.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9b01c22bc74a7fb44066aaf765224c0d933ddf1f5047d6cdfe4795504a4493f8", size = 251912, upload-time = "2025-12-08T13:13:00.604Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d2/aa4f8acd1f7c06024705c12609d8698c51b27e4d635d717cd1934c9668e2/coverage-7.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:898cce66d0836973f48dda4e3514d863d70142bdf6dfab932b9b6a90ea5b222d", size = 250054, upload-time = "2025-12-08T13:13:01.892Z" }, - { url = "https://files.pythonhosted.org/packages/19/98/8df9e1af6a493b03694a1e8070e024e7d2cdc77adedc225a35e616d505de/coverage-7.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3ab483ea0e251b5790c2aac03acde31bff0c736bf8a86829b89382b407cd1c3b", size = 249619, upload-time = "2025-12-08T13:13:03.236Z" }, - { url = "https://files.pythonhosted.org/packages/d8/71/f8679231f3353018ca66ef647fa6fe7b77e6bff7845be54ab84f86233363/coverage-7.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d84e91521c5e4cb6602fe11ece3e1de03b2760e14ae4fcf1a4b56fa3c801fcd", size = 251496, upload-time = "2025-12-08T13:13:04.511Z" }, - { url = "https://files.pythonhosted.org/packages/04/86/9cb406388034eaf3c606c22094edbbb82eea1fa9d20c0e9efadff20d0733/coverage-7.13.0-cp312-cp312-win32.whl", hash = "sha256:193c3887285eec1dbdb3f2bd7fbc351d570ca9c02ca756c3afbc71b3c98af6ef", size = 220808, upload-time = "2025-12-08T13:13:06.422Z" }, - { url = "https://files.pythonhosted.org/packages/1c/59/af483673df6455795daf5f447c2f81a3d2fcfc893a22b8ace983791f6f34/coverage-7.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:4f3e223b2b2db5e0db0c2b97286aba0036ca000f06aca9b12112eaa9af3d92ae", size = 221616, upload-time = "2025-12-08T13:13:07.95Z" }, - { url = "https://files.pythonhosted.org/packages/64/b0/959d582572b30a6830398c60dd419c1965ca4b5fb38ac6b7093a0d50ca8d/coverage-7.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:086cede306d96202e15a4b77ace8472e39d9f4e5f9fd92dd4fecdfb2313b2080", size = 220261, upload-time = "2025-12-08T13:13:09.581Z" }, - { url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" }, - { url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" }, - { url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" }, - { url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" }, - { url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" }, - { url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" }, - { url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" }, - { url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" }, - { url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" }, - { url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" }, - { url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" }, - { url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" }, - { url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" }, - { url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" }, - { url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" }, - { url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" }, - { url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" }, - { url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" }, - { url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" }, - { url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" }, - { url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" }, - { url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" }, - { url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" }, - { url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" }, - { url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" }, - { url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" }, - { url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" }, - { url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" }, - { url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" }, - { url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" }, - { url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" }, - { url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" }, - { url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" }, - { url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" }, - { url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" }, - { url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" }, - { url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" }, - { url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" }, - { url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" }, - { url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" }, - { url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" }, - { url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" }, - { url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" }, - { url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" }, + { url = "https://files.pythonhosted.org/packages/a4/2d/63e37369c8e81a643afe54f76073b020f7b97ddbe698c5c944b51b0a2bc5/coverage-7.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4af3b01763909f477ea17c962e2cca8f39b350a4e46e3a30838b2c12e31b81b", size = 218842, upload-time = "2026-01-25T12:57:15.3Z" }, + { url = "https://files.pythonhosted.org/packages/57/06/86ce882a8d58cbcb3030e298788988e618da35420d16a8c66dac34f138d0/coverage-7.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36393bd2841fa0b59498f75466ee9bdec4f770d3254f031f23e8fd8e140ffdd2", size = 219360, upload-time = "2026-01-25T12:57:17.572Z" }, + { url = "https://files.pythonhosted.org/packages/cd/84/70b0eb1ee19ca4ef559c559054c59e5b2ae4ec9af61398670189e5d276e9/coverage-7.13.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cc7573518b7e2186bd229b1a0fe24a807273798832c27032c4510f47ffdb896", size = 246123, upload-time = "2026-01-25T12:57:19.087Z" }, + { url = "https://files.pythonhosted.org/packages/35/fb/05b9830c2e8275ebc031e0019387cda99113e62bb500ab328bb72578183b/coverage-7.13.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca9566769b69a5e216a4e176d54b9df88f29d750c5b78dbb899e379b4e14b30c", size = 247930, upload-time = "2026-01-25T12:57:20.929Z" }, + { url = "https://files.pythonhosted.org/packages/81/aa/3f37858ca2eed4f09b10ca3c6ddc9041be0a475626cd7fd2712f4a2d526f/coverage-7.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c9bdea644e94fd66d75a6f7e9a97bb822371e1fe7eadae2cacd50fcbc28e4dc", size = 249804, upload-time = "2026-01-25T12:57:22.904Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b3/c904f40c56e60a2d9678a5ee8df3d906d297d15fb8bec5756c3b0a67e2df/coverage-7.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5bd447332ec4f45838c1ad42268ce21ca87c40deb86eabd59888859b66be22a5", size = 246815, upload-time = "2026-01-25T12:57:24.314Z" }, + { url = "https://files.pythonhosted.org/packages/41/91/ddc1c5394ca7fd086342486440bfdd6b9e9bda512bf774599c7c7a0081e0/coverage-7.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7c79ad5c28a16a1277e1187cf83ea8dafdcc689a784228a7d390f19776db7c31", size = 247843, upload-time = "2026-01-25T12:57:26.544Z" }, + { url = "https://files.pythonhosted.org/packages/87/d2/cdff8f4cd33697883c224ea8e003e9c77c0f1a837dc41d95a94dd26aad67/coverage-7.13.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:76e06ccacd1fb6ada5d076ed98a8c6f66e2e6acd3df02819e2ee29fd637b76ad", size = 245850, upload-time = "2026-01-25T12:57:28.507Z" }, + { url = "https://files.pythonhosted.org/packages/f5/42/e837febb7866bf2553ab53dd62ed52f9bb36d60c7e017c55376ad21fbb05/coverage-7.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:49d49e9a5e9f4dc3d3dac95278a020afa6d6bdd41f63608a76fa05a719d5b66f", size = 246116, upload-time = "2026-01-25T12:57:30.16Z" }, + { url = "https://files.pythonhosted.org/packages/09/b1/4a3f935d7df154df02ff4f71af8d61298d713a7ba305d050ae475bfbdde2/coverage-7.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed2bce0e7bfa53f7b0b01c722da289ef6ad4c18ebd52b1f93704c21f116360c8", size = 246720, upload-time = "2026-01-25T12:57:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/538a6fd44c515f1c5197a3f078094cbaf2ce9f945df5b44e29d95c864bff/coverage-7.13.2-cp310-cp310-win32.whl", hash = "sha256:1574983178b35b9af4db4a9f7328a18a14a0a0ce76ffaa1c1bacb4cc82089a7c", size = 221465, upload-time = "2026-01-25T12:57:33.511Z" }, + { url = "https://files.pythonhosted.org/packages/5e/09/4b63a024295f326ec1a40ec8def27799300ce8775b1cbf0d33b1790605c4/coverage-7.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:a360a8baeb038928ceb996f5623a4cd508728f8f13e08d4e96ce161702f3dd99", size = 222397, upload-time = "2026-01-25T12:57:34.927Z" }, + { url = "https://files.pythonhosted.org/packages/6c/01/abca50583a8975bb6e1c59eff67ed8e48bb127c07dad5c28d9e96ccc09ec/coverage-7.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:060ebf6f2c51aff5ba38e1f43a2095e087389b1c69d559fde6049a4b0001320e", size = 218971, upload-time = "2026-01-25T12:57:36.953Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0e/b6489f344d99cd1e5b4d5e1be52dfd3f8a3dc5112aa6c33948da8cabad4e/coverage-7.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1ea8ca9db5e7469cd364552985e15911548ea5b69c48a17291f0cac70484b2e", size = 219473, upload-time = "2026-01-25T12:57:38.934Z" }, + { url = "https://files.pythonhosted.org/packages/17/11/db2f414915a8e4ec53f60b17956c27f21fb68fcf20f8a455ce7c2ccec638/coverage-7.13.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b780090d15fd58f07cf2011943e25a5f0c1c894384b13a216b6c86c8a8a7c508", size = 249896, upload-time = "2026-01-25T12:57:40.365Z" }, + { url = "https://files.pythonhosted.org/packages/80/06/0823fe93913663c017e508e8810c998c8ebd3ec2a5a85d2c3754297bdede/coverage-7.13.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:88a800258d83acb803c38175b4495d293656d5fac48659c953c18e5f539a274b", size = 251810, upload-time = "2026-01-25T12:57:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/61/dc/b151c3cc41b28cdf7f0166c5fa1271cbc305a8ec0124cce4b04f74791a18/coverage-7.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6326e18e9a553e674d948536a04a80d850a5eeefe2aae2e6d7cf05d54046c01b", size = 253920, upload-time = "2026-01-25T12:57:44.026Z" }, + { url = "https://files.pythonhosted.org/packages/2d/35/e83de0556e54a4729a2b94ea816f74ce08732e81945024adee46851c2264/coverage-7.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:59562de3f797979e1ff07c587e2ac36ba60ca59d16c211eceaa579c266c5022f", size = 250025, upload-time = "2026-01-25T12:57:45.624Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/af2eb9c3926ce3ea0d58a0d2516fcbdacf7a9fc9559fe63076beaf3f2596/coverage-7.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27ba1ed6f66b0e2d61bfa78874dffd4f8c3a12f8e2b5410e515ab345ba7bc9c3", size = 251612, upload-time = "2026-01-25T12:57:47.713Z" }, + { url = "https://files.pythonhosted.org/packages/26/62/5be2e25f3d6c711d23b71296f8b44c978d4c8b4e5b26871abfc164297502/coverage-7.13.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8be48da4d47cc68754ce643ea50b3234557cbefe47c2f120495e7bd0a2756f2b", size = 249670, upload-time = "2026-01-25T12:57:49.378Z" }, + { url = "https://files.pythonhosted.org/packages/b3/51/400d1b09a8344199f9b6a6fc1868005d766b7ea95e7882e494fa862ca69c/coverage-7.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2a47a4223d3361b91176aedd9d4e05844ca67d7188456227b6bf5e436630c9a1", size = 249395, upload-time = "2026-01-25T12:57:50.86Z" }, + { url = "https://files.pythonhosted.org/packages/e0/36/f02234bc6e5230e2f0a63fd125d0a2093c73ef20fdf681c7af62a140e4e7/coverage-7.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c6f141b468740197d6bd38f2b26ade124363228cc3f9858bd9924ab059e00059", size = 250298, upload-time = "2026-01-25T12:57:52.287Z" }, + { url = "https://files.pythonhosted.org/packages/b0/06/713110d3dd3151b93611c9cbfc65c15b4156b44f927fced49ac0b20b32a4/coverage-7.13.2-cp311-cp311-win32.whl", hash = "sha256:89567798404af067604246e01a49ef907d112edf2b75ef814b1364d5ce267031", size = 221485, upload-time = "2026-01-25T12:57:53.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/0c/3ae6255fa1ebcb7dec19c9a59e85ef5f34566d1265c70af5b2fc981da834/coverage-7.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:21dd57941804ae2ac7e921771a5e21bbf9aabec317a041d164853ad0a96ce31e", size = 222421, upload-time = "2026-01-25T12:57:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/b5/37/fabc3179af4d61d89ea47bd04333fec735cd5e8b59baad44fed9fc4170d7/coverage-7.13.2-cp311-cp311-win_arm64.whl", hash = "sha256:10758e0586c134a0bafa28f2d37dd2cdb5e4a90de25c0fc0c77dabbad46eca28", size = 221088, upload-time = "2026-01-25T12:57:57.41Z" }, + { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, + { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, + { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, + { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, + { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, + { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, + { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, + { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, + { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, + { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, + { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, + { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, + { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, + { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, + { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, + { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, + { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, + { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, + { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, + { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, + { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, + { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, + { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, + { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, + { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, + { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, + { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, + { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, + { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, + { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, + { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, + { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, + { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, ] [package.optional-dependencies] @@ -513,7 +513,7 @@ wheels = [ [[package]] name = "cyclopts" -version = "4.3.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -523,9 +523,9 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/0f/fe026df2ab8301e30a2b0bd425ff1462ad858fd4f991c1ac0389c2059c24/cyclopts-4.3.0.tar.gz", hash = "sha256:e95179cd0a959ce250ecfb2f0262a5996a92c1f9467bccad2f3d829e6833cef5", size = 151411, upload-time = "2025-11-25T02:59:33.572Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/e8/77a231ae531cf38765b75ddf27dae28bb5f70b41d8bb4f15ce1650e93f57/cyclopts-4.3.0-py3-none-any.whl", hash = "sha256:91a30b69faf128ada7cfeaefd7d9649dc222e8b2a8697f1fc99e4ee7b7ca44f3", size = 187184, upload-time = "2025-11-25T02:59:32.21Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, ] [[package]] @@ -584,11 +584,11 @@ wheels = [ [[package]] name = "docutils" -version = "0.22.3" +version = "0.22.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/02/111134bfeb6e6c7ac4c74594e39a59f6c0195dc4846afbeac3cba60f1927/docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd", size = 2290153, upload-time = "2025-11-06T02:35:55.655Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb", size = 633032, upload-time = "2025-11-06T02:35:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, ] [[package]] @@ -636,16 +636,16 @@ wheels = [ [[package]] name = "fakeredis" -version = "2.32.1" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "redis" }, { name = "sortedcontainers" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/14/b47b8471303af7deed7080290c14cff27a831fa47b38f45643e6bf889cee/fakeredis-2.32.1.tar.gz", hash = "sha256:dd8246db159f0b66a1ced7800c9d5ef07769e3d2fde44b389a57f2ce2834e444", size = 171582, upload-time = "2025-11-06T01:40:57.836Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/d2/c28f6909864bfdb7411bb8f39fabedb5a50da1cbd7da5a1a3a46dfea2eab/fakeredis-2.32.1-py3-none-any.whl", hash = "sha256:e80c8886db2e47ba784f7dfe66aad6cd2eab76093c6bfda50041e5bc890d46cf", size = 118964, upload-time = "2025-11-06T01:40:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, ] [package.optional-dependencies] @@ -668,7 +668,7 @@ wheels = [ [[package]] name = "fastapi" -version = "0.124.0" +version = "0.128.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -676,9 +676,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/48/9c/11969bd3e3bc4aa3a711f83dd3720239d3565a934929c74fc32f6c9f3638/fastapi-0.124.0.tar.gz", hash = "sha256:260cd178ad75e6d259991f2fd9b0fee924b224850079df576a3ba604ce58f4e6", size = 357623, upload-time = "2025-12-06T13:11:35.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/29/9e1e82e16e9a1763d3b55bfbe9b2fa39d7175a1fd97685c482fa402e111d/fastapi-0.124.0-py3-none-any.whl", hash = "sha256:91596bdc6dde303c318f06e8d2bc75eafb341fc793a0c9c92c0bc1db1ac52480", size = 112505, upload-time = "2025-12-06T13:11:34.392Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, ] [[package]] @@ -688,10 +688,13 @@ dependencies = [ { name = "authlib" }, { name = "cyclopts" }, { name = "exceptiongroup" }, + { name = "fakeredis", extra = ["lua"] }, { name = "httpx" }, + { name = "jsonref" }, { name = "jsonschema-path" }, { name = "mcp" }, { name = "openapi-pydantic" }, + { name = "packaging" }, { name = "platformdirs" }, { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, { name = "pydantic", extra = ["email"] }, @@ -717,8 +720,8 @@ dev = [ { name = "fastapi" }, { name = "fastmcp", extra = ["anthropic", "openai"] }, { name = "inline-snapshot", extra = ["dirty-equals"] }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pdbpp" }, { name = "prek" }, { name = "psutil" }, @@ -744,15 +747,18 @@ requires-dist = [ { name = "authlib", specifier = ">=1.6.5" }, { name = "cyclopts", specifier = ">=4.0.0" }, { name = "exceptiongroup", specifier = ">=1.2.2" }, + { name = "fakeredis", extras = ["lua"], specifier = "<2.35.0" }, { name = "httpx", specifier = ">=0.28.1" }, + { name = "jsonref", specifier = ">=1.1.0" }, { name = "jsonschema-path", specifier = ">=0.3.4" }, - { name = "mcp", specifier = ">=1.24.0" }, + { name = "mcp", specifier = ">=1.24.0,<2.0" }, { name = "openai", marker = "extra == 'openai'", specifier = ">=1.102.0" }, { name = "openapi-pydantic", specifier = ">=0.5.1" }, + { name = "packaging", specifier = ">=20.0" }, { name = "platformdirs", specifier = ">=4.0.0" }, { name = "py-key-value-aio", extras = ["disk", "keyring", "memory"], specifier = ">=0.3.0,<0.4.0" }, { name = "pydantic", extras = ["email"], specifier = ">=2.11.7" }, - { name = "pydocket", specifier = ">=0.15.5" }, + { name = "pydocket", specifier = ">=0.17.2" }, { name = "pyperclip", specifier = ">=1.9.0" }, { name = "python-dotenv", specifier = ">=1.1.0" }, { name = "rich", specifier = ">=13.9.4" }, @@ -844,14 +850,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] [[package]] @@ -886,7 +892,7 @@ dirty-equals = [ [[package]] name = "ipython" -version = "8.37.0" +version = "8.38.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.11'", @@ -904,14 +910,14 @@ dependencies = [ { name = "traitlets", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, ] [[package]] name = "ipython" -version = "9.8.0" +version = "9.9.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", @@ -929,9 +935,9 @@ dependencies = [ { name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/51/a703c030f4928646d390b4971af4938a1b10c9dfce694f0d99a0bb073cb2/ipython-9.8.0.tar.gz", hash = "sha256:8e4ce129a627eb9dd221c41b1d2cdaed4ef7c9da8c17c63f6f578fe231141f83", size = 4424940, upload-time = "2025-12-03T10:18:24.353Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/dd/fb08d22ec0c27e73c8bc8f71810709870d51cadaf27b7ddd3f011236c100/ipython-9.9.0.tar.gz", hash = "sha256:48fbed1b2de5e2c7177eefa144aba7fcb82dac514f09b57e2ac9da34ddb54220", size = 4425043, upload-time = "2026-01-05T12:36:46.233Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/df/8ee1c5dd1e3308b5d5b2f2dfea323bb2f3827da8d654abb6642051199049/ipython-9.8.0-py3-none-any.whl", hash = "sha256:ebe6d1d58d7d988fbf23ff8ff6d8e1622cfdb194daf4b7b73b792c4ec3b85385", size = 621374, upload-time = "2025-12-03T10:18:22.335Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.0-py3-none-any.whl", hash = "sha256:b457fe9165df2b84e8ec909a97abcf2ed88f565970efba16b1f7229c283d252b", size = 621431, upload-time = "2026-01-05T12:36:44.669Z" }, ] [[package]] @@ -960,26 +966,26 @@ wheels = [ [[package]] name = "jaraco-context" -version = "6.0.1" +version = "6.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" }, + { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, ] [[package]] name = "jaraco-functools" -version = "4.3.0" +version = "4.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294", size = 19755, upload-time = "2025-08-18T20:05:09.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", size = 10408, upload-time = "2025-08-18T20:05:08.69Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, ] [[package]] @@ -1100,9 +1106,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" }, ] +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, +] + [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1110,9 +1125,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [[package]] @@ -1260,7 +1275,7 @@ wheels = [ [[package]] name = "mcp" -version = "1.24.0" +version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1278,9 +1293,9 @@ dependencies = [ { name = "typing-inspection" }, { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/db9ae5ab1fcdd9cd2bcc7ca3b7361b712e30590b64d5151a31563af8f82d/mcp-1.24.0.tar.gz", hash = "sha256:aeaad134664ce56f2721d1abf300666a1e8348563f4d3baff361c3b652448efc", size = 604375, upload-time = "2025-12-12T14:19:38.205Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/0d/5cf14e177c8ae655a2fd9324a6ef657ca4cafd3fc2201c87716055e29641/mcp-1.24.0-py3-none-any.whl", hash = "sha256:db130e103cc50ddc3dffc928382f33ba3eaef0b711f7a87c05e7ded65b1ca062", size = 232896, upload-time = "2025-12-12T14:19:36.14Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, ] [[package]] @@ -1303,7 +1318,7 @@ wheels = [ [[package]] name = "openai" -version = "2.9.0" +version = "2.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -1315,9 +1330,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/48/516290f38745cc1e72856f50e8afed4a7f9ac396a5a18f39e892ab89dfc2/openai-2.9.0.tar.gz", hash = "sha256:b52ec65727fc8f1eed2fbc86c8eac0998900c7ef63aa2eb5c24b69717c56fa5f", size = 608202, upload-time = "2025-12-04T18:15:09.01Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/f4/4690ecb5d70023ce6bfcfeabfe717020f654bde59a775058ec6ac4692463/openai-2.15.0.tar.gz", hash = "sha256:42eb8cbb407d84770633f31bf727d4ffb4138711c670565a41663d9439174fba", size = 627383, upload-time = "2026-01-09T22:10:08.603Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/fd/ae2da789cd923dd033c99b8d544071a827c92046b150db01cfa5cea5b3fd/openai-2.9.0-py3-none-any.whl", hash = "sha256:0d168a490fbb45630ad508a6f3022013c155a68fd708069b6a1a01a5e8f0ffad", size = 1030836, upload-time = "2025-12-04T18:15:07.063Z" }, + { url = "https://files.pythonhosted.org/packages/b5/df/c306f7375d42bafb379934c2df4c2fa3964656c8c782bac75ee10c102818/openai-2.15.0-py3-none-any.whl", hash = "sha256:6ae23b932cd7230f7244e52954daa6602716d6b9bf235401a107af731baea6c3", size = 1067879, upload-time = "2026-01-09T22:10:06.446Z" }, ] [[package]] @@ -1334,80 +1349,24 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.39.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/0b/e5428c009d4d9af0515b0a8371a8aaae695371af291f45e702f7969dce6b/opentelemetry_api-1.39.0.tar.gz", hash = "sha256:6130644268c5ac6bdffaf660ce878f10906b3e789f7e2daa5e169b047a2933b9", size = 65763, upload-time = "2025-12-03T13:19:56.378Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/85/d831a9bc0a9e0e1a304ff3d12c1489a5fbc9bf6690a15dcbdae372bbca45/opentelemetry_api-1.39.0-py3-none-any.whl", hash = "sha256:3c3b3ca5c5687b1b5b37e5c5027ff68eacea8675241b29f13110a8ffbb8f0459", size = 66357, upload-time = "2025-12-03T13:19:33.043Z" }, -] - -[[package]] -name = "opentelemetry-exporter-prometheus" -version = "0.60b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/da/8b81ff9d045fae7cac1e8bbf7ad59bf113d1008e483fc03e2cd3a0e620a3/opentelemetry_exporter_prometheus-0.60b0.tar.gz", hash = "sha256:c6ae33e52cdd1dbfed1f7436935df94eb03c725b57322026d04e6fbc37108e6e", size = 14975, upload-time = "2025-12-03T13:20:01.826Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/18/18b662a6ecb8252db9e7457fd3c836729bf28b055b60505cbd4763ea9300/opentelemetry_exporter_prometheus-0.60b0-py3-none-any.whl", hash = "sha256:4f616397040257fae4c5e5272b57b47c13372e3b7f0f2db2427fd4dbe69c60b5", size = 13017, upload-time = "2025-12-03T13:19:40.866Z" }, -] - -[[package]] -name = "opentelemetry-instrumentation" -version = "0.60b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "packaging" }, - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/3c/bd53dbb42eff93d18e3047c7be11224aa9966ce98ac4cc5bfb860a32c95a/opentelemetry_instrumentation-0.60b0.tar.gz", hash = "sha256:4e9fec930f283a2677a2217754b40aaf9ef76edae40499c165bc7f1d15366a74", size = 31707, upload-time = "2025-12-03T13:22:00.352Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/7b/5b5b9f8cfe727a28553acf9cd287b1d7f706f5c0a00d6e482df55b169483/opentelemetry_instrumentation-0.60b0-py3-none-any.whl", hash = "sha256:aaafa1483543a402819f1bdfb06af721c87d60dd109501f9997332862a35c76a", size = 33096, upload-time = "2025-12-03T13:20:51.785Z" }, -] - -[[package]] -name = "opentelemetry-sdk" -version = "1.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "opentelemetry-semantic-conventions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/51/e3/7cd989003e7cde72e0becfe830abff0df55c69d237ee7961a541e0167833/opentelemetry_sdk-1.39.0.tar.gz", hash = "sha256:c22204f12a0529e07aa4d985f1bca9d6b0e7b29fe7f03e923548ae52e0e15dde", size = 171322, upload-time = "2025-12-03T13:20:09.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/b4/2adc8bc83eb1055ecb592708efb6f0c520cc2eb68970b02b0f6ecda149cf/opentelemetry_sdk-1.39.0-py3-none-any.whl", hash = "sha256:90cfb07600dfc0d2de26120cebc0c8f27e69bf77cd80ef96645232372709a514", size = 132413, upload-time = "2025-12-03T13:19:51.364Z" }, -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.60b0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-api" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/0e/176a7844fe4e3cb5de604212094dffaed4e18b32f1c56b5258bcbcba85c2/opentelemetry_semantic_conventions-0.60b0.tar.gz", hash = "sha256:227d7aa73cbb8a2e418029d6b6465553aa01cf7e78ec9d0bc3255c7b3ac5bf8f", size = 137935, upload-time = "2025-12-03T13:20:12.395Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/56/af0306666f91bae47db14d620775604688361f0f76a872e0005277311131/opentelemetry_semantic_conventions-0.60b0-py3-none-any.whl", hash = "sha256:069530852691136018087b52688857d97bba61cd641d0f8628d2d92788c4f78a", size = 219981, upload-time = "2025-12-03T13:19:53.585Z" }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, ] [[package]] name = "packaging" -version = "25.0" +version = "26.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] @@ -1439,15 +1398,15 @@ wheels = [ [[package]] name = "pdbpp" -version = "0.11.7" +version = "0.12.0.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fancycompleter" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/4c/118ef9534ac0632859b48c305d8c5dc9d6f963564fdfa66bc785c560247c/pdbpp-0.11.7.tar.gz", hash = "sha256:cb6604ac31a35ed0f2a29650a8c022b26284620be3e01cfd41b683b91da1ff14", size = 76026, upload-time = "2025-07-18T09:36:02.781Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/23/0bd339679059fb289e0752671b5eac63a472742d1de0035e13a27429e500/pdbpp-0.12.0.post1.tar.gz", hash = "sha256:cace9951d7414fe651141d240ab20e4509c8f97d20f7b142386d1bd1bb182d18", size = 75765, upload-time = "2026-01-19T10:56:55.026Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/e9/704bbc08aace64fee536e4c2c20f63f64f6fdbad72938c5ed46c9723a9f1/pdbpp-0.11.7-py3-none-any.whl", hash = "sha256:51916b63693898cf4881b36b4501c83947758d73f582f1f84893662b163bdb75", size = 30545, upload-time = "2025-07-18T09:36:01.478Z" }, + { url = "https://files.pythonhosted.org/packages/28/f2/20fa391af27da4ef421832fb7d554748786efcc3b0da69a51e2ca66043d0/pdbpp-0.12.0.post1-py3-none-any.whl", hash = "sha256:8c62acd6adaa02f620d7d9cff689208a28019b6c05e6b894318f313e61fb1dc7", size = 30661, upload-time = "2026-01-19T10:56:53.638Z" }, ] [[package]] @@ -1482,37 +1441,35 @@ wheels = [ [[package]] name = "prek" -version = "0.2.21" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/b6/8d87842c37c84946bc7b4910bf1604d66cabc755b92d4e67db7bfea660fc/prek-0.2.21.tar.gz", hash = "sha256:cf0e1f99c3ecbccf04656746f5e4e1048100904f25673a33bdea61cc01ecb142", size = 373714, upload-time = "2025-12-09T05:21:59.75Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/1e/6c23d3470145be1d6ff29d93f2a521864788827d22e509e2b978eb5bb4cb/prek-0.3.0.tar.gz", hash = "sha256:e70f16bbaf2803e490b866cfa997ea5cc46e7ada55d61f0cdd84bc90b8d5ca7f", size = 316063, upload-time = "2026-01-22T04:00:01.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/bb/b493656c5c99a2c7b4e9d6b8ad5a0f68b6e7578e12cc13ff4c9c30d7c41c/prek-0.2.21-py3-none-linux_armv6l.whl", hash = "sha256:8e534e750093314129780dd8f50b2e4123d4a057223820c76c3ceae6e7474203", size = 4794694, upload-time = "2025-12-09T05:21:52.429Z" }, - { url = "https://files.pythonhosted.org/packages/16/cd/a9401ca52681b05ec28290fbc92b5346edcf72728fbdad3a29a5ea115599/prek-0.2.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c7b19c3ed6e62e9fdab22941b8fa42c41381105f895db57931613aaa60bc31c", size = 4886559, upload-time = "2025-12-09T05:21:41.966Z" }, - { url = "https://files.pythonhosted.org/packages/99/7e/541d7f09d19546144f2b01d5cfa06c3a238b856d4d7c89685ae2ac3c39bc/prek-0.2.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fcedd889c97ef43785e55061004def57cefa0c5551b6bf8cd111ac006d429b4a", size = 4609524, upload-time = "2025-12-09T05:21:45.16Z" }, - { url = "https://files.pythonhosted.org/packages/40/20/4903ab2142b8def1a4018c092a5aab48121cf77c5ea311060fc87ac6efd3/prek-0.2.21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:e77a6fc39618af192b59342912d5cbac033b9ad4d8e6223f4c9a790973bdada4", size = 4808079, upload-time = "2025-12-09T05:21:56.967Z" }, - { url = "https://files.pythonhosted.org/packages/a3/90/20529ce0cf788038ae9e47b6acb0a07f5e2d4b1f03a5bfa3faf27d297872/prek-0.2.21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b718ce075eb972add76554862cab812085439c5e6f17421b08754a3c031d5e2", size = 4718210, upload-time = "2025-12-09T05:21:38.373Z" }, - { url = "https://files.pythonhosted.org/packages/92/29/4dea1c14eee78a624233285b681686289fc47aae0dc8d87d7c2c7c1175a1/prek-0.2.21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0fecb2f1cfa0f1cf12e591fc882a8528c450bb0307e056401ec3007b384514a", size = 5029090, upload-time = "2025-12-09T05:21:51.088Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a9/5dd17876de86d4e537ef5bb802393eecb06afced3daa4d284afdf1330c9c/prek-0.2.21-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:673b5cc78603194abb55c96263b42b5b3663e8cb4cede49c528a7586807203a9", size = 5446474, upload-time = "2025-12-09T05:21:43.68Z" }, - { url = "https://files.pythonhosted.org/packages/d7/22/287efe85770e4f75a09a5006df2141d4aed79d300d08b175bd8865521e41/prek-0.2.21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fe37f220782f436a53b38e1efb863a27f300a51f13245a37b445aec6ffde0db", size = 5395331, upload-time = "2025-12-09T05:21:37.112Z" }, - { url = "https://files.pythonhosted.org/packages/72/51/0c763cabc78b14e08f315643f1e29b5791f7f104ca2dd39657c5891cdcb6/prek-0.2.21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:66e7341359ac1069f74fba5f579bace7e240aaca93bfc2fbc8551537316660e3", size = 5491119, upload-time = "2025-12-09T05:21:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/40/a4/947051a6e53232c567651b2be2ef546c53ce17ca517e2bf0773732f5e2a9/prek-0.2.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f82ccf1dbf36ff1d7128542165d885818c762c5ceb042b13937d81fc87b053a6", size = 5070460, upload-time = "2025-12-09T05:21:53.971Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f0/0c87332101d94c046f1c82493b50164017176594f50e81493c3c6556788d/prek-0.2.21-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:3abd7e379a0870962f76b6def776cb65aa678aab6083f8babd9c20f7064755d0", size = 4815558, upload-time = "2025-12-09T05:21:58.591Z" }, - { url = "https://files.pythonhosted.org/packages/80/a7/4adfbec77348cc6e5aee906d9f947a67cb51916e95910d4eeffc20cb9fbe/prek-0.2.21-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48d8dc32b285505972fe0715c3c4f97ceb78db4c6c7d2e95f259c14821e07593", size = 4829289, upload-time = "2025-12-09T05:21:46.437Z" }, - { url = "https://files.pythonhosted.org/packages/d0/5d/e08a3ced6e2267cb9d8ab3f7b52f74ada86041ea2904c4079229244e4a37/prek-0.2.21-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:2690bbce9526d320a7a39474236a9c8960069ce2b0434e8ff11fa62581ee528b", size = 4704742, upload-time = "2025-12-09T05:22:02.059Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5a/6b39bdab771de405fccd82f4e349f01dc81c60bbbbabcace42bbad03d070/prek-0.2.21-py3-none-musllinux_1_1_i686.whl", hash = "sha256:2a44ebcc16a5fddd643714757742440279abfbab79c45cac90c087db527ed8f6", size = 4918736, upload-time = "2025-12-09T05:21:48.103Z" }, - { url = "https://files.pythonhosted.org/packages/74/06/26185f018ed2a12995f81d6390d0b73749170fd7d879c2d2979821ef692a/prek-0.2.21-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:419ea476daa576b6b89564add55fb63b3432f0f85f3dcd1b3640cd9a70683ba4", size = 5186798, upload-time = "2025-12-09T05:22:00.8Z" }, - { url = "https://files.pythonhosted.org/packages/85/de/07a4cfb60b8af0e83ec20bcb68d388e23e72954d6d76cd974c9fc1e5adbc/prek-0.2.21-py3-none-win32.whl", hash = "sha256:5d169c8f99e4b6c0488d507dda4b20025c4f4c509aa9da26c3e57f135d6590e6", size = 4576863, upload-time = "2025-12-09T05:21:49.329Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bd/1db02e1c7a6dd35d066719ec02e0a79be69d922f19f9f3b1887942365fbe/prek-0.2.21-py3-none-win_amd64.whl", hash = "sha256:468fcc2241e312552d3926ede0ddf014fb99c1826b0d3ed5bbc2d2b8a7b4af14", size = 5265929, upload-time = "2025-12-09T05:21:35.4Z" }, - { url = "https://files.pythonhosted.org/packages/8e/0a/f9568f33edd1a42e5f5211f3ea84c2bd4d576acfe7abfbe6dcfeda191835/prek-0.2.21-py3-none-win_arm64.whl", hash = "sha256:3134e192508b0efd28011dfe2c8abb05dc1ef22030147f6b0433a3a6b7912309", size = 4931528, upload-time = "2025-12-09T05:21:55.419Z" }, + { url = "https://files.pythonhosted.org/packages/84/49/469219c19bb00db678806f79fc084ac1ce9952004a183a798db26f6df22b/prek-0.3.0-py3-none-linux_armv6l.whl", hash = "sha256:7e5d40b22deff23e36f7ad91e24b8e62edf32f30f6dad420459f7ec7188233c3", size = 4317493, upload-time = "2026-01-22T03:59:51.769Z" }, + { url = "https://files.pythonhosted.org/packages/87/9f/f7afc49cc0fd92d1ba492929dc1573cb7004d09b61341aa6ee32a5288657/prek-0.3.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6712b58cbb5a7db0aaef180c489ce9f3462e0293d54e54baeedd75fc0d9d8c28", size = 4323961, upload-time = "2026-01-22T03:59:56.92Z" }, + { url = "https://files.pythonhosted.org/packages/42/94/ba36dc29e71d476bf71c3bac2b0c89cfcfc4b8973a0a6b20728f429f4560/prek-0.3.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f2c446fd9012a98c5690b4badf3f7dfb8d424cf0c6798a2d08ee56511f0a670", size = 3970121, upload-time = "2026-01-22T03:59:55.722Z" }, + { url = "https://files.pythonhosted.org/packages/b5/93/6131dd9f6cde3d72815b978b766de21b2ac9cc15fc38f5c22267cc7e574d/prek-0.3.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:10f3da7cda2397f7d2f3ff7f2be0d7486c15d4941f7568095b7168e57a9c88c5", size = 4307430, upload-time = "2026-01-22T03:59:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/6f/08/7c55a765d96028d38dc984e66a096a969d80e56f66a47801acc86dede856/prek-0.3.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f747bb4a4322fea35d548cd2c1bd24477f56ed009f3d62a2b97ecbfc88096ac", size = 4238032, upload-time = "2026-01-22T04:00:02.606Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a7/59d9bf902b749c8a0cef9e8ac073cc5c886634cd09404c00af4a76470b3b/prek-0.3.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40bd61f11d8caabc0e2a5d4c326639d6ff558b580ef4388aabec293ddb5afd35", size = 4493295, upload-time = "2026-01-22T03:59:45.964Z" }, + { url = "https://files.pythonhosted.org/packages/08/dc/902b2e4ddff59ad001ddc2cda3b47e457ab1ee811698a4002b3e4f84faf1/prek-0.3.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d096b5e273d17a1300b20a7101a9e5a624a8104825eb59659776177f7fccea1", size = 5033370, upload-time = "2026-01-22T03:59:44.806Z" }, + { url = "https://files.pythonhosted.org/packages/15/cd/277a3d2768b80bb1ff3c2ea8378687bb4c527d88a8b543bf6f364f8a0dc9/prek-0.3.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df39face5f1298851fbae495267ddf60f1694ea594ed5c6cdb88bdd6de14f6a4", size = 4549792, upload-time = "2026-01-22T03:59:41.518Z" }, + { url = "https://files.pythonhosted.org/packages/26/21/53aeabd3822ef7fa350aac66d099d4d97b05e8383a2df35499229389a642/prek-0.3.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9462f80a576d661490aa058d4493a991a34c7532dea76b7b004a17c8bc6b80f2", size = 4323158, upload-time = "2026-01-22T03:59:54.284Z" }, + { url = "https://files.pythonhosted.org/packages/27/c2/3a7392b0e7fd07e339d89701b49b12a89d85256a57279877195028215957/prek-0.3.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:33d3fa40eecf996ed14bab2d006c39d21ae344677d62599963efd9b27936558e", size = 4344632, upload-time = "2026-01-22T04:00:03.71Z" }, + { url = "https://files.pythonhosted.org/packages/71/89/8254ac981d75d0ce2826bcac74fed901540d629cb2d9f4d73ce62f8ce843/prek-0.3.0-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:d8c6abfd53a23718afdf4e6107418db1d74c5d904e9b7ec7900e285f8da90723", size = 4216608, upload-time = "2026-01-22T03:59:58.527Z" }, + { url = "https://files.pythonhosted.org/packages/20/f5/854d57d89376fac577ee647a1dba1b87e27b2baeca7edc3d40295adeb7c8/prek-0.3.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:eb4c80c3e7c0e16bf307947809112bfef3715a1b83c2b03f5937707934635617", size = 4371174, upload-time = "2026-01-22T03:59:53.088Z" }, + { url = "https://files.pythonhosted.org/packages/03/38/8927619411da8d3f189415c452ec7a463f09dea69e272888723f37b4b18f/prek-0.3.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:602bcce070c50900167acd89dcdf95d27894412f8a7b549c8eb66de612a99653", size = 4659113, upload-time = "2026-01-22T03:59:43.166Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4d/16baeef633b8b230dde878b858c0e955149c860feef518b5eb5aac640eec/prek-0.3.0-py3-none-win32.whl", hash = "sha256:a69229365ce33c68c05db7ae73ad1ef8bc7f0914ab3bc484ab7781256bcdfb7a", size = 3937103, upload-time = "2026-01-22T03:59:48.719Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f2/c7395b4afd1bba32cad2b24c30fd7781e94c1e41137348cd150bbef001d6/prek-0.3.0-py3-none-win_amd64.whl", hash = "sha256:a0379afd8d31bd5da6ee8977820fdb3c30601bed836b39761e6f605451dbccaa", size = 4290763, upload-time = "2026-01-22T03:59:59.938Z" }, + { url = "https://files.pythonhosted.org/packages/df/83/97ed76ab5470025992cd50cb1ebdeb21fcf6c25459f9ffc49ac7bf040cf4/prek-0.3.0-py3-none-win_arm64.whl", hash = "sha256:82e2c64f75dc1ea6f2023f4322500eb8da5d0557baf06c88677bddf163e1542a", size = 4041580, upload-time = "2026-01-22T03:59:50.082Z" }, ] [[package]] name = "prometheus-client" -version = "0.23.1" +version = "0.24.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/53/3edb5d68ecf6b38fcbcc1ad28391117d2a322d9a1a3eff04bfdb184d8c3b/prometheus_client-0.23.1.tar.gz", hash = "sha256:6ae8f9081eaaaf153a2e959d2e6c4f4fb57b12ef76c8c7980202f1e57b48b2ce", size = 80481, upload-time = "2025-09-18T20:47:25.043Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/db/14bafcb4af2139e046d03fd00dea7873e48eafe18b7d2797e73d6681f210/prometheus_client-0.23.1-py3-none-any.whl", hash = "sha256:dd1913e6e76b59cfe44e7a4b83e01afc9873c1bdfd2ed8739f1e76aeca115f99", size = 61145, upload-time = "2025-09-18T20:47:23.875Z" }, + { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, ] [[package]] @@ -1529,28 +1486,30 @@ wheels = [ [[package]] name = "psutil" -version = "7.1.3" +version = "7.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/cb/09e5184fb5fc0358d110fc3ca7f6b1d033800734d34cac10f4136cfac10e/psutil-7.2.1.tar.gz", hash = "sha256:f7583aec590485b43ca601dd9cea0dcd65bd7bb21d30ef4ddbf4ea6b5ed1bdd3", size = 490253, upload-time = "2025-12-29T08:26:00.169Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/93/0c49e776b8734fef56ec9c5c57f923922f2cf0497d62e0f419465f28f3d0/psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc", size = 239751, upload-time = "2025-11-02T12:25:58.161Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8d/b31e39c769e70780f007969815195a55c81a63efebdd4dbe9e7a113adb2f/psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0", size = 240368, upload-time = "2025-11-02T12:26:00.491Z" }, - { url = "https://files.pythonhosted.org/packages/62/61/23fd4acc3c9eebbf6b6c78bcd89e5d020cfde4acf0a9233e9d4e3fa698b4/psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7", size = 287134, upload-time = "2025-11-02T12:26:02.613Z" }, - { url = "https://files.pythonhosted.org/packages/30/1c/f921a009ea9ceb51aa355cb0cc118f68d354db36eae18174bab63affb3e6/psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251", size = 289904, upload-time = "2025-11-02T12:26:05.207Z" }, - { url = "https://files.pythonhosted.org/packages/a6/82/62d68066e13e46a5116df187d319d1724b3f437ddd0f958756fc052677f4/psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa", size = 249642, upload-time = "2025-11-02T12:26:07.447Z" }, - { url = "https://files.pythonhosted.org/packages/df/ad/c1cd5fe965c14a0392112f68362cfceb5230819dbb5b1888950d18a11d9f/psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee", size = 245518, upload-time = "2025-11-02T12:26:09.719Z" }, - { url = "https://files.pythonhosted.org/packages/2e/bb/6670bded3e3236eb4287c7bcdc167e9fae6e1e9286e437f7111caed2f909/psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353", size = 239843, upload-time = "2025-11-02T12:26:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/b8/66/853d50e75a38c9a7370ddbeefabdd3d3116b9c31ef94dc92c6729bc36bec/psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b", size = 240369, upload-time = "2025-11-02T12:26:14.358Z" }, - { url = "https://files.pythonhosted.org/packages/41/bd/313aba97cb5bfb26916dc29cf0646cbe4dd6a89ca69e8c6edce654876d39/psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9", size = 288210, upload-time = "2025-11-02T12:26:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/76e3c06e760927a0cfb5705eb38164254de34e9bd86db656d4dbaa228b04/psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f", size = 291182, upload-time = "2025-11-02T12:26:18.848Z" }, - { url = "https://files.pythonhosted.org/packages/0f/1d/5774a91607035ee5078b8fd747686ebec28a962f178712de100d00b78a32/psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7", size = 250466, upload-time = "2025-11-02T12:26:21.183Z" }, - { url = "https://files.pythonhosted.org/packages/00/ca/e426584bacb43a5cb1ac91fae1937f478cd8fbe5e4ff96574e698a2c77cd/psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264", size = 245756, upload-time = "2025-11-02T12:26:23.148Z" }, - { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" }, - { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" }, - { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" }, - { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" }, + { url = "https://files.pythonhosted.org/packages/77/8e/f0c242053a368c2aa89584ecd1b054a18683f13d6e5a318fc9ec36582c94/psutil-7.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9f33bb525b14c3ea563b2fd521a84d2fa214ec59e3e6a2858f78d0844dd60d", size = 129624, upload-time = "2025-12-29T08:26:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/a58a4968f8990617decee234258a2b4fc7cd9e35668387646c1963e69f26/psutil-7.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:81442dac7abfc2f4f4385ea9e12ddf5a796721c0f6133260687fec5c3780fa49", size = 130132, upload-time = "2025-12-29T08:26:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/db/6d/ed44901e830739af5f72a85fa7ec5ff1edea7f81bfbf4875e409007149bd/psutil-7.2.1-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea46c0d060491051d39f0d2cff4f98d5c72b288289f57a21556cc7d504db37fc", size = 180612, upload-time = "2025-12-29T08:26:08.276Z" }, + { url = "https://files.pythonhosted.org/packages/c7/65/b628f8459bca4efbfae50d4bf3feaab803de9a160b9d5f3bd9295a33f0c2/psutil-7.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35630d5af80d5d0d49cfc4d64c1c13838baf6717a13effb35869a5919b854cdf", size = 183201, upload-time = "2025-12-29T08:26:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/fb/23/851cadc9764edcc18f0effe7d0bf69f727d4cf2442deb4a9f78d4e4f30f2/psutil-7.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:923f8653416604e356073e6e0bccbe7c09990acef442def2f5640dd0faa9689f", size = 139081, upload-time = "2025-12-29T08:26:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/59/82/d63e8494ec5758029f31c6cb06d7d161175d8281e91d011a4a441c8a43b5/psutil-7.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cfbe6b40ca48019a51827f20d830887b3107a74a79b01ceb8cc8de4ccb17b672", size = 134767, upload-time = "2025-12-29T08:26:14.528Z" }, + { url = "https://files.pythonhosted.org/packages/05/c2/5fb764bd61e40e1fe756a44bd4c21827228394c17414ade348e28f83cd79/psutil-7.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:494c513ccc53225ae23eec7fe6e1482f1b8a44674241b54561f755a898650679", size = 129716, upload-time = "2025-12-29T08:26:16.017Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d2/935039c20e06f615d9ca6ca0ab756cf8408a19d298ffaa08666bc18dc805/psutil-7.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fce5f92c22b00cdefd1645aa58ab4877a01679e901555067b1bd77039aa589f", size = 130133, upload-time = "2025-12-29T08:26:18.009Z" }, + { url = "https://files.pythonhosted.org/packages/77/69/19f1eb0e01d24c2b3eacbc2f78d3b5add8a89bf0bb69465bc8d563cc33de/psutil-7.2.1-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93f3f7b0bb07711b49626e7940d6fe52aa9940ad86e8f7e74842e73189712129", size = 181518, upload-time = "2025-12-29T08:26:20.241Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6d/7e18b1b4fa13ad370787626c95887b027656ad4829c156bb6569d02f3262/psutil-7.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d34d2ca888208eea2b5c68186841336a7f5e0b990edec929be909353a202768a", size = 184348, upload-time = "2025-12-29T08:26:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/60/1672114392dd879586d60dd97896325df47d9a130ac7401318005aab28ec/psutil-7.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2ceae842a78d1603753561132d5ad1b2f8a7979cb0c283f5b52fb4e6e14b1a79", size = 140400, upload-time = "2025-12-29T08:26:23.993Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7b/d0e9d4513c46e46897b46bcfc410d51fc65735837ea57a25170f298326e6/psutil-7.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:08a2f175e48a898c8eb8eace45ce01777f4785bc744c90aa2cc7f2fa5462a266", size = 135430, upload-time = "2025-12-29T08:26:25.999Z" }, + { url = "https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2e953fcfaedcfbc952b44744f22d16575d3aa78eb4f51ae74165b4e96e55f42", size = 128137, upload-time = "2025-12-29T08:26:27.759Z" }, + { url = "https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:05cc68dbb8c174828624062e73078e7e35406f4ca2d0866c272c2410d8ef06d1", size = 128947, upload-time = "2025-12-29T08:26:29.548Z" }, + { url = "https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e38404ca2bb30ed7267a46c02f06ff842e92da3bb8c5bfdadbd35a5722314d8", size = 154694, upload-time = "2025-12-29T08:26:32.147Z" }, + { url = "https://files.pythonhosted.org/packages/06/e4/b751cdf839c011a9714a783f120e6a86b7494eb70044d7d81a25a5cd295f/psutil-7.2.1-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab2b98c9fc19f13f59628d94df5cc4cc4844bc572467d113a8b517d634e362c6", size = 156136, upload-time = "2025-12-29T08:26:34.079Z" }, + { url = "https://files.pythonhosted.org/packages/44/ad/bbf6595a8134ee1e94a4487af3f132cef7fce43aef4a93b49912a48c3af7/psutil-7.2.1-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f78baafb38436d5a128f837fab2d92c276dfb48af01a240b861ae02b2413ada8", size = 148108, upload-time = "2025-12-29T08:26:36.225Z" }, + { url = "https://files.pythonhosted.org/packages/1c/15/dd6fd869753ce82ff64dcbc18356093471a5a5adf4f77ed1f805d473d859/psutil-7.2.1-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:99a4cd17a5fdd1f3d014396502daa70b5ec21bf4ffe38393e152f8e449757d67", size = 147402, upload-time = "2025-12-29T08:26:39.21Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl", hash = "sha256:b1b0671619343aa71c20ff9767eced0483e4fc9e1f489d50923738caf6a03c17", size = 136938, upload-time = "2025-12-29T08:26:41.036Z" }, + { url = "https://files.pythonhosted.org/packages/3e/73/2ce007f4198c80fcf2cb24c169884f833fe93fbc03d55d302627b094ee91/psutil-7.2.1-cp37-abi3-win_arm64.whl", hash = "sha256:0d67c1822c355aa6f7314d92018fb4268a76668a536f133599b91edd48759442", size = 133836, upload-time = "2025-12-29T08:26:43.086Z" }, ] [[package]] @@ -1614,11 +1573,11 @@ wheels = [ [[package]] name = "pycparser" -version = "2.23" +version = "3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, ] [[package]] @@ -1775,26 +1734,25 @@ wheels = [ [[package]] name = "pydocket" -version = "0.15.5" +version = "0.17.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "fakeredis", extra = ["lua"] }, { name = "opentelemetry-api" }, - { name = "opentelemetry-exporter-prometheus" }, - { name = "opentelemetry-instrumentation" }, { name = "prometheus-client" }, { name = "py-key-value-aio", extra = ["memory", "redis"] }, { name = "python-json-logger" }, { name = "redis" }, { name = "rich" }, + { name = "taskgroup", marker = "python_full_version < '3.11'" }, { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/86/77/842e41be3cf3592b971cf42b24cae76e282294f474dc2dbf7cd6808d1b09/pydocket-0.15.5.tar.gz", hash = "sha256:b3af47702a293dd1da2e5e0f8f73f27fd3b3c95e36de72a2f71026d16908d5ba", size = 277245, upload-time = "2025-12-12T22:28:47.32Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/17/1fb6309e40bbee999c5d881b8213a1078968412d855e064a9a94cfb9eeef/pydocket-0.17.2.tar.gz", hash = "sha256:8f02c68952701eb1b3a70d439b76392d15f1eb9568d0bde6a69997ea5c79c89f", size = 329829, upload-time = "2026-01-26T16:07:56.217Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/c0/fdbc6e04e3369b90c6bf6567bc62871cf59e88550b94529821500dc807c1/pydocket-0.15.5-py3-none-any.whl", hash = "sha256:ad0d86c9a1bea394e875bcf8c793be2d0a7ebd1891bfe99e2e9eaf99ef0cb42e", size = 58517, upload-time = "2025-12-12T22:28:45.598Z" }, + { url = "https://files.pythonhosted.org/packages/53/74/4c9b70753d5721165047e6428ac239ca083118474794deaca5a27d0ab212/pydocket-0.17.2-py3-none-any.whl", hash = "sha256:f43743b84b4e3d614d99b0cad2deebab028104c217745406ecf9e1efb8926e04", size = 91628, upload-time = "2026-01-26T16:07:55.018Z" }, ] [[package]] @@ -1808,58 +1766,58 @@ wheels = [ [[package]] name = "pyinstrument" -version = "5.1.1" +version = "5.1.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/ce/824ee634994e612156f7b84eaf50b8523c676ebfed8d8dd12939a82f4c15/pyinstrument-5.1.1.tar.gz", hash = "sha256:bc401cda990b3c1cfe8e0e0473cbd605df3c63b73478a89ac4ab108f2184baa8", size = 264730, upload-time = "2025-08-12T11:35:43.426Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/7f/d3c4ef7c43f3294bd5a475dfa6f295a9fee5243c292d5c8122044fa83bcb/pyinstrument-5.1.2.tar.gz", hash = "sha256:af149d672da9493fa37334a1cc68f7b80c3e6cb9fd99b9e426c447db5c650bf0", size = 266889, upload-time = "2026-01-04T18:38:58.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/80/57448dba5ced8c45d753b4b8dc587f0e117712405d4235b305327fb90671/pyinstrument-5.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f4912edf01912792d2f44dfc43d3f041acc7ea634d0300b3394711963a431d1b", size = 130457, upload-time = "2025-08-12T11:34:20.354Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2a/af500f4352a2ca952eb6465e861964def199e30bfe6f3f5e7f0f05bd870e/pyinstrument-5.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:217ee5decbc7312b16092307a1bfbe1c04b175bb91ad9622388cd266f54fb260", size = 122967, upload-time = "2025-08-12T11:34:21.914Z" }, - { url = "https://files.pythonhosted.org/packages/83/e4/68952fce23590eb7cc47c852ad22037ef87148897af45194c6133689cc57/pyinstrument-5.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fcad0f4211226e445cdbca1ee308567d3d7507cb18355463de64bea2dbe0b80", size = 147557, upload-time = "2025-08-12T11:34:23.386Z" }, - { url = "https://files.pythonhosted.org/packages/69/cd/d54450580569a075ee165e91a885586e0099424a7792ab4c3e1480fbbda5/pyinstrument-5.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:129dcdd57f0720ffb0c181517eddf5db6bfb2cdd1338c6fd5f4082d62c657ba0", size = 146253, upload-time = "2025-08-12T11:34:24.577Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d6/8756e79c29e9ac6f21b437627012bfb3329d4af792a2f529a4a9b31f0c71/pyinstrument-5.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1efb57a5b708f76b5fed08ecb9dc380a1949f4cb82794d14c3c40113c2f4a2d", size = 146483, upload-time = "2025-08-12T11:34:25.992Z" }, - { url = "https://files.pythonhosted.org/packages/73/48/8e0d264c5116325e38587aceda98b2d3da708149c320b6999b636a1735ad/pyinstrument-5.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d1e89dcf9a88d328cc7eb7c67c43c8a4ad38c8162eda87d93166a998fc8580d", size = 146009, upload-time = "2025-08-12T11:34:27.109Z" }, - { url = "https://files.pythonhosted.org/packages/da/02/b492507090cf18f6d7a3ff419eecb53c1725c33f4ab6bbe32e9e611bacb5/pyinstrument-5.1.1-cp310-cp310-win32.whl", hash = "sha256:954bc886e11ddcf5789a8a953bec65e663f48d93cc634a8c77585f3e8762bcbb", size = 124155, upload-time = "2025-08-12T11:34:28.542Z" }, - { url = "https://files.pythonhosted.org/packages/86/2a/7e909fc1a3e8e9d0724b71829b1d9f4d398a9108b20ee1591bed45b014c5/pyinstrument-5.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:9b2f1d54c72661feeefd5ca81b6f487baf52add1d688ae349be599a258168bfc", size = 125038, upload-time = "2025-08-12T11:34:29.702Z" }, - { url = "https://files.pythonhosted.org/packages/52/65/dc0fcc6122e6f484ffa48260d1023e1fe90e53da8d69a7de656205af91ba/pyinstrument-5.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5272cff6eea21163b2105f6a80c907315e0f567720621e6d5672dc01bf71ee48", size = 130287, upload-time = "2025-08-12T11:34:32.436Z" }, - { url = "https://files.pythonhosted.org/packages/90/96/8a6cab312342f1ad7322b459dcbdb2e041251b7bbe4c1d0dd38ccbf2ae20/pyinstrument-5.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d4e7dc5a4aee37a44ff2e63db3127f2044dd95edcae240cb95915adbf223d4be", size = 122879, upload-time = "2025-08-12T11:34:33.506Z" }, - { url = "https://files.pythonhosted.org/packages/8a/89/1ad1ae703951832a34ec6b32354c6df1d0690b0333b7b8396c92afcdc23e/pyinstrument-5.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:602d55121df1d88aeb6d8ebc801597fdcb9718f78d602ae81458d65c56f25d24", size = 146412, upload-time = "2025-08-12T11:34:34.975Z" }, - { url = "https://files.pythonhosted.org/packages/a5/70/588ec07eeaa5d5d4aff2d47c8010379e9b39a7aee7e3fda625a0ce4f5838/pyinstrument-5.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63b5a788ff955e0597bc95463e64d5fa3747017524fdc02a0f5d12d5117cf2b9", size = 144885, upload-time = "2025-08-12T11:34:36.546Z" }, - { url = "https://files.pythonhosted.org/packages/0a/8f/119ad44d454edb927434bb53d8a69904ecaa47ec56f95c688c557a9590de/pyinstrument-5.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7f66038ec55a12e5510689240cdc745f8e98c90b93363f745106976e5cfb7397", size = 145530, upload-time = "2025-08-12T11:34:37.726Z" }, - { url = "https://files.pythonhosted.org/packages/8e/31/d53b20d1967ba78574807c5940251c0f238e8e3515ee9c4f20207eb09199/pyinstrument-5.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:110a08b6e7fa9542eb37c337e79467913d364a03bc2062f85566ba96bc82f54e", size = 144708, upload-time = "2025-08-12T11:34:38.855Z" }, - { url = "https://files.pythonhosted.org/packages/48/18/78ccb35dd265e83c0b7404a501004387b2e2390a0a44b6e4a004307931d1/pyinstrument-5.1.1-cp311-cp311-win32.whl", hash = "sha256:a223d5e9226ccede5bf2fbd4d13ce0aeb5120501b633ba85290ed94df37d3623", size = 124151, upload-time = "2025-08-12T11:34:40.392Z" }, - { url = "https://files.pythonhosted.org/packages/43/c0/3eb94aa2c2f0b8d49e290d4df662e21e707eb4a23f3b938239634fbfdc17/pyinstrument-5.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:16ce582a7b56287d338a8b59688458341aab5c6abda970ba50b2f7b3fd69f89d", size = 124942, upload-time = "2025-08-12T11:34:41.564Z" }, - { url = "https://files.pythonhosted.org/packages/76/3a/7824caf1fb419d0108f375a15b28cdd7ace8593f1ea56ef8276fddce9526/pyinstrument-5.1.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bcd6a03bdf180d73bc8dc7371e09dda089a48057095584e5f2818df1c820525b", size = 130306, upload-time = "2025-08-12T11:34:42.624Z" }, - { url = "https://files.pythonhosted.org/packages/3a/54/60ddd5eae617e29b58de774d178f4e4f7cdffd07ed1de36f976927ce69d3/pyinstrument-5.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ffa0948c1e268356dcf930c128624f34037ce92ee865fa4c056dee067aee4c5", size = 122817, upload-time = "2025-08-12T11:34:44.182Z" }, - { url = "https://files.pythonhosted.org/packages/35/12/35b694bfa58050607eedc80a68f64e6195c738249101a0dcbed0657147e7/pyinstrument-5.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c95adf98a920f2039eb0065966f980452a7af794bab387e9bfe8af3c681affa0", size = 148053, upload-time = "2025-08-12T11:34:45.589Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4a/338b891f9119cf747153301d5d095942f378032309cd385e53857d03c2d2/pyinstrument-5.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bcb46ca8596b375c27850d4d06a1ce94ed78074774d35cbed3ccd28b663c5ba6", size = 146817, upload-time = "2025-08-12T11:34:46.668Z" }, - { url = "https://files.pythonhosted.org/packages/3b/cc/186cdb048fee445bbf9bd18819287a61b57b66ec68cfc47bc3c1e38b1ae6/pyinstrument-5.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3fc16597d26b24a46bf3455686300c0b8a3eb565ebc82396f402c031dccc0145", size = 146914, upload-time = "2025-08-12T11:34:47.878Z" }, - { url = "https://files.pythonhosted.org/packages/b8/d1/533309830dd356d43e54d7feebaffab357f08568972285c609e98f7e6119/pyinstrument-5.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5aa135b4bd9667ddcb25fa582f4db77c5117ef207cb10ae901a8e4c5d5cde0e0", size = 146533, upload-time = "2025-08-12T11:34:49.015Z" }, - { url = "https://files.pythonhosted.org/packages/b8/df/2a656d6b1bd68ecfbb73c557906274a40ec7219dd92980fc1324997cf93e/pyinstrument-5.1.1-cp312-cp312-win32.whl", hash = "sha256:d15e37f8074b3043fca7aa985cb2079d2c221ccb0d27f059451ede800c801645", size = 124286, upload-time = "2025-08-12T11:34:50.285Z" }, - { url = "https://files.pythonhosted.org/packages/51/af/144d331cc9734e9141ac1a75f3ce904074ebc93dfe43cab44049ba8c8c28/pyinstrument-5.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c27d5cef0e809f213e5a94143c397d948650f5142c91dcce3611f584779183e", size = 125032, upload-time = "2025-08-12T11:34:51.369Z" }, - { url = "https://files.pythonhosted.org/packages/36/d4/b94f47aa7d301f6cdf5924bb75caacd0d0a1852bd4e876e3a64fc5798dad/pyinstrument-5.1.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:45af421c60c943a7f1619afabeba4951d4cc16b4206490d7d5b7ef5a4e2dfd42", size = 130315, upload-time = "2025-08-12T11:34:52.91Z" }, - { url = "https://files.pythonhosted.org/packages/1e/42/1bc2f28e139f69a0918d5d5dc1d59e65c640d4da9dd153fa48c2a8a87dd9/pyinstrument-5.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2603db3d745a65de66c96929ab9b0fcce050511eb24e32856ea2458785b8917f", size = 122805, upload-time = "2025-08-12T11:34:54.201Z" }, - { url = "https://files.pythonhosted.org/packages/a8/85/2f0c9115cd8a01e0a18d0650d9f3f20ff71e8ca17bd4af60dd3a0cb76f8a/pyinstrument-5.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fe32492100efaa1b0a488c237fe420fdaf141646733a31a97f96c4e1fa6bbf8", size = 148210, upload-time = "2025-08-12T11:34:55.662Z" }, - { url = "https://files.pythonhosted.org/packages/86/62/3c73a63e6913378cc7e9ffb5af1e50836511eee83b7c7bf252fad7ec24e4/pyinstrument-5.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:999b5373f8b1e846357923063ae5c9275ad8a85ed4e0a42960a349288d1f5007", size = 146995, upload-time = "2025-08-12T11:34:57.133Z" }, - { url = "https://files.pythonhosted.org/packages/ab/8b/d21f4b6d8849881e9572967818e3e6d2dcb212e7dfa89e4e356d359db32b/pyinstrument-5.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58a2f69052178ec624e4df0cf546eda48b3a381572ac1cb3272b4c163888af9d", size = 147029, upload-time = "2025-08-12T11:34:58.255Z" }, - { url = "https://files.pythonhosted.org/packages/8a/4d/1e43cecf2bcf4a3dd1100f4fc7a3da6438a65d0b95ca7b8ab5d094ea7c0b/pyinstrument-5.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d9bbc00d2e258edbefeb39b61ad4636099b08acd1effdd40d76883a13e7bf5a", size = 146668, upload-time = "2025-08-12T11:34:59.401Z" }, - { url = "https://files.pythonhosted.org/packages/34/48/00322b48e7adb665d04303b487454eb0c13a76ec0af8da20f452098fcc12/pyinstrument-5.1.1-cp313-cp313-win32.whl", hash = "sha256:cf2d8933e2aeaa02d4cb6279d83ef11ee882fb243fff96e3378153a730aadd6e", size = 124288, upload-time = "2025-08-12T11:35:00.514Z" }, - { url = "https://files.pythonhosted.org/packages/f5/14/d56515a110f74799aefc7489c1578ce4d99a4d731309559a427f954e7abc/pyinstrument-5.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:2402683a92617066b13a6d48f904396dcd15938016875b392534df027660eed4", size = 125041, upload-time = "2025-08-12T11:35:01.913Z" }, - { url = "https://files.pythonhosted.org/packages/18/2b/e4bdcabb5ae67de2ec3fa1f6e4eb4ae707b0bf460f895d4594792cdc919b/pyinstrument-5.1.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:688acba1c00cad73e43254e610f8e384a53ced3b0dbb5268fb44636e2b99663e", size = 130358, upload-time = "2025-08-12T11:35:03.569Z" }, - { url = "https://files.pythonhosted.org/packages/20/36/616f8db63997c096d3fb65e657cdf5bd2a63b53ed24a14750770dc500979/pyinstrument-5.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:116f5ad8cec4d6f5626305d7c1a104f5845a084bfb4b192d231eb8c41ea81f9a", size = 122827, upload-time = "2025-08-12T11:35:04.661Z" }, - { url = "https://files.pythonhosted.org/packages/af/7a/4f5d2bbc7c2466d46eb5ff47c6e667464eead47140e01a64be45215a59d4/pyinstrument-5.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d139d12a637001d3884344330054ce8335b2c8165dc3dd239726e1b358576bd", size = 147947, upload-time = "2025-08-12T11:35:05.786Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/c9b0081c0e52789a910390ce44e54c1318999d74386f15d92d0deb522aff/pyinstrument-5.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc5b87b1e27bec94457fed8d03c755a3c09edb4f35d975dbdffd77d863173254", size = 146702, upload-time = "2025-08-12T11:35:07.202Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1b/745ed7997da22ae68ff21b8f28e5e3a97b220335dce4ee7cf46d5eb17b32/pyinstrument-5.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15f4a2ed9562efab34b555e1208955cf9681b2272489d7a59cd0e289344ada2e", size = 146836, upload-time = "2025-08-12T11:35:08.297Z" }, - { url = "https://files.pythonhosted.org/packages/70/f0/05cefdcf79d1901f9d179e7f55f3acaadbc5fee7af955cebb3f555280638/pyinstrument-5.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1cb0c79bfa2b2b5734213429c9d7f455e5af664cfde785c69a5780f6c532c1fd", size = 146463, upload-time = "2025-08-12T11:35:09.483Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cb/6a6f33316be3c7b8247f8ca0e418a2b6fb68d64c227169b7dbee50009366/pyinstrument-5.1.1-cp314-cp314-win32.whl", hash = "sha256:3b9f1216ae4848a8983dc405e1a42e46e75bd8ae96aaba328d4358b8fc80a7a0", size = 124950, upload-time = "2025-08-12T11:35:11.607Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ea/99caeb29f446f57d077a83c7c5f2b7c27c1719984d425f679bf2ec1eb6b0/pyinstrument-5.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:26971d4a17e0d5d4f6737e71c9de7a7ce5c83ab7daf078c6bf330be41d65273b", size = 125720, upload-time = "2025-08-12T11:35:12.683Z" }, - { url = "https://files.pythonhosted.org/packages/f6/d0/953b75d634565ef34f8ed559f2e4af7cd1f2d5f5b578092e8f1d8199e4b1/pyinstrument-5.1.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:62362843884d654401ec4c25fed35f4b4ded077d96b3396f1e791c31e4203d3e", size = 131258, upload-time = "2025-08-12T11:35:13.805Z" }, - { url = "https://files.pythonhosted.org/packages/a6/a4/4ec87cfd0974d79b2fcd72b3e20336fc65b96a5b08f2eb2867bf71b27b82/pyinstrument-5.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f2d640230b71c6d9ac8f27a9c5cd07fc8a6acad9196d1e48d9c33658b176fb80", size = 123276, upload-time = "2025-08-12T11:35:14.933Z" }, - { url = "https://files.pythonhosted.org/packages/eb/f8/6a210989c8ede85f91b7e4ba5d9730492f1d081762570c06c750d787536c/pyinstrument-5.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f54f7292c63461c75ddf193f5e733803e463ccbc54f2fb7c9591337ddea7d10", size = 155767, upload-time = "2025-08-12T11:35:16.124Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a8/5ac81ffbfe36d2e5c3332a9452746a21540987da0d9491db751a905bba13/pyinstrument-5.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c156eb442f9f22960ae16bd195051863d5e8a68b877926e88bbaf8bbdc1456d1", size = 153423, upload-time = "2025-08-12T11:35:17.312Z" }, - { url = "https://files.pythonhosted.org/packages/3f/55/5620c2a61403cde044e81e33056c14fbf5793eea33f67f2223d61abec9ae/pyinstrument-5.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:caadaf67ad5926c46af784316024793c909b9e9ee550475855fd32171c4bd033", size = 153542, upload-time = "2025-08-12T11:35:18.729Z" }, - { url = "https://files.pythonhosted.org/packages/7a/83/a8f22466652250a847dfdf58f9a2717b470fdbbcb075c7f730bf608041a6/pyinstrument-5.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88ef2e8f483a5e1501d79a7ebdab592a597467810ed24d8db09ab6f568e938d3", size = 152337, upload-time = "2025-08-12T11:35:19.849Z" }, - { url = "https://files.pythonhosted.org/packages/0d/a6/cd4590da14deaeda6315519c26064874bbb9648a1358b80e8a8ca5d4add0/pyinstrument-5.1.1-cp314-cp314t-win32.whl", hash = "sha256:265bc4389f82e6521777bfab426a62a15c4940955e86f75db79a44e7349f9757", size = 125621, upload-time = "2025-08-12T11:35:21.201Z" }, - { url = "https://files.pythonhosted.org/packages/b3/30/177102e798539368aef25688a6a171d66ec92e6f16b6b651a89045a2bd13/pyinstrument-5.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:fa254f269a72a007b5d02c18cd4b67081e0efabbd33e18acdbd5e3be905afa06", size = 126528, upload-time = "2025-08-12T11:35:22.578Z" }, + { url = "https://files.pythonhosted.org/packages/43/74/c66e1bf3565600d78f53195efb6f8fd31610f85a58aa3fee39c56bf71d1b/pyinstrument-5.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f224fe80ba288a00980af298d3808219f9d246fd95b4f91729c9c33a0dc54fe6", size = 131470, upload-time = "2026-01-04T18:37:22.536Z" }, + { url = "https://files.pythonhosted.org/packages/1a/6b/606c5bfa311b5be74f58ef505c678216dda2be3b76a2ac770c2b0fccff77/pyinstrument-5.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7df09fc0d5b72daf48b73cdf07738761bff7f656c81aff686b3ccdd7d2abe236", size = 124567, upload-time = "2026-01-04T18:37:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/15/70/c8a88defb77873513971f590549c48ceb70f7ef10f30a689762ef36dd877/pyinstrument-5.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75a7e17377d4405666bbaf126b1fd7bbb7e206d7246e6db3d62864d3d4790ae3", size = 149205, upload-time = "2026-01-04T18:37:25.696Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4b/0e64fefb939af472c3fbc63ab45224766447bde73f51579f3ecc335b0a49/pyinstrument-5.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5381cc6583d26e04d9298acded4242f4fe71986f1472c8aee6992c6816f0cac5", size = 147900, upload-time = "2026-01-04T18:37:27.343Z" }, + { url = "https://files.pythonhosted.org/packages/38/6e/b4209711c61176acfeb6c351e9f88a37ed3d3bc3b749c374c0a655ee8f50/pyinstrument-5.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ec08a530bef8d3492d31d8b0b12d0cfde09539f2a1c4b9678662ebc3c843e478", size = 148133, upload-time = "2026-01-04T18:37:29.047Z" }, + { url = "https://files.pythonhosted.org/packages/26/28/f323b70789833baf0628af7b9f797b8c1a13b695bd8aa582b1312f14b602/pyinstrument-5.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d671168508129b472be570bc9aee361190ba917b997c703bd134bb4de445ce7", size = 147652, upload-time = "2026-01-04T18:37:30.682Z" }, + { url = "https://files.pythonhosted.org/packages/16/cd/9b0af0307a3a2cffb48ca76275c50b8bec3f85ca6e7b996e2e6cfbda1207/pyinstrument-5.1.2-cp310-cp310-win32.whl", hash = "sha256:5957a94f84564b374a7f856d1b322345d600964280b0d687b8ddcc483f21e576", size = 125793, upload-time = "2026-01-04T18:37:31.906Z" }, + { url = "https://files.pythonhosted.org/packages/05/89/fe4c650c252aefb8064bfdff6c0a020d33d15c55dc22abfa1f352dcc2dd1/pyinstrument-5.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:38a2180a7801c51610b50e5d423674b21872efd019ccf05a11b7f9016cb1dcfc", size = 126679, upload-time = "2026-01-04T18:37:33.59Z" }, + { url = "https://files.pythonhosted.org/packages/79/ef/0288edd620fb0cf2074d8c8e3567007a6bac66307b839d99988563de4eb8/pyinstrument-5.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3739a05583ea6312c385eb59fe985cd20d9048e95f9eeeb6a2f6c35202e2d36e", size = 131284, upload-time = "2026-01-04T18:37:35.01Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4e/2a90a6997d9f7a39a6998d56de72e52673ebf5a9169a1c39dbf173e95105/pyinstrument-5.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c9ee05dc75ac5fb18498c311e624f77f7f321f7ff325b251aa09e52e46f1d6a", size = 124468, upload-time = "2026-01-04T18:37:36.628Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/7bfd403e81f9b5ec523f60cced8f516ee52312752bb2e0fafabfd90bbd78/pyinstrument-5.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a49a55ca5b75218767e29cacbe515d0b66fc18cb48a937bca0f77b8dafc7202", size = 148057, upload-time = "2026-01-04T18:37:37.998Z" }, + { url = "https://files.pythonhosted.org/packages/50/3a/7205d7c199947d18edcd013af4ddf4d3cca85c5488fbe493050035947f7c/pyinstrument-5.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c45c14974ff04b1bfdc6c2a448627c6da7409c7800d0eb7bd03fb435dcb41d7", size = 146526, upload-time = "2026-01-04T18:37:39.642Z" }, + { url = "https://files.pythonhosted.org/packages/24/e8/f6864172e7ebe4bc5209bafbc574a619b4c511b9506b941789b11441be7c/pyinstrument-5.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:22b9c04b3982c41c04b1c5ed05d1bc3a2ba26533450084058119f6dc160e70a3", size = 147179, upload-time = "2026-01-04T18:37:41.332Z" }, + { url = "https://files.pythonhosted.org/packages/6d/04/89ef2d1c34767bfdbcc74ab0c7e0d021d7fac5e79873239e4ca26e97d6da/pyinstrument-5.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5c4995ee0774801790c138f0dfec17d4e7a7ef09a6d56d53cbcbf0578a711021", size = 146354, upload-time = "2026-01-04T18:37:42.808Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d4/64441547ec12391b92c739a3b0685059e7dfa088d928df8364676ef7abc7/pyinstrument-5.1.2-cp311-cp311-win32.whl", hash = "sha256:fe449e4a8ee60a2a27cf509350a584670f4c3704649601be7937598f09dbe7ca", size = 125790, upload-time = "2026-01-04T18:37:44.141Z" }, + { url = "https://files.pythonhosted.org/packages/4d/8b/0a5f6b239294decb0ecd932711f3470bfbd42fc2e08a94cd5c1f4f6da7f1/pyinstrument-5.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:3fb839429671a42bf349335af4c1ce5cf83386ac11f04df0bc40720d4cb7d77d", size = 126578, upload-time = "2026-01-04T18:37:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/26/d9/8fa5571ddd21b2b7189bd8b0bb4e90be1659a54dda5af51c7f6bf2b5666f/pyinstrument-5.1.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2519865d4bf58936f2506c1c46a82d29a20f3239aa50c941df1ca9618c7da5f0", size = 131419, upload-time = "2026-01-04T18:37:46.843Z" }, + { url = "https://files.pythonhosted.org/packages/6f/50/0512adb83cadfeaa1a215dc9784defff5043c5aa052d15015e3d8013af75/pyinstrument-5.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:059442106b8b5de29ae5ac1bdc20d044fed4da534b8caba434b6ffb119037bf5", size = 124446, upload-time = "2026-01-04T18:37:48.572Z" }, + { url = "https://files.pythonhosted.org/packages/9b/78/c45f0b668fb3c8c0d32058a451a8e1d34737cd7586387982185e12df1977/pyinstrument-5.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cd51f2d54fc39a4cfd73ba6be27cd0187123132ce3f445b639bff5e1b23d7e26", size = 149694, upload-time = "2026-01-04T18:37:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/91/4d/2ca3ca9906ce6e05070f431c54d54ccbaf57a980cfa58032d35b0b0ac1f8/pyinstrument-5.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12af1e83795b6c640d657d339014dd1ff718b182dec736d7d1f1d8a97534eb53", size = 148461, upload-time = "2026-01-04T18:37:51.544Z" }, + { url = "https://files.pythonhosted.org/packages/18/d2/bfe84a4326172ef68655b65b49fd041eeb94c8e59ee47258589b8b79dd3b/pyinstrument-5.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2565513658e742c5eb691a779cb29d19d01bc9ee951d0eb76482e9f343c38c2e", size = 148560, upload-time = "2026-01-04T18:37:52.931Z" }, + { url = "https://files.pythonhosted.org/packages/d0/00/db7f5def351e869230b0165828c4edacbf3fdda8d66aff30dd73a62082c2/pyinstrument-5.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5afd0ba788a1d112da49fb77966918e01df1f9e7d62e72894d82f7acb0996c2d", size = 148178, upload-time = "2026-01-04T18:37:54.278Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bc/aea3329576e20b987d205027b8e6442ece845d681b9f9d8682d5404f81f3/pyinstrument-5.1.2-cp312-cp312-win32.whl", hash = "sha256:554077b031b278593cb2301f0057be771ea62a729878c69aaf29fcdfb7b71281", size = 125927, upload-time = "2026-01-04T18:37:55.615Z" }, + { url = "https://files.pythonhosted.org/packages/14/e2/d928434ec3a840478e95fd0d73b0dfc0b8060a07b06f4b45e9df30444e9a/pyinstrument-5.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:55a905384ba43efc924b8863aa6cfd276f029e4aa70c4a0e3b7389e27b191e45", size = 126675, upload-time = "2026-01-04T18:37:57.278Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8e/b9aea969eec67c129652000446384d550a0df45c297adc9fd74da2f8482c/pyinstrument-5.1.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7b8bab2334bf1d4c9e92d61db574300b914b594588a6b6dd67c45450152dfc29", size = 131418, upload-time = "2026-01-04T18:37:58.642Z" }, + { url = "https://files.pythonhosted.org/packages/8f/62/76418eb29b5591f3e5500369a6777ce928135c3aa6ccdb0c861a9c6ca93b/pyinstrument-5.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:13dcc138a61298ef4994b7aebff509d2c06db89dfd6e2021f0b9cd96aaa44ec3", size = 124448, upload-time = "2026-01-04T18:37:59.95Z" }, + { url = "https://files.pythonhosted.org/packages/07/73/874bccc04bcf6f4babc3de1a9568e209e7e40998563974f5030b0fb4d3e0/pyinstrument-5.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8abd4a7ffa2e7f9e00039a5e549e8eebc80d7ca8d43f0fb51a50ff2b117ce4a", size = 149853, upload-time = "2026-01-04T18:38:01.405Z" }, + { url = "https://files.pythonhosted.org/packages/cf/85/268446c4388d77ff4abdeaff202356e1527b3ff9576f5587443a24980bec/pyinstrument-5.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb3a05108edebc30f31e2c69c904576042f1158b2513ab80adc08f7848a7a8f0", size = 148641, upload-time = "2026-01-04T18:38:03.086Z" }, + { url = "https://files.pythonhosted.org/packages/fc/15/4f8dea3381483e68d00582a9b823a21a088acfe77a847a7991a1a8feed76/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f70d588b53f3f35829d1d1ddfa05e07fcebf1434b3b1509d542ca317d8e9a2a5", size = 148674, upload-time = "2026-01-04T18:38:04.805Z" }, + { url = "https://files.pythonhosted.org/packages/fa/61/72c180454b6511d5b90166f8828e1bab3b45d0489952a1fe48c5c585233d/pyinstrument-5.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b007327e0d6a6a01d5064883dd27c19996f044ce7488d507826fee7884e6a32e", size = 148315, upload-time = "2026-01-04T18:38:06.114Z" }, + { url = "https://files.pythonhosted.org/packages/2c/f0/4c27cebddf22a8840bd8b419366bb321ce41f921ca1893e309c932ab28bf/pyinstrument-5.1.2-cp313-cp313-win32.whl", hash = "sha256:9ba0e6b17a7e86c3dc02d208e4c25506e8f914d9964ae89449f1f37f0b70abc0", size = 125926, upload-time = "2026-01-04T18:38:07.507Z" }, + { url = "https://files.pythonhosted.org/packages/6c/20/6b1bee88ddef065b0df3a3ba4ba60ed8a9ca443d5cded7152a8a9750914f/pyinstrument-5.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:660d7fc486a839814db0b2f716bc13d8b99b9c780aaeb47f74a70a34adc02a7b", size = 126678, upload-time = "2026-01-04T18:38:08.826Z" }, + { url = "https://files.pythonhosted.org/packages/66/0f/7d5154c92904bdf25be067a7fe4cad4ba48919f16ccbb51bb953d9ae1a20/pyinstrument-5.1.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0baed297beee2bb9897e737bbd89e3b9d45a2fbbea9f1ad4e809007d780a9b1e", size = 131388, upload-time = "2026-01-04T18:38:10.491Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/bf83231a3f951e11b4dfaf160e1eeba1ce29377eab30e3d2eb6ee22ff3ba/pyinstrument-5.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ebb910a32a45bde6c3fc30c578efc28a54517990e11e94b5e48a0d5479728568", size = 124456, upload-time = "2026-01-04T18:38:11.792Z" }, + { url = "https://files.pythonhosted.org/packages/ac/98/762cf10896d907268629e1db08a48f128984a53e8d92b99ea96f862597e5/pyinstrument-5.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bad403c157f9c6dba7f731a6fca5bfcd8ca2701a39bcc717dcc6e0b10055ffc4", size = 149594, upload-time = "2026-01-04T18:38:13.434Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1b/48580e16e623d89af58b89c552c95a2ae65f70a1f4fab1d97879f34791db/pyinstrument-5.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f456cabdb95fd343c798a7f2a56688b028f981522e283c5f59bd59195b66df5", size = 148339, upload-time = "2026-01-04T18:38:14.767Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/38157a8a6ec67789d8ee109fd09877ea3340df44e1a7add8f249e30a8ade/pyinstrument-5.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4e9c4dcc1f2c4a0cd6b576e3604abc37496a7868243c9a1443ad3b9db69d590f", size = 148485, upload-time = "2026-01-04T18:38:16.121Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/31ee72b19cfc48a82801024b5d653f07982154a11381a3ae65bbfdbf2c7b/pyinstrument-5.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:acf93b128328c6d80fdb85431068ac17508f0f7845e89505b0ea6130dead5ca6", size = 148106, upload-time = "2026-01-04T18:38:17.623Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b4/7ab20243187262d66ab062778b1ccac4ca55090752f32a83f603f4e5e3a2/pyinstrument-5.1.2-cp314-cp314-win32.whl", hash = "sha256:9c7f0167903ecff8b1d744f7e37b2bd4918e05a69cca724cb112f5ed59d1e41b", size = 126593, upload-time = "2026-01-04T18:38:18.968Z" }, + { url = "https://files.pythonhosted.org/packages/9e/a0/db6a8ae3182546227f5a043b1be29b8d5f98bf973e20d922981ef206de85/pyinstrument-5.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:ce3f6b1f9a2b5d74819ecc07d631eadececf915f551474a75ad65ac580ec5a0e", size = 127358, upload-time = "2026-01-04T18:38:20.28Z" }, + { url = "https://files.pythonhosted.org/packages/59/d2/719f439972b3f80e35fb5b1bcd888c3218d60dbc91957b99ffafd7ac9221/pyinstrument-5.1.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:af8651b239049accbeecd389d35823233f649446f76f47fd005316b05d08cef2", size = 132317, upload-time = "2026-01-04T18:38:21.669Z" }, + { url = "https://files.pythonhosted.org/packages/e2/1c/0ebfef69ae926665fae635424c5647411235c3689c9a9ad69fd68de6cae2/pyinstrument-5.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c6082f1c3e43e1d22834e91ba8975f0080186df4018a04b4dd29f9623c59df1d", size = 124917, upload-time = "2026-01-04T18:38:23.385Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ee/5599f769f515a0f1c97443edc7394fe2b9829bf39f404c046499c1a62378/pyinstrument-5.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c031eb066ddc16425e1e2f56aad5c1ce1e27b2432a70329e5385b85e812decee", size = 157407, upload-time = "2026-01-04T18:38:24.774Z" }, + { url = "https://files.pythonhosted.org/packages/fd/40/32aa865252288caef301237488ee309bd6701125888bf453d23ab764e357/pyinstrument-5.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f447ec391cad30667ba412dce41607aaa20d4a2496a7ab867e0c199f0fe3ae3d", size = 155068, upload-time = "2026-01-04T18:38:26.112Z" }, + { url = "https://files.pythonhosted.org/packages/91/68/0b56a1540fe1c357dfcda82d4f5b52c87fada5962cbf18703ea39ccbbe69/pyinstrument-5.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:50299bddfc1fe0039898f895b10ef12f9db08acffb4d85326fad589cda24d2ee", size = 155186, upload-time = "2026-01-04T18:38:27.914Z" }, + { url = "https://files.pythonhosted.org/packages/7a/48/7ef84abfc3e41148cf993095214f104e75ecff585e94c6e8be001e672573/pyinstrument-5.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a193ff08825ece115ececa136832acb14c491c77ab1e6b6a361905df8753d5c6", size = 153979, upload-time = "2026-01-04T18:38:29.236Z" }, + { url = "https://files.pythonhosted.org/packages/8f/cf/a28ad117d58b33c1d74bcdfbbcf1603b67346883800ac7d510cff8d3bcee/pyinstrument-5.1.2-cp314-cp314t-win32.whl", hash = "sha256:de887ba19e1057bd2d86e6584f17788516a890ae6fe1b7eed9927873f416b4d8", size = 127267, upload-time = "2026-01-04T18:38:30.619Z" }, + { url = "https://files.pythonhosted.org/packages/8e/97/03635143a12a5d941f545548b00f8ac39d35565321a2effb4154ed267338/pyinstrument-5.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:b6a71f5e7f53c86c9b476b30cf19509463a63581ef17ddbd8680fee37ae509db", size = 128164, upload-time = "2026-01-04T18:38:32.281Z" }, ] [[package]] @@ -2053,11 +2011,11 @@ wheels = [ [[package]] name = "python-multipart" -version = "0.0.20" +version = "0.0.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, ] [[package]] @@ -2198,15 +2156,15 @@ wheels = [ [[package]] name = "rich" -version = "14.2.0" +version = "14.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/84/4831f881aa6ff3c976f6d6809b58cdfa350593ffc0dc3c58f5f6586780fb/rich-14.3.1.tar.gz", hash = "sha256:b8c5f568a3a749f9290ec6bddedf835cec33696bfc1e48bcfecb276c7386e4b8", size = 230125, upload-time = "2026-01-24T21:40:44.847Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, + { url = "https://files.pythonhosted.org/packages/87/2a/a1810c8627b9ec8c57ec5ec325d306701ae7be50235e8fd81266e002a3cc/rich-14.3.1-py3-none-any.whl", hash = "sha256:da750b1aebbff0b372557426fb3f35ba56de8ef954b3190315eb64076d6fb54e", size = 309952, upload-time = "2026-01-24T21:40:42.969Z" }, ] [[package]] @@ -2346,28 +2304,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.8" +version = "0.14.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/d9/f7a0c4b3a2bf2556cd5d99b05372c29980249ef71e8e32669ba77428c82c/ruff-0.14.8.tar.gz", hash = "sha256:774ed0dd87d6ce925e3b8496feb3a00ac564bea52b9feb551ecd17e0a23d1eed", size = 5765385, upload-time = "2025-12-04T15:06:17.669Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/b8/9537b52010134b1d2b72870cc3f92d5fb759394094741b09ceccae183fbe/ruff-0.14.8-py3-none-linux_armv6l.whl", hash = "sha256:ec071e9c82eca417f6111fd39f7043acb53cd3fde9b1f95bbed745962e345afb", size = 13441540, upload-time = "2025-12-04T15:06:14.896Z" }, - { url = "https://files.pythonhosted.org/packages/24/00/99031684efb025829713682012b6dd37279b1f695ed1b01725f85fd94b38/ruff-0.14.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8cdb162a7159f4ca36ce980a18c43d8f036966e7f73f866ac8f493b75e0c27e9", size = 13669384, upload-time = "2025-12-04T15:06:51.809Z" }, - { url = "https://files.pythonhosted.org/packages/72/64/3eb5949169fc19c50c04f28ece2c189d3b6edd57e5b533649dae6ca484fe/ruff-0.14.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2e2fcbefe91f9fad0916850edf0854530c15bd1926b6b779de47e9ab619ea38f", size = 12806917, upload-time = "2025-12-04T15:06:08.925Z" }, - { url = "https://files.pythonhosted.org/packages/c4/08/5250babb0b1b11910f470370ec0cbc67470231f7cdc033cee57d4976f941/ruff-0.14.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d70721066a296f45786ec31916dc287b44040f553da21564de0ab4d45a869b", size = 13256112, upload-time = "2025-12-04T15:06:23.498Z" }, - { url = "https://files.pythonhosted.org/packages/78/4c/6c588e97a8e8c2d4b522c31a579e1df2b4d003eddfbe23d1f262b1a431ff/ruff-0.14.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c87e09b3cd9d126fc67a9ecd3b5b1d3ded2b9c7fce3f16e315346b9d05cfb52", size = 13227559, upload-time = "2025-12-04T15:06:33.432Z" }, - { url = "https://files.pythonhosted.org/packages/23/ce/5f78cea13eda8eceac71b5f6fa6e9223df9b87bb2c1891c166d1f0dce9f1/ruff-0.14.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d62cb310c4fbcb9ee4ac023fe17f984ae1e12b8a4a02e3d21489f9a2a5f730c", size = 13896379, upload-time = "2025-12-04T15:06:02.687Z" }, - { url = "https://files.pythonhosted.org/packages/cf/79/13de4517c4dadce9218a20035b21212a4c180e009507731f0d3b3f5df85a/ruff-0.14.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1af35c2d62633d4da0521178e8a2641c636d2a7153da0bac1b30cfd4ccd91344", size = 15372786, upload-time = "2025-12-04T15:06:29.828Z" }, - { url = "https://files.pythonhosted.org/packages/00/06/33df72b3bb42be8a1c3815fd4fae83fa2945fc725a25d87ba3e42d1cc108/ruff-0.14.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25add4575ffecc53d60eed3f24b1e934493631b48ebbc6ebaf9d8517924aca4b", size = 14990029, upload-time = "2025-12-04T15:06:36.812Z" }, - { url = "https://files.pythonhosted.org/packages/64/61/0f34927bd90925880394de0e081ce1afab66d7b3525336f5771dcf0cb46c/ruff-0.14.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c943d847b7f02f7db4201a0600ea7d244d8a404fbb639b439e987edcf2baf9a", size = 14407037, upload-time = "2025-12-04T15:06:39.979Z" }, - { url = "https://files.pythonhosted.org/packages/96/bc/058fe0aefc0fbf0d19614cb6d1a3e2c048f7dc77ca64957f33b12cfdc5ef/ruff-0.14.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb6e8bf7b4f627548daa1b69283dac5a296bfe9ce856703b03130732e20ddfe2", size = 14102390, upload-time = "2025-12-04T15:06:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/af/a4/e4f77b02b804546f4c17e8b37a524c27012dd6ff05855d2243b49a7d3cb9/ruff-0.14.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7aaf2974f378e6b01d1e257c6948207aec6a9b5ba53fab23d0182efb887a0e4a", size = 14230793, upload-time = "2025-12-04T15:06:20.497Z" }, - { url = "https://files.pythonhosted.org/packages/3f/52/bb8c02373f79552e8d087cedaffad76b8892033d2876c2498a2582f09dcf/ruff-0.14.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e5758ca513c43ad8a4ef13f0f081f80f08008f410790f3611a21a92421ab045b", size = 13160039, upload-time = "2025-12-04T15:06:49.06Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ad/b69d6962e477842e25c0b11622548df746290cc6d76f9e0f4ed7456c2c31/ruff-0.14.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f74f7ba163b6e85a8d81a590363bf71618847e5078d90827749bfda1d88c9cdf", size = 13205158, upload-time = "2025-12-04T15:06:54.574Z" }, - { url = "https://files.pythonhosted.org/packages/06/63/54f23da1315c0b3dfc1bc03fbc34e10378918a20c0b0f086418734e57e74/ruff-0.14.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:eed28f6fafcc9591994c42254f5a5c5ca40e69a30721d2ab18bb0bb3baac3ab6", size = 13469550, upload-time = "2025-12-04T15:05:59.209Z" }, - { url = "https://files.pythonhosted.org/packages/70/7d/a4d7b1961e4903bc37fffb7ddcfaa7beb250f67d97cfd1ee1d5cddb1ec90/ruff-0.14.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:21d48fa744c9d1cb8d71eb0a740c4dd02751a5de9db9a730a8ef75ca34cf138e", size = 14211332, upload-time = "2025-12-04T15:06:06.027Z" }, - { url = "https://files.pythonhosted.org/packages/5d/93/2a5063341fa17054e5c86582136e9895db773e3c2ffb770dde50a09f35f0/ruff-0.14.8-py3-none-win32.whl", hash = "sha256:15f04cb45c051159baebb0f0037f404f1dc2f15a927418f29730f411a79bc4e7", size = 13151890, upload-time = "2025-12-04T15:06:11.668Z" }, - { url = "https://files.pythonhosted.org/packages/02/1c/65c61a0859c0add13a3e1cbb6024b42de587456a43006ca2d4fd3d1618fe/ruff-0.14.8-py3-none-win_amd64.whl", hash = "sha256:9eeb0b24242b5bbff3011409a739929f497f3fb5fe3b5698aba5e77e8c833097", size = 14537826, upload-time = "2025-12-04T15:06:26.409Z" }, - { url = "https://files.pythonhosted.org/packages/6d/63/8b41cea3afd7f58eb64ac9251668ee0073789a3bc9ac6f816c8c6fef986d/ruff-0.14.8-py3-none-win_arm64.whl", hash = "sha256:965a582c93c63fe715fd3e3f8aa37c4b776777203d8e1d8aa3cc0c14424a4b99", size = 13634522, upload-time = "2025-12-04T15:06:43.212Z" }, + { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, + { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, + { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, + { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, ] [[package]] @@ -2412,14 +2370,15 @@ wheels = [ [[package]] name = "sse-starlette" -version = "3.0.3" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, + { name = "starlette" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/db/3c/fa6517610dc641262b77cc7bf994ecd17465812c1b0585fe33e11be758ab/sse_starlette-3.0.3.tar.gz", hash = "sha256:88cfb08747e16200ea990c8ca876b03910a23b547ab3bd764c0d8eb81019b971", size = 21943, upload-time = "2025-10-30T18:44:20.117Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/a0/984525d19ca5c8a6c33911a0c164b11490dd0f90ff7fd689f704f84e9a11/sse_starlette-3.0.3-py3-none-any.whl", hash = "sha256:af5bf5a6f3933df1d9c7f8539633dc8444ca6a97ab2e2a7cd3b6e431ac03a431", size = 11765, upload-time = "2025-10-30T18:44:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, ] [[package]] @@ -2450,52 +2409,70 @@ wheels = [ ] [[package]] -name = "tomli" -version = "2.3.0" +name = "taskgroup" +version = "0.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/8d/e218e0160cc1b692e6e0e5ba34e8865dbb171efeb5fc9a704544b3020605/taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d", size = 11504, upload-time = "2025-01-03T09:24:13.761Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, - { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, - { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, - { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, - { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, - { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, - { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, - { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, - { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, - { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, - { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, - { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, - { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, - { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, - { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, - { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, - { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, - { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, - { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, - { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, - { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b1/74babcc824a57904e919f3af16d86c08b524c0691504baf038ef2d7f655c/taskgroup-0.2.2-py2.py3-none-any.whl", hash = "sha256:e2c53121609f4ae97303e9ea1524304b4de6faf9eb2c9280c7f87976479a52fb", size = 14237, upload-time = "2025-01-03T09:24:11.41Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] [[package]] @@ -2546,7 +2523,7 @@ wheels = [ [[package]] name = "typer" -version = "0.20.0" +version = "0.21.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -2554,9 +2531,9 @@ dependencies = [ { name = "shellingham" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/28/7c85c8032b91dbe79725b6f17d2fffc595dff06a35c7a30a37bef73a1ab4/typer-0.20.0.tar.gz", hash = "sha256:1aaf6494031793e4876fb0bacfa6a912b551cf43c1e63c800df8b1a866720c37", size = 106492, upload-time = "2025-10-20T17:03:49.445Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/64/7713ffe4b5983314e9d436a90d5bd4f63b6054e2aca783a3cfc44cb95bbf/typer-0.20.0-py3-none-any.whl", hash = "sha256:5b463df6793ec1dca6213a3cf4c0f03bc6e322ac5e16e13ddd622a889489784a", size = 47028, upload-time = "2025-10-20T17:03:47.617Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, ] [[package]] @@ -2582,162 +2559,102 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.1" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] name = "uvicorn" -version = "0.38.0" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] [[package]] name = "wcwidth" -version = "0.2.14" +version = "0.3.5" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/30/6b0809f4510673dc723187aeaf24c7f5459922d01e2f794277a3dfb90345/wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605", size = 102293, upload-time = "2025-09-22T16:29:53.023Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/fc/44799c4a51ff0da0de0ff27e68c9dea3454f3d9bf15ffb606aeb6943e672/wcwidth-0.3.5.tar.gz", hash = "sha256:7c3463f312540cf21ddd527ea34f3ae95c057fa191aa7a9e043898d20d636e59", size = 234185, upload-time = "2026-01-25T04:37:23.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/7a/86/7f461495625145a99d60f14cdac142a11db5d501c86d48aad165575d1e06/wcwidth-0.3.5-py3-none-any.whl", hash = "sha256:b0a0245130566939a24ab8432e625b38272fbc62ecbe5aecbdcb50b8f02ce993", size = 86681, upload-time = "2026-01-25T04:37:22.585Z" }, ] [[package]] name = "websockets" -version = "15.0.1" +version = "16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/23/bb82321b86411eb51e5a5db3fb8f8032fd30bd7c2d74bfe936136b2fa1d6/wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04", size = 53482, upload-time = "2025-08-12T05:51:44.467Z" }, - { url = "https://files.pythonhosted.org/packages/45/69/f3c47642b79485a30a59c63f6d739ed779fb4cc8323205d047d741d55220/wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2", size = 38676, upload-time = "2025-08-12T05:51:32.636Z" }, - { url = "https://files.pythonhosted.org/packages/d1/71/e7e7f5670c1eafd9e990438e69d8fb46fa91a50785332e06b560c869454f/wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c", size = 38957, upload-time = "2025-08-12T05:51:54.655Z" }, - { url = "https://files.pythonhosted.org/packages/de/17/9f8f86755c191d6779d7ddead1a53c7a8aa18bccb7cea8e7e72dfa6a8a09/wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775", size = 81975, upload-time = "2025-08-12T05:52:30.109Z" }, - { url = "https://files.pythonhosted.org/packages/f2/15/dd576273491f9f43dd09fce517f6c2ce6eb4fe21681726068db0d0467096/wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd", size = 83149, upload-time = "2025-08-12T05:52:09.316Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c4/5eb4ce0d4814521fee7aa806264bf7a114e748ad05110441cd5b8a5c744b/wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05", size = 82209, upload-time = "2025-08-12T05:52:10.331Z" }, - { url = "https://files.pythonhosted.org/packages/31/4b/819e9e0eb5c8dc86f60dfc42aa4e2c0d6c3db8732bce93cc752e604bb5f5/wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418", size = 81551, upload-time = "2025-08-12T05:52:31.137Z" }, - { url = "https://files.pythonhosted.org/packages/f8/83/ed6baf89ba3a56694700139698cf703aac9f0f9eb03dab92f57551bd5385/wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390", size = 36464, upload-time = "2025-08-12T05:53:01.204Z" }, - { url = "https://files.pythonhosted.org/packages/2f/90/ee61d36862340ad7e9d15a02529df6b948676b9a5829fd5e16640156627d/wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6", size = 38748, upload-time = "2025-08-12T05:53:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c3/cefe0bd330d389c9983ced15d326f45373f4073c9f4a8c2f99b50bfea329/wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18", size = 36810, upload-time = "2025-08-12T05:52:51.906Z" }, - { url = "https://files.pythonhosted.org/packages/52/db/00e2a219213856074a213503fdac0511203dceefff26e1daa15250cc01a0/wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7", size = 53482, upload-time = "2025-08-12T05:51:45.79Z" }, - { url = "https://files.pythonhosted.org/packages/5e/30/ca3c4a5eba478408572096fe9ce36e6e915994dd26a4e9e98b4f729c06d9/wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85", size = 38674, upload-time = "2025-08-12T05:51:34.629Z" }, - { url = "https://files.pythonhosted.org/packages/31/25/3e8cc2c46b5329c5957cec959cb76a10718e1a513309c31399a4dad07eb3/wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f", size = 38959, upload-time = "2025-08-12T05:51:56.074Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8f/a32a99fc03e4b37e31b57cb9cefc65050ea08147a8ce12f288616b05ef54/wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311", size = 82376, upload-time = "2025-08-12T05:52:32.134Z" }, - { url = "https://files.pythonhosted.org/packages/31/57/4930cb8d9d70d59c27ee1332a318c20291749b4fba31f113c2f8ac49a72e/wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1", size = 83604, upload-time = "2025-08-12T05:52:11.663Z" }, - { url = "https://files.pythonhosted.org/packages/a8/f3/1afd48de81d63dd66e01b263a6fbb86e1b5053b419b9b33d13e1f6d0f7d0/wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5", size = 82782, upload-time = "2025-08-12T05:52:12.626Z" }, - { url = "https://files.pythonhosted.org/packages/1e/d7/4ad5327612173b144998232f98a85bb24b60c352afb73bc48e3e0d2bdc4e/wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2", size = 82076, upload-time = "2025-08-12T05:52:33.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/59/e0adfc831674a65694f18ea6dc821f9fcb9ec82c2ce7e3d73a88ba2e8718/wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89", size = 36457, upload-time = "2025-08-12T05:53:03.936Z" }, - { url = "https://files.pythonhosted.org/packages/83/88/16b7231ba49861b6f75fc309b11012ede4d6b0a9c90969d9e0db8d991aeb/wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77", size = 38745, upload-time = "2025-08-12T05:53:02.885Z" }, - { url = "https://files.pythonhosted.org/packages/9a/1e/c4d4f3398ec073012c51d1c8d87f715f56765444e1a4b11e5180577b7e6e/wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a", size = 36806, upload-time = "2025-08-12T05:52:53.368Z" }, - { url = "https://files.pythonhosted.org/packages/9f/41/cad1aba93e752f1f9268c77270da3c469883d56e2798e7df6240dcb2287b/wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0", size = 53998, upload-time = "2025-08-12T05:51:47.138Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/096a7cc13097a1869fe44efe68dace40d2a16ecb853141394047f0780b96/wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba", size = 39020, upload-time = "2025-08-12T05:51:35.906Z" }, - { url = "https://files.pythonhosted.org/packages/33/df/bdf864b8997aab4febb96a9ae5c124f700a5abd9b5e13d2a3214ec4be705/wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd", size = 39098, upload-time = "2025-08-12T05:51:57.474Z" }, - { url = "https://files.pythonhosted.org/packages/9f/81/5d931d78d0eb732b95dc3ddaeeb71c8bb572fb01356e9133916cd729ecdd/wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828", size = 88036, upload-time = "2025-08-12T05:52:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/ca/38/2e1785df03b3d72d34fc6252d91d9d12dc27a5c89caef3335a1bbb8908ca/wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9", size = 88156, upload-time = "2025-08-12T05:52:13.599Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8b/48cdb60fe0603e34e05cffda0b2a4adab81fd43718e11111a4b0100fd7c1/wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396", size = 87102, upload-time = "2025-08-12T05:52:14.56Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/d81abca783b58f40a154f1b2c56db1d2d9e0d04fa2d4224e357529f57a57/wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc", size = 87732, upload-time = "2025-08-12T05:52:36.165Z" }, - { url = "https://files.pythonhosted.org/packages/9e/b1/43b286ca1392a006d5336412d41663eeef1ad57485f3e52c767376ba7e5a/wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe", size = 36705, upload-time = "2025-08-12T05:53:07.123Z" }, - { url = "https://files.pythonhosted.org/packages/28/de/49493f962bd3c586ab4b88066e967aa2e0703d6ef2c43aa28cb83bf7b507/wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c", size = 38877, upload-time = "2025-08-12T05:53:05.436Z" }, - { url = "https://files.pythonhosted.org/packages/f1/48/0f7102fe9cb1e8a5a77f80d4f0956d62d97034bbe88d33e94699f99d181d/wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6", size = 36885, upload-time = "2025-08-12T05:52:54.367Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, + { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] [[package]]