├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── .python-version ├── LICENSE ├── README.md ├── pyproject.toml ├── src └── oura_mcp_server │ ├── __init__.py │ ├── py.typed │ └── server.py └── uv.lock /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Python Package 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up Python 3.12 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: "3.12" 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | python -m pip install ruff mypy 22 | pip install -e . 23 | - name: Lint with ruff 24 | run: | 25 | ruff check . 26 | - name: Type check with mypy 27 | run: | 28 | mypy src -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | build/ 3 | dist/ 4 | wheels/ 5 | *.egg-info/ 6 | *.egg 7 | 8 | # Python artifacts 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | *.so 13 | .Python 14 | .pytest_cache/ 15 | .coverage 16 | htmlcov/ 17 | .tox/ 18 | .nox/ 19 | .hypothesis/ 20 | .mypy_cache/ 21 | .ruff_cache/ 22 | 23 | # Virtual environments 24 | .venv/ 25 | venv/ 26 | ENV/ 27 | env/ 28 | 29 | # IDE files 30 | .idea/ 31 | .vscode/ 32 | *.swp 33 | *.swo 34 | .DS_Store 35 | 36 | # Environment variables 37 | .env 38 | .env.local 39 | 40 | # Node-generated files (if any) 41 | node_modules/ 42 | 43 | # Logs 44 | *.log -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Tomek Korbak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oura MCP Server 2 | 3 | ![Python Package](https://github.com/tomekkorbak/oura-mcp-server/workflows/Python%20Package/badge.svg) 4 | [![PyPI version](https://badge.fury.io/py/oura-mcp-server.svg)](https://badge.fury.io/py/oura-mcp-server) 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 6 | [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/) 7 | 8 | A [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) server that provides access to the Oura API. It allows language models to query sleep, readiness, and resilience data from Oura API. 9 | 10 | ## Available Tools 11 | 12 | The server exposes the following tools: 13 | 14 | ### Date Range Queries 15 | 16 | - `get_sleep_data(start_date: str, end_date: str)`: Get sleep data for a specific date range 17 | - `get_readiness_data(start_date: str, end_date: str)`: Get readiness data for a specific date range 18 | - `get_resilience_data(start_date: str, end_date: str)`: Get resilience data for a specific date range 19 | 20 | Dates should be provided in ISO format (`YYYY-MM-DD`). 21 | 22 | ### Today's Data Queries 23 | 24 | - `get_today_sleep_data()`: Get sleep data for today 25 | - `get_today_readiness_data()`: Get readiness data for today 26 | - `get_today_resilience_data()`: Get resilience data for today 27 | 28 | ## Usage 29 | 30 | You'll need an Oura API token to use this server. You can obtain one by: 31 | 32 | 1. Going to the [Oura Developer Portal](https://cloud.ouraring.com/v2/docs) 33 | 2. Creating a Personal Access Token 34 | 35 | ### Claude for Desktop 36 | 37 | Update your `claude_desktop_config.json` (located in `~/Library/Application\ Support/Claude/claude_desktop_config.json` on macOS and `%APPDATA%/Claude/claude_desktop_config.json` on Windows) to include the following: 38 | 39 | ```json 40 | { 41 | "mcpServers": { 42 | "oura": { 43 | "command": "uvx", 44 | "args": [ 45 | "oura-mcp-server" 46 | ], 47 | "env": { 48 | "OURA_API_TOKEN": "YOUR_OURA_API_TOKEN" 49 | } 50 | } 51 | } 52 | } 53 | ``` 54 | 55 | 56 | ## Example Queries 57 | 58 | Once connected, you can ask Claude questions like: 59 | 60 | - "What's my sleep score for today?" 61 | - "Show me my readiness data for the past week" 62 | - "How was my sleep from January 1st to January 7th?" 63 | - "What's my resilience score today?" 64 | 65 | ## Error Handling 66 | 67 | The server provides human-readable error messages for common issues: 68 | 69 | - Invalid date formats 70 | - API authentication errors 71 | - Network connectivity problems 72 | 73 | ## License 74 | 75 | This project is licensed under the MIT License - see the LICENSE file for details. 76 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "oura-mcp-server" 3 | version = "0.1.3" 4 | description = "MCP server for Oura API integration" 5 | license = {text = "MIT"} 6 | readme = "README.md" 7 | authors = [ 8 | { name = "Tomek Korbak", email = "tomasz.korbak@gmail.com" } 9 | ] 10 | requires-python = ">=3.12" 11 | dependencies = [ 12 | "mcp[cli]>=1.3.0", 13 | "httpx>=0.24.0", 14 | ] 15 | classifiers = [ 16 | "Development Status :: 4 - Beta", 17 | "Intended Audience :: Developers", 18 | "License :: OSI Approved :: MIT License", 19 | "Programming Language :: Python :: 3", 20 | "Programming Language :: Python :: 3.12", 21 | "Topic :: Software Development :: Libraries :: Python Modules", 22 | ] 23 | 24 | [project.optional-dependencies] 25 | dev = [ 26 | "ruff>=0.1.0", 27 | "mypy>=1.0.0", 28 | ] 29 | 30 | [project.urls] 31 | "Homepage" = "https://github.com/tomek-korbak/oura-mcp-server" 32 | "Bug Tracker" = "https://github.com/tomek-korbak/oura-mcp-server/issues" 33 | 34 | [project.scripts] 35 | oura-mcp-server = "oura_mcp_server.server:main" 36 | 37 | [build-system] 38 | requires = ["hatchling"] 39 | build-backend = "hatchling.build" 40 | 41 | [tool.hatch.build.targets.wheel] 42 | packages = ["src/oura_mcp_server"] 43 | 44 | [tool.hatch.build] 45 | only-packages = true 46 | 47 | [tool.ruff] 48 | line-length = 100 49 | target-version = "py312" 50 | select = ["E", "F", "I", "B", "W"] 51 | 52 | [tool.mypy] 53 | python_version = "3.12" 54 | warn_return_any = true 55 | warn_unused_configs = true 56 | disallow_untyped_defs = true 57 | disallow_incomplete_defs = true -------------------------------------------------------------------------------- /src/oura_mcp_server/__init__.py: -------------------------------------------------------------------------------- 1 | from .server import main 2 | 3 | __all__ = ["main"] 4 | -------------------------------------------------------------------------------- /src/oura_mcp_server/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomekkorbak/oura-mcp-server/72416a947547fb6a3aa4c1f18a8838cbceaefc04/src/oura_mcp_server/py.typed -------------------------------------------------------------------------------- /src/oura_mcp_server/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | MCP server for Oura API integration. 4 | This server exposes methods to query the Oura API for sleep, readiness, and resilience data. 5 | """ 6 | 7 | import os 8 | from datetime import date, datetime 9 | from typing import Any, Optional 10 | 11 | import httpx 12 | from mcp.server.fastmcp import FastMCP 13 | 14 | 15 | class OuraClient: 16 | """Client for interacting with the Oura API.""" 17 | 18 | BASE_URL = "https://api.ouraring.com/v2/usercollection" 19 | 20 | def __init__(self, access_token: str): 21 | """ 22 | Initialize the Oura API client. 23 | 24 | Args: 25 | access_token: Personal access token for Oura API 26 | """ 27 | self.access_token = access_token 28 | self.headers = {"Authorization": f"Bearer {access_token}"} 29 | self.client = httpx.Client(timeout=30.0) 30 | 31 | def get_sleep_data( 32 | self, start_date: date, end_date: Optional[date] = None 33 | ) -> dict[str, Any]: 34 | """ 35 | Get sleep data for a specific date range. 36 | 37 | Args: 38 | start_date: Start date for the query 39 | end_date: End date for the query (optional, defaults to start_date) 40 | 41 | Returns: 42 | Dictionary containing sleep data 43 | """ 44 | if end_date is None: 45 | end_date = start_date 46 | 47 | params = { 48 | "start_date": start_date.isoformat(), 49 | "end_date": end_date.isoformat(), 50 | } 51 | 52 | url = f"{self.BASE_URL}/sleep" 53 | response = self.client.get(url, headers=self.headers, params=params) 54 | 55 | if response.status_code != 200: 56 | error_msg = f"Error {response.status_code}: {response.text}" 57 | raise Exception(error_msg) 58 | 59 | # Get the raw response 60 | raw_data = response.json() 61 | 62 | # Transform the data 63 | transformed_data = [] 64 | 65 | for item in raw_data.get("data", []): 66 | # Format time durations 67 | awake_time = self._format_duration(item.get("awake_time", 0)) 68 | deep_sleep_duration = self._format_duration( 69 | item.get("deep_sleep_duration", 0) 70 | ) 71 | light_sleep_duration = self._format_duration( 72 | item.get("light_sleep_duration", 0) 73 | ) 74 | rem_sleep_duration = self._format_duration( 75 | item.get("rem_sleep_duration", 0) 76 | ) 77 | total_sleep_duration = self._format_duration( 78 | item.get("total_sleep_duration", 0) 79 | ) 80 | time_in_bed = self._format_duration(item.get("time_in_bed", 0)) 81 | 82 | # Format bedtime timestamps 83 | bedtime_start = self._format_time(item.get("bedtime_start", "")) 84 | bedtime_end = self._format_time(item.get("bedtime_end", "")) 85 | 86 | # Extract readiness data if available 87 | readiness = item.get("readiness", {}) 88 | readiness_score = readiness.get("score") if readiness else None 89 | readiness_contributors = ( 90 | readiness.get("contributors", {}) if readiness else {} 91 | ) 92 | 93 | # Create transformed item 94 | transformed_item = { 95 | "day": item.get("day"), 96 | "bedtime_start": bedtime_start, 97 | "bedtime_end": bedtime_end, 98 | "awake_time": awake_time, 99 | "deep_sleep_duration": deep_sleep_duration, 100 | "light_sleep_duration": light_sleep_duration, 101 | "rem_sleep_duration": rem_sleep_duration, 102 | "total_sleep_duration": total_sleep_duration, 103 | "time_in_bed": time_in_bed, 104 | "efficiency": item.get("efficiency"), 105 | "latency": item.get("latency"), 106 | "restless_periods": item.get("restless_periods"), 107 | "average_breath": item.get("average_breath"), 108 | "average_heart_rate": item.get("average_heart_rate"), 109 | "average_hrv": item.get("average_hrv"), 110 | "lowest_heart_rate": item.get("lowest_heart_rate"), 111 | } 112 | 113 | # Add readiness data if available 114 | if readiness_score is not None: 115 | transformed_item["readiness_score"] = readiness_score 116 | transformed_item["readiness_contributors"] = readiness_contributors 117 | 118 | transformed_data.append(transformed_item) 119 | 120 | # Return with the original structure but with transformed data 121 | return {"data": transformed_data} 122 | 123 | def _format_duration(self, seconds: int) -> str: 124 | """ 125 | Format duration in seconds to a human-readable string. 126 | 127 | Args: 128 | seconds: Duration in seconds 129 | 130 | Returns: 131 | Formatted string (e.g., "7 hours, 30 minutes, 15 seconds") 132 | """ 133 | hours, remainder = divmod(seconds, 3600) 134 | minutes, seconds = divmod(remainder, 60) 135 | 136 | parts = [] 137 | if hours > 0: 138 | parts.append(f"{hours} hour{'s' if hours != 1 else ''}") 139 | if minutes > 0: 140 | parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}") 141 | if seconds > 0: 142 | parts.append(f"{seconds} second{'s' if seconds != 1 else ''}") 143 | 144 | if not parts: 145 | return "0 seconds" 146 | 147 | return ", ".join(parts) 148 | 149 | def _format_time(self, timestamp: str) -> str: 150 | """ 151 | Format ISO timestamp to a time-only string. 152 | 153 | Args: 154 | timestamp: ISO timestamp string 155 | 156 | Returns: 157 | Formatted time string (e.g., "10:30 PM") 158 | """ 159 | if not timestamp: 160 | return "" 161 | 162 | try: 163 | dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00")) 164 | return dt.strftime("%I:%M %p") 165 | except (ValueError, TypeError): 166 | return timestamp 167 | 168 | def get_daily_sleep_data( 169 | self, start_date: date, end_date: Optional[date] = None 170 | ) -> dict[str, Any]: 171 | """ 172 | Get daily sleep data for a specific date range. 173 | 174 | Args: 175 | start_date: Start date for the query 176 | end_date: End date for the query (optional, defaults to start_date) 177 | 178 | Returns: 179 | Dictionary containing daily sleep data 180 | """ 181 | if end_date is None: 182 | end_date = start_date 183 | 184 | params = { 185 | "start_date": start_date.isoformat(), 186 | "end_date": end_date.isoformat(), 187 | } 188 | 189 | url = f"{self.BASE_URL}/daily_sleep" 190 | response = self.client.get(url, headers=self.headers, params=params) 191 | 192 | if response.status_code != 200: 193 | error_msg = f"Error {response.status_code}: {response.text}" 194 | raise Exception(error_msg) 195 | 196 | # Get the raw response 197 | raw_data = response.json() 198 | 199 | # Transform the data - just return the data array directly 200 | transformed_data = [] 201 | 202 | for item in raw_data.get("data", []): 203 | # Create transformed item without the id field 204 | transformed_item = {k: v for k, v in item.items() if k != "id"} 205 | 206 | # Format any duration fields if present 207 | if "total_sleep_duration" in transformed_item: 208 | transformed_item["total_sleep_duration"] = self._format_duration( 209 | transformed_item["total_sleep_duration"] 210 | ) 211 | 212 | transformed_data.append(transformed_item) 213 | 214 | # Return with the original structure but with transformed data 215 | return {"data": transformed_data} 216 | 217 | def get_readiness_data( 218 | self, start_date: date, end_date: Optional[date] = None 219 | ) -> dict[str, Any]: 220 | """ 221 | Get readiness data for a specific date range. 222 | 223 | Args: 224 | start_date: Start date for the query 225 | end_date: End date for the query (optional, defaults to start_date) 226 | 227 | Returns: 228 | Dictionary containing readiness data 229 | """ 230 | if end_date is None: 231 | end_date = start_date 232 | 233 | params = { 234 | "start_date": start_date.isoformat(), 235 | "end_date": end_date.isoformat(), 236 | } 237 | 238 | url = f"{self.BASE_URL}/daily_readiness" 239 | response = self.client.get(url, headers=self.headers, params=params) 240 | 241 | if response.status_code != 200: 242 | error_msg = f"Error {response.status_code}: {response.text}" 243 | raise Exception(error_msg) 244 | 245 | # Get the raw response 246 | raw_data = response.json() 247 | 248 | # Transform the data - just return the data array directly 249 | transformed_data = [] 250 | 251 | for item in raw_data.get("data", []): 252 | # Create transformed item without the id field and timestamp fields 253 | transformed_item = { 254 | k: v 255 | for k, v in item.items() 256 | if k != "id" and not k.endswith("_timestamp") and k != "timestamp" 257 | } 258 | transformed_data.append(transformed_item) 259 | 260 | # Return with the original structure but with transformed data 261 | return {"data": transformed_data} 262 | 263 | def get_resilience_data( 264 | self, start_date: date, end_date: Optional[date] = None 265 | ) -> dict[str, Any]: 266 | """ 267 | Get resilience data for a specific date range. 268 | 269 | Args: 270 | start_date: Start date for the query 271 | end_date: End date for the query (optional, defaults to start_date) 272 | 273 | Returns: 274 | Dictionary containing resilience data 275 | """ 276 | if end_date is None: 277 | end_date = start_date 278 | 279 | params = { 280 | "start_date": start_date.isoformat(), 281 | "end_date": end_date.isoformat(), 282 | } 283 | 284 | url = f"{self.BASE_URL}/daily_resilience" 285 | response = self.client.get(url, headers=self.headers, params=params) 286 | 287 | if response.status_code != 200: 288 | error_msg = f"Error {response.status_code}: {response.text}" 289 | raise Exception(error_msg) 290 | 291 | # Get the raw response 292 | raw_data = response.json() 293 | 294 | # Transform the data - just return the data array directly 295 | transformed_data = [] 296 | 297 | for item in raw_data.get("data", []): 298 | # Create transformed item without the id field 299 | transformed_item = {k: v for k, v in item.items() if k != "id"} 300 | transformed_data.append(transformed_item) 301 | 302 | # Return with the original structure but with transformed data 303 | return {"data": transformed_data} 304 | 305 | def close(self) -> None: 306 | """Close the HTTP client.""" 307 | self.client.close() 308 | 309 | 310 | def parse_date(date_str: str) -> date: 311 | """ 312 | Parse a date string in ISO format (YYYY-MM-DD). 313 | 314 | Args: 315 | date_str: Date string in ISO format 316 | 317 | Returns: 318 | Date object 319 | """ 320 | try: 321 | return date.fromisoformat(date_str) 322 | except ValueError as err: 323 | raise ValueError( 324 | f"Invalid date format: {date_str}. Expected format: YYYY-MM-DD" 325 | ) from err 326 | 327 | 328 | # Create MCP server and OuraClient at module level 329 | mcp = FastMCP("Oura API MCP Server") 330 | 331 | # Default access token (will be overridden in main or by direct assignment) 332 | default_token = os.environ.get("OURA_API_TOKEN") 333 | oura_client = OuraClient(default_token) if default_token else None 334 | 335 | 336 | # Add tools for querying sleep data 337 | @mcp.tool() 338 | def get_sleep_data(start_date: str, end_date: str) -> dict[str, Any]: 339 | """ 340 | Get sleep data for a specific date range. 341 | 342 | Args: 343 | start_date: Start date in ISO format (YYYY-MM-DD) 344 | end_date: End date in ISO format (YYYY-MM-DD) 345 | 346 | Returns: 347 | Dictionary containing sleep data 348 | """ 349 | if oura_client is None: 350 | return {"error": "Oura client not initialized. Please provide an access token."} 351 | 352 | try: 353 | start = parse_date(start_date) 354 | end = parse_date(end_date) 355 | return oura_client.get_sleep_data(start, end) 356 | except Exception as e: 357 | return {"error": str(e)} 358 | 359 | 360 | @mcp.tool() 361 | def get_readiness_data(start_date: str, end_date: str) -> dict[str, Any]: 362 | """ 363 | Get readiness data for a specific date range. 364 | 365 | Args: 366 | start_date: Start date in ISO format (YYYY-MM-DD) 367 | end_date: End date in ISO format (YYYY-MM-DD) 368 | 369 | Returns: 370 | Dictionary containing readiness data 371 | """ 372 | if oura_client is None: 373 | return {"error": "Oura client not initialized. Please provide an access token."} 374 | 375 | try: 376 | start = parse_date(start_date) 377 | end = parse_date(end_date) 378 | return oura_client.get_readiness_data(start, end) 379 | except Exception as e: 380 | return {"error": str(e)} 381 | 382 | 383 | @mcp.tool() 384 | def get_resilience_data(start_date: str, end_date: str) -> dict[str, Any]: 385 | """ 386 | Get resilience data for a specific date range. 387 | 388 | Args: 389 | start_date: Start date in ISO format (YYYY-MM-DD) 390 | end_date: End date in ISO format (YYYY-MM-DD) 391 | 392 | Returns: 393 | Dictionary containing resilience data 394 | """ 395 | if oura_client is None: 396 | return {"error": "Oura client not initialized. Please provide an access token."} 397 | 398 | try: 399 | start = parse_date(start_date) 400 | end = parse_date(end_date) 401 | return oura_client.get_resilience_data(start, end) 402 | except Exception as e: 403 | return {"error": str(e)} 404 | 405 | 406 | # Add tools for querying today's data 407 | @mcp.tool() 408 | def get_today_sleep_data() -> dict[str, Any]: 409 | """ 410 | Get sleep data for today. 411 | 412 | Returns: 413 | Dictionary containing sleep data for today 414 | """ 415 | if oura_client is None: 416 | return {"error": "Oura client not initialized. Please provide an access token."} 417 | 418 | try: 419 | today = date.today() 420 | return oura_client.get_sleep_data(today, today) 421 | except Exception as e: 422 | return {"error": str(e)} 423 | 424 | 425 | @mcp.tool() 426 | def get_today_readiness_data() -> dict[str, Any]: 427 | """ 428 | Get readiness data for today. 429 | 430 | Returns: 431 | Dictionary containing readiness data for today 432 | """ 433 | if oura_client is None: 434 | return {"error": "Oura client not initialized. Please provide an access token."} 435 | 436 | try: 437 | today = date.today() 438 | return oura_client.get_readiness_data(today, today) 439 | except Exception as e: 440 | return {"error": str(e)} 441 | 442 | 443 | @mcp.tool() 444 | def get_today_resilience_data() -> dict[str, Any]: 445 | """ 446 | Get resilience data for today. 447 | 448 | Returns: 449 | Dictionary containing resilience data for today 450 | """ 451 | if oura_client is None: 452 | return {"error": "Oura client not initialized. Please provide an access token."} 453 | 454 | try: 455 | today = date.today() 456 | return oura_client.get_resilience_data(today, today) 457 | except Exception as e: 458 | return {"error": str(e)} 459 | 460 | 461 | def main() -> None: 462 | print("Starting Oura MCP server!") 463 | mcp.run(transport="stdio") 464 | 465 | 466 | if __name__ == "__main__": 467 | main() 468 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 1 3 | requires-python = ">=3.12" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.8.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "idna" }, 20 | { name = "sniffio" }, 21 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 22 | ] 23 | sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, 26 | ] 27 | 28 | [[package]] 29 | name = "black" 30 | version = "25.1.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | dependencies = [ 33 | { name = "click" }, 34 | { name = "mypy-extensions" }, 35 | { name = "packaging" }, 36 | { name = "pathspec" }, 37 | { name = "platformdirs" }, 38 | ] 39 | sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } 40 | wheels = [ 41 | { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, 42 | { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, 43 | { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, 44 | { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, 45 | { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, 46 | { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, 47 | { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, 48 | { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, 49 | { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, 50 | ] 51 | 52 | [[package]] 53 | name = "certifi" 54 | version = "2025.1.31" 55 | source = { registry = "https://pypi.org/simple" } 56 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 57 | wheels = [ 58 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 59 | ] 60 | 61 | [[package]] 62 | name = "click" 63 | version = "8.1.8" 64 | source = { registry = "https://pypi.org/simple" } 65 | dependencies = [ 66 | { name = "colorama", marker = "sys_platform == 'win32'" }, 67 | ] 68 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 69 | wheels = [ 70 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 71 | ] 72 | 73 | [[package]] 74 | name = "colorama" 75 | version = "0.4.6" 76 | source = { registry = "https://pypi.org/simple" } 77 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 78 | wheels = [ 79 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 80 | ] 81 | 82 | [[package]] 83 | name = "h11" 84 | version = "0.14.0" 85 | source = { registry = "https://pypi.org/simple" } 86 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 87 | wheels = [ 88 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 89 | ] 90 | 91 | [[package]] 92 | name = "httpcore" 93 | version = "1.0.7" 94 | source = { registry = "https://pypi.org/simple" } 95 | dependencies = [ 96 | { name = "certifi" }, 97 | { name = "h11" }, 98 | ] 99 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 100 | wheels = [ 101 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 102 | ] 103 | 104 | [[package]] 105 | name = "httpx" 106 | version = "0.28.1" 107 | source = { registry = "https://pypi.org/simple" } 108 | dependencies = [ 109 | { name = "anyio" }, 110 | { name = "certifi" }, 111 | { name = "httpcore" }, 112 | { name = "idna" }, 113 | ] 114 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 115 | wheels = [ 116 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 117 | ] 118 | 119 | [[package]] 120 | name = "httpx-sse" 121 | version = "0.4.0" 122 | source = { registry = "https://pypi.org/simple" } 123 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 124 | wheels = [ 125 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 126 | ] 127 | 128 | [[package]] 129 | name = "idna" 130 | version = "3.10" 131 | source = { registry = "https://pypi.org/simple" } 132 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 133 | wheels = [ 134 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 135 | ] 136 | 137 | [[package]] 138 | name = "markdown-it-py" 139 | version = "3.0.0" 140 | source = { registry = "https://pypi.org/simple" } 141 | dependencies = [ 142 | { name = "mdurl" }, 143 | ] 144 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 145 | wheels = [ 146 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 147 | ] 148 | 149 | [[package]] 150 | name = "mcp" 151 | version = "1.3.0" 152 | source = { registry = "https://pypi.org/simple" } 153 | dependencies = [ 154 | { name = "anyio" }, 155 | { name = "httpx" }, 156 | { name = "httpx-sse" }, 157 | { name = "pydantic" }, 158 | { name = "pydantic-settings" }, 159 | { name = "sse-starlette" }, 160 | { name = "starlette" }, 161 | { name = "uvicorn" }, 162 | ] 163 | sdist = { url = "https://files.pythonhosted.org/packages/6b/b6/81e5f2490290351fc97bf46c24ff935128cb7d34d68e3987b522f26f7ada/mcp-1.3.0.tar.gz", hash = "sha256:f409ae4482ce9d53e7ac03f3f7808bcab735bdfc0fba937453782efb43882d45", size = 150235 } 164 | wheels = [ 165 | { url = "https://files.pythonhosted.org/packages/d0/d2/a9e87b506b2094f5aa9becc1af5178842701b27217fa43877353da2577e3/mcp-1.3.0-py3-none-any.whl", hash = "sha256:2829d67ce339a249f803f22eba5e90385eafcac45c94b00cab6cef7e8f217211", size = 70672 }, 166 | ] 167 | 168 | [package.optional-dependencies] 169 | cli = [ 170 | { name = "python-dotenv" }, 171 | { name = "typer" }, 172 | ] 173 | 174 | [[package]] 175 | name = "mdurl" 176 | version = "0.1.2" 177 | source = { registry = "https://pypi.org/simple" } 178 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 179 | wheels = [ 180 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 181 | ] 182 | 183 | [[package]] 184 | name = "mypy" 185 | version = "1.15.0" 186 | source = { registry = "https://pypi.org/simple" } 187 | dependencies = [ 188 | { name = "mypy-extensions" }, 189 | { name = "typing-extensions" }, 190 | ] 191 | sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } 192 | wheels = [ 193 | { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, 194 | { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, 195 | { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, 196 | { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, 197 | { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, 198 | { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, 199 | { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, 200 | { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, 201 | { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, 202 | { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, 203 | { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, 204 | { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, 205 | { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, 206 | ] 207 | 208 | [[package]] 209 | name = "mypy-extensions" 210 | version = "1.0.0" 211 | source = { registry = "https://pypi.org/simple" } 212 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } 213 | wheels = [ 214 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, 215 | ] 216 | 217 | [[package]] 218 | name = "oura-mcp-server" 219 | version = "0.1.2" 220 | source = { editable = "." } 221 | dependencies = [ 222 | { name = "httpx" }, 223 | { name = "mcp", extra = ["cli"] }, 224 | ] 225 | 226 | [package.optional-dependencies] 227 | dev = [ 228 | { name = "black" }, 229 | { name = "mypy" }, 230 | { name = "ruff" }, 231 | ] 232 | 233 | [package.metadata] 234 | requires-dist = [ 235 | { name = "black", marker = "extra == 'dev'", specifier = ">=23.0.0" }, 236 | { name = "httpx", specifier = ">=0.24.0" }, 237 | { name = "mcp", extras = ["cli"], specifier = ">=1.3.0" }, 238 | { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0.0" }, 239 | { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, 240 | ] 241 | provides-extras = ["dev"] 242 | 243 | [[package]] 244 | name = "packaging" 245 | version = "24.2" 246 | source = { registry = "https://pypi.org/simple" } 247 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 248 | wheels = [ 249 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 250 | ] 251 | 252 | [[package]] 253 | name = "pathspec" 254 | version = "0.12.1" 255 | source = { registry = "https://pypi.org/simple" } 256 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } 257 | wheels = [ 258 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, 259 | ] 260 | 261 | [[package]] 262 | name = "platformdirs" 263 | version = "4.3.6" 264 | source = { registry = "https://pypi.org/simple" } 265 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } 266 | wheels = [ 267 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, 268 | ] 269 | 270 | [[package]] 271 | name = "pydantic" 272 | version = "2.10.6" 273 | source = { registry = "https://pypi.org/simple" } 274 | dependencies = [ 275 | { name = "annotated-types" }, 276 | { name = "pydantic-core" }, 277 | { name = "typing-extensions" }, 278 | ] 279 | sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } 280 | wheels = [ 281 | { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, 282 | ] 283 | 284 | [[package]] 285 | name = "pydantic-core" 286 | version = "2.27.2" 287 | source = { registry = "https://pypi.org/simple" } 288 | dependencies = [ 289 | { name = "typing-extensions" }, 290 | ] 291 | sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } 292 | wheels = [ 293 | { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, 294 | { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, 295 | { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, 296 | { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, 297 | { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, 298 | { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, 299 | { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, 300 | { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, 301 | { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, 302 | { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, 303 | { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, 304 | { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, 305 | { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, 306 | { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, 307 | { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, 308 | { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, 309 | { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, 310 | { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, 311 | { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, 312 | { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, 313 | { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, 314 | { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, 315 | { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, 316 | { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, 317 | { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, 318 | { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, 319 | { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, 320 | { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, 321 | ] 322 | 323 | [[package]] 324 | name = "pydantic-settings" 325 | version = "2.8.1" 326 | source = { registry = "https://pypi.org/simple" } 327 | dependencies = [ 328 | { name = "pydantic" }, 329 | { name = "python-dotenv" }, 330 | ] 331 | sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } 332 | wheels = [ 333 | { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, 334 | ] 335 | 336 | [[package]] 337 | name = "pygments" 338 | version = "2.19.1" 339 | source = { registry = "https://pypi.org/simple" } 340 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 341 | wheels = [ 342 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 343 | ] 344 | 345 | [[package]] 346 | name = "python-dotenv" 347 | version = "1.0.1" 348 | source = { registry = "https://pypi.org/simple" } 349 | sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } 350 | wheels = [ 351 | { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, 352 | ] 353 | 354 | [[package]] 355 | name = "rich" 356 | version = "13.9.4" 357 | source = { registry = "https://pypi.org/simple" } 358 | dependencies = [ 359 | { name = "markdown-it-py" }, 360 | { name = "pygments" }, 361 | ] 362 | sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } 363 | wheels = [ 364 | { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, 365 | ] 366 | 367 | [[package]] 368 | name = "ruff" 369 | version = "0.9.8" 370 | source = { registry = "https://pypi.org/simple" } 371 | sdist = { url = "https://files.pythonhosted.org/packages/e9/59/ac745a2492986a4c900c73a7a3a10eb4d7a3853e43443519bceecae5eefc/ruff-0.9.8.tar.gz", hash = "sha256:12d455f2be6fe98accbea2487bbb8eaec716c760bf60b45e7e13f76f913f56e9", size = 3715230 } 372 | wheels = [ 373 | { url = "https://files.pythonhosted.org/packages/5c/1c/9de3a463279e9a203104fe80881d7dcfd8377eb52b3d5608770ea6ff3dc6/ruff-0.9.8-py3-none-linux_armv6l.whl", hash = "sha256:d236f0ce0190bbc6fa9b4c4b85e916fb4c50fd087e6558af1bf5a45eb20e374d", size = 10036520 }, 374 | { url = "https://files.pythonhosted.org/packages/35/10/a4eda083ad0b60a4c16bc9a68c6eda59de69a3a58913a0b62541f5c551cd/ruff-0.9.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:59fac6922b336d0c38df199761ade561563e1b7636e3a2b767b9ee5a68aa9cbf", size = 10827099 }, 375 | { url = "https://files.pythonhosted.org/packages/57/34/cf7e18f2315926ee2c98f931717e1302f8c3face189f5b99352eb48c5373/ruff-0.9.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a82082ec72bde2166ec138055307396c4d4e543fd97266dc2bfa24284cb30af6", size = 10161605 }, 376 | { url = "https://files.pythonhosted.org/packages/f3/08/5e7e8fc08d193e3520b9227249a00bc9b8da9e0a20bf97bef03a9a9f0d38/ruff-0.9.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e06635d12321605d1d11226c7d3c6b1245a0df498099868d14b4e353b3f0ac22", size = 10338840 }, 377 | { url = "https://files.pythonhosted.org/packages/54/c0/df2187618b87334867ea7942f6d2d79ea3e5cb3ed709cfa5c8df115d3715/ruff-0.9.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:65961815bb35d427e957940d13b2a1d0a67d8b245d3a7e0b5a4a2058536d3532", size = 9891009 }, 378 | { url = "https://files.pythonhosted.org/packages/fb/39/8fc50b87203e71e6f3281111813ab0f3d6095cb1129efc2cf4c33e977657/ruff-0.9.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c18356beaef174797ad83f11debc5569e96afa73a549b2d073912565cfc4cfd1", size = 11413420 }, 379 | { url = "https://files.pythonhosted.org/packages/6a/7b/53cd91b99a1cef31126859fb98fdc347c47e0047a9ec51391ea28f08284d/ruff-0.9.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a1dfc443bee0288ea926a4d9ecfd858bf94ddf0a03a256c63e81b2b6dccdfc7d", size = 12138017 }, 380 | { url = "https://files.pythonhosted.org/packages/1a/d4/949a328934202a2d2641dcd759761d8ed806e672cbbad0a88e20a46c43ba/ruff-0.9.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc86d5a85cd5ab1d5aff1650f038aa34681d0692cc2467aa9ddef37bd56ea3f9", size = 11592548 }, 381 | { url = "https://files.pythonhosted.org/packages/c6/8e/8520a4d97eefedb8472811fd5144fcb1fcbb29f83bb9bb4356a468e7eeac/ruff-0.9.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:66662aa19535d58fe6d04e5b59a39e495b102f2f5a2a1b9698e240eb78f429ef", size = 13787277 }, 382 | { url = "https://files.pythonhosted.org/packages/24/68/f1629e00dbc5c9adcd31f12f9438b68c50ab0eefca8b07e11b6c94f11b09/ruff-0.9.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:733647b2fe9367e1aa049c0eba296363746f3bc0dbfd454b0bc4b7b46cdf0146", size = 11275421 }, 383 | { url = "https://files.pythonhosted.org/packages/28/65/c133462f179b925e49910532c7d7b5a244df5995c155cd2ab9452545926f/ruff-0.9.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:100031be9777f67af7f61b4d4eea2a0531ed6788940aca4360f6b9aae317c53b", size = 10220273 }, 384 | { url = "https://files.pythonhosted.org/packages/d8/1e/9339aef1896470380838385dbdc91f62998c37d406009f05ff3b810265f3/ruff-0.9.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2f090758d58b4667d9022eee1085a854db93d800279e5a177ebda5adc1faf639", size = 9860266 }, 385 | { url = "https://files.pythonhosted.org/packages/ca/33/2a2934860df6bd3665776ec686fc33910e7a1b793bdd2f000aea3e8f0b65/ruff-0.9.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f774998b9c9a062510533aba9b53085de6be6d41e13a7a0bd086af8a40e838c3", size = 10831947 }, 386 | { url = "https://files.pythonhosted.org/packages/74/66/0a7677b1cda4b2367a654f9af57f1dbe58f38c6704da88aee9bbf3941197/ruff-0.9.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6ef7cc80626264ab8ab4d68b359ba867b8a52b0830a9643cd31289146dd40892", size = 11306767 }, 387 | { url = "https://files.pythonhosted.org/packages/c4/90/6c98f94e036c8acdf19bd8f3f84d246e43cbcc950e24dc7ff85d2f2735ba/ruff-0.9.8-py3-none-win32.whl", hash = "sha256:54b57b623a683e696a1ede99db95500763c1badafe105b6ad8d8e9d96e385ae2", size = 10234107 }, 388 | { url = "https://files.pythonhosted.org/packages/f5/e7/35877491b4b64daa35cbd7dc06aa5969e7bb1cd6f69e5594e4376dfbc16d/ruff-0.9.8-py3-none-win_amd64.whl", hash = "sha256:b0878103b2fb8af55ad701308a69ce713108ad346c3a3a143ebcd1e13829c9a7", size = 11357825 }, 389 | { url = "https://files.pythonhosted.org/packages/6e/98/de77a972b2e9ded804dea5d4e6fbfa093d99e81092602567787ea87979af/ruff-0.9.8-py3-none-win_arm64.whl", hash = "sha256:e459a4fc4150fcc60da26c59a6a4b70878c60a99df865a71cf6f958dc68c419a", size = 10435420 }, 390 | ] 391 | 392 | [[package]] 393 | name = "shellingham" 394 | version = "1.5.4" 395 | source = { registry = "https://pypi.org/simple" } 396 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } 397 | wheels = [ 398 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, 399 | ] 400 | 401 | [[package]] 402 | name = "sniffio" 403 | version = "1.3.1" 404 | source = { registry = "https://pypi.org/simple" } 405 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 406 | wheels = [ 407 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 408 | ] 409 | 410 | [[package]] 411 | name = "sse-starlette" 412 | version = "2.2.1" 413 | source = { registry = "https://pypi.org/simple" } 414 | dependencies = [ 415 | { name = "anyio" }, 416 | { name = "starlette" }, 417 | ] 418 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 419 | wheels = [ 420 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 421 | ] 422 | 423 | [[package]] 424 | name = "starlette" 425 | version = "0.46.0" 426 | source = { registry = "https://pypi.org/simple" } 427 | dependencies = [ 428 | { name = "anyio" }, 429 | ] 430 | sdist = { url = "https://files.pythonhosted.org/packages/44/b6/fb9a32e3c5d59b1e383c357534c63c2d3caa6f25bf3c59dd89d296ecbaec/starlette-0.46.0.tar.gz", hash = "sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50", size = 2575568 } 431 | wheels = [ 432 | { url = "https://files.pythonhosted.org/packages/41/94/8af675a62e3c91c2dee47cf92e602cfac86e8767b1a1ac3caf1b327c2ab0/starlette-0.46.0-py3-none-any.whl", hash = "sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038", size = 71991 }, 433 | ] 434 | 435 | [[package]] 436 | name = "typer" 437 | version = "0.15.1" 438 | source = { registry = "https://pypi.org/simple" } 439 | dependencies = [ 440 | { name = "click" }, 441 | { name = "rich" }, 442 | { name = "shellingham" }, 443 | { name = "typing-extensions" }, 444 | ] 445 | sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/dca7b219718afd37a0068f4f2530a727c2b74a8b6e8e0c0080a4c0de4fcd/typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a", size = 99789 } 446 | wheels = [ 447 | { url = "https://files.pythonhosted.org/packages/d0/cc/0a838ba5ca64dc832aa43f727bd586309846b0ffb2ce52422543e6075e8a/typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847", size = 44908 }, 448 | ] 449 | 450 | [[package]] 451 | name = "typing-extensions" 452 | version = "4.12.2" 453 | source = { registry = "https://pypi.org/simple" } 454 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 455 | wheels = [ 456 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 457 | ] 458 | 459 | [[package]] 460 | name = "uvicorn" 461 | version = "0.34.0" 462 | source = { registry = "https://pypi.org/simple" } 463 | dependencies = [ 464 | { name = "click" }, 465 | { name = "h11" }, 466 | ] 467 | sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } 468 | wheels = [ 469 | { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, 470 | ] 471 | --------------------------------------------------------------------------------