├── .env.example ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .python-version ├── LICENSE ├── README.md ├── amap_mcp_server ├── __init__.py ├── __main__.py ├── __pycache__ │ └── server.cpython-312.pyc └── server.py ├── pyproject.toml └── uv.lock /.env.example: -------------------------------------------------------------------------------- 1 | AMAP_MAPS_API_KEY= 2 | PORT= -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | # Only publish to PyPI on release 14 | if: github.event_name == 'release' 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install build twine 25 | - name: Build package 26 | run: python -m build 27 | - name: Publish package 28 | env: 29 | TWINE_USERNAME: __token__ 30 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 31 | run: | 32 | twine upload dist/* 33 | 34 | test: 35 | runs-on: ubuntu-latest 36 | # Run tests on every push to main 37 | if: github.event_name == 'push' 38 | steps: 39 | - uses: actions/checkout@v4 40 | - name: Set up Python 41 | uses: actions/setup-python@v4 42 | with: 43 | python-version: '3.x' 44 | - name: Install dependencies 45 | run: | 46 | python -m pip install --upgrade pip 47 | pip install build 48 | - name: Build package 49 | run: python -m build 50 | - name: Test build 51 | env: 52 | AMAP_MAPS_API_KEY: mock_key_for_testing 53 | run: | 54 | python -m pip install dist/*.whl 55 | python -c "import amap_mcp_server" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 sugarforever 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 高德地图 MCP Server 2 | 3 | 该高德地图 MCP Server 发布在 [PyPI](https://pypi.org/project/amap-mcp-server/)。 4 | 5 | ## MCP 工具列表 6 | 7 | 本服务提供以下工具: 8 | 9 | ### 地理编码工具 10 | 11 | #### maps_regeocode 12 | 将一个高德经纬度坐标转换为行政区划地址信息 13 | 14 | **参数:** 15 | - `location`: 经纬度坐标 16 | 17 | #### maps_geo 18 | 将详细的结构化地址转换为经纬度坐标。支持对地标性名胜景区、建筑物名称解析为经纬度坐标 19 | 20 | **参数:** 21 | - `address`: 结构化地址 22 | - `city` (可选): 指定查询的城市 23 | 24 | ### 位置服务工具 25 | 26 | #### maps_ip_location 27 | IP 定位根据用户输入的 IP 地址,定位 IP 的所在位置 28 | 29 | **参数:** 30 | - `ip`: IP地址 31 | 32 | #### maps_weather 33 | 根据城市名称或者标准adcode查询指定城市的天气 34 | 35 | **参数:** 36 | - `city`: 城市名称或者adcode 37 | 38 | ### 路线规划工具 39 | 40 | #### 骑行路线 41 | ##### maps_bicycling_by_coordinates 42 | 骑行路径规划用于规划骑行通勤方案,规划时会考虑天桥、单行线、封路等情况。最大支持 500km 的骑行路线规划 43 | 44 | **参数:** 45 | - `origin`: 起点经纬度坐标 46 | - `destination`: 终点经纬度坐标 47 | 48 | ##### maps_bicycling_by_address 49 | 骑行路径规划(地址版),使用地址进行骑行路线规划,推荐优先使用此工具 50 | 51 | **参数:** 52 | - `origin_address`: 起点地址(例如:"北京市朝阳区阜通东大街6号") 53 | - `destination_address`: 终点地址(例如:"北京市海淀区上地十街10号") 54 | - `origin_city` (可选): 起点所在城市,用于提高地理编码准确性 55 | - `destination_city` (可选): 终点所在城市,用于提高地理编码准确性 56 | 57 | #### 步行路线 58 | ##### maps_direction_walking_by_coordinates 59 | 步行路径规划 API 可以根据输入起点终点经纬度坐标规划100km 以内的步行通勤方案,并且返回通勤方案的数据 60 | 61 | **参数:** 62 | - `origin`: 起点经纬度坐标 63 | - `destination`: 终点经纬度坐标 64 | 65 | ##### maps_direction_walking_by_address 66 | 步行路径规划(地址版),使用地址进行步行路线规划,推荐优先使用此工具 67 | 68 | **参数:** 69 | - `origin_address`: 起点地址(例如:"北京市朝阳区阜通东大街6号") 70 | - `destination_address`: 终点地址(例如:"北京市海淀区上地十街10号") 71 | - `origin_city` (可选): 起点所在城市,用于提高地理编码准确性 72 | - `destination_city` (可选): 终点所在城市,用于提高地理编码准确性 73 | 74 | #### 驾车路线 75 | ##### maps_direction_driving_by_coordinates 76 | 驾车路径规划 API 可以根据用户起终点经纬度坐标规划以小客车、轿车通勤出行的方案,并且返回通勤方案的数据 77 | 78 | **参数:** 79 | - `origin`: 起点经纬度坐标 80 | - `destination`: 终点经纬度坐标 81 | 82 | ##### maps_direction_driving_by_address 83 | 驾车路径规划(地址版),使用地址进行驾车路线规划,推荐优先使用此工具 84 | 85 | **参数:** 86 | - `origin_address`: 起点地址(例如:"北京市朝阳区阜通东大街6号") 87 | - `destination_address`: 终点地址(例如:"北京市海淀区上地十街10号") 88 | - `origin_city` (可选): 起点所在城市,用于提高地理编码准确性 89 | - `destination_city` (可选): 终点所在城市,用于提高地理编码准确性 90 | 91 | #### 公共交通路线 92 | ##### maps_direction_transit_integrated_by_coordinates 93 | 根据用户起终点经纬度坐标规划综合各类公共(火车、公交、地铁)交通方式的通勤方案,并且返回通勤方案的数据,跨城场景下必须传起点城市与终点城市 94 | 95 | **参数:** 96 | - `origin`: 起点经纬度坐标 97 | - `destination`: 终点经纬度坐标 98 | - `city`: 起点城市 99 | - `cityd`: 终点城市 100 | 101 | ##### maps_direction_transit_integrated_by_address 102 | 公共交通路径规划(地址版),使用地址进行公共交通路线规划,推荐优先使用此工具 103 | 104 | **参数:** 105 | - `origin_address`: 起点地址(例如:"北京市朝阳区阜通东大街6号") 106 | - `destination_address`: 终点地址(例如:"北京市海淀区上地十街10号") 107 | - `origin_city`: 起点所在城市(跨城交通必需) 108 | - `destination_city`: 终点所在城市(跨城交通必需) 109 | 110 | ### 距离测量工具 111 | 112 | #### maps_distance 113 | 测量两个经纬度坐标之间的距离,支持驾车、步行以及球面距离测量 114 | 115 | **参数:** 116 | - `origins`: 起点经纬度坐标 117 | - `destination`: 终点经纬度坐标 118 | - `type` (可选,默认为"1"): 测量类型 119 | 120 | ### POI搜索工具 121 | 122 | #### maps_text_search 123 | 关键词搜索 API 根据用户输入的关键字进行 POI 搜索,并返回相关的信息 124 | 125 | **参数:** 126 | - `keywords`: 搜索关键词 127 | - `city` (可选): 查询城市 128 | - `citylimit` (可选,默认为"false"): 是否限制城市范围内搜索 129 | 130 | #### maps_around_search 131 | 周边搜,根据用户传入关键词以及坐标location,搜索出radius半径范围的POI 132 | 133 | **参数:** 134 | - `location`: 中心点经纬度坐标 135 | - `radius` (可选,默认为"1000"): 搜索半径 136 | - `keywords` (可选): 搜索关键词 137 | 138 | #### maps_search_detail 139 | 查询关键词搜或者周边搜获取到的POI ID的详细信息 140 | 141 | **参数:** 142 | - `id`: POI ID 143 | 144 | ## 配置方法 145 | 146 | 要使用此服务,您需要在应用中添加以下MCP配置。服务支持三种传输方式:`stdio`(默认)、`sse` 和 `streamable-http`。 147 | 148 | ### stdio 传输(默认) 149 | 150 | 直接在客户端配置如下MCP Server即可。 151 | 152 | ```json 153 | { 154 | "mcpServers": { 155 | "amap-mcp-server": { 156 | "command": "uvx", 157 | "args": [ 158 | "amap-mcp-server" 159 | ], 160 | "env": { 161 | "AMAP_MAPS_API_KEY": "your valid amap maps api key" 162 | } 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | ### SSE 传输 169 | 170 | SSE传输支持实时数据推送,适合远程部署MCP Server。 171 | 172 | 本地以SSE运行 `amap-mcp-server`: 173 | 174 | ```bash 175 | $ export AMAP_MAPS_API_KEY=你的有效API Key 176 | $ uvx amap-mcp-server sse 177 | 178 | INFO: Started server process [50125] 179 | INFO: Waiting for application startup. 180 | INFO: Application startup complete. 181 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) 182 | ``` 183 | 184 | MCP客户端配置: 185 | 186 | ```json 187 | { 188 | "mcpServers": { 189 | "amap-mcp-server": { 190 | "url": "http://0.0.0.0:8000/sse" 191 | } 192 | } 193 | } 194 | ``` 195 | 196 | ### Streamable HTTP 传输 197 | 198 | 本地以Streamable HTTP运行 `amap-mcp-server`: 199 | 200 | ```bash 201 | $ export AMAP_MAPS_API_KEY=你的有效API Key 202 | $ uvx amap-mcp-server streamable-http 203 | 204 | INFO: Started server process [50227] 205 | INFO: Waiting for application startup. 206 | StreamableHTTP session manager started 207 | INFO: Application startup complete. 208 | INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) 209 | ``` 210 | 211 | MCP客户端配置: 212 | 213 | ```json 214 | { 215 | "mcpServers": { 216 | "amap-mcp-server": { 217 | "url": "http://localhost:8000/mcp" 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | 您可以在[高德开放平台](https://lbs.amap.com/)注册并获取API密钥。 -------------------------------------------------------------------------------- /amap_mcp_server/__init__.py: -------------------------------------------------------------------------------- 1 | """amap_mcp_server package""" 2 | 3 | import argparse 4 | from .server import mcp 5 | 6 | def main(): 7 | """Entry point for the MCP server""" 8 | # Parse command line arguments 9 | parser = argparse.ArgumentParser(description="Amap MCP Server") 10 | parser.add_argument('transport', nargs='?', default='stdio', choices=['stdio', 'sse', 'streamable-http'], 11 | help='Transport type (stdio, sse, or streamable-http)') 12 | args = parser.parse_args() 13 | 14 | # Run the MCP server with the specified transport 15 | mcp.run(transport=args.transport) 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /amap_mcp_server/__main__.py: -------------------------------------------------------------------------------- 1 | from amap_mcp_server import main 2 | 3 | main() -------------------------------------------------------------------------------- /amap_mcp_server/__pycache__/server.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sugarforever/amap-mcp-server/478680ed9a26d914920697f7d09701bdefe28f96/amap_mcp_server/__pycache__/server.cpython-312.pyc -------------------------------------------------------------------------------- /amap_mcp_server/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | from typing import Any, Dict, List, Optional 5 | import requests 6 | from mcp.server.fastmcp import FastMCP 7 | 8 | def get_api_key() -> str: 9 | """Get the Amap Maps API key from environment variables""" 10 | api_key = os.getenv("AMAP_MAPS_API_KEY") 11 | if not api_key: 12 | raise ValueError("AMAP_MAPS_API_KEY environment variable is required") 13 | return api_key 14 | 15 | AMAP_MAPS_API_KEY = get_api_key() 16 | 17 | mcp = FastMCP("amap-maps") 18 | 19 | @mcp.tool() 20 | def maps_regeocode(location: str) -> Dict[str, Any]: 21 | """将一个高德经纬度坐标转换为行政区划地址信息""" 22 | try: 23 | response = requests.get( 24 | "https://restapi.amap.com/v3/geocode/regeo", 25 | params={ 26 | "key": AMAP_MAPS_API_KEY, 27 | "location": location 28 | } 29 | ) 30 | response.raise_for_status() 31 | data = response.json() 32 | 33 | if data["status"] != "1": 34 | return {"error": f"RGeocoding failed: {data.get('info') or data.get('infocode')}"} 35 | 36 | return { 37 | "province": data["regeocode"]["addressComponent"]["province"], 38 | "city": data["regeocode"]["addressComponent"]["city"], 39 | "district": data["regeocode"]["addressComponent"]["district"] 40 | } 41 | except requests.exceptions.RequestException as e: 42 | return {"error": f"Request failed: {str(e)}"} 43 | 44 | @mcp.tool() 45 | def maps_geo(address: str, city: Optional[str] = None) -> Dict[str, Any]: 46 | """将详细的结构化地址转换为经纬度坐标。支持对地标性名胜景区、建筑物名称解析为经纬度坐标""" 47 | try: 48 | params = { 49 | "key": AMAP_MAPS_API_KEY, 50 | "address": address 51 | } 52 | if city: 53 | params["city"] = city 54 | 55 | response = requests.get( 56 | "https://restapi.amap.com/v3/geocode/geo", 57 | params=params 58 | ) 59 | response.raise_for_status() 60 | data = response.json() 61 | 62 | if data["status"] != "1": 63 | return {"error": f"Geocoding failed: {data.get('info') or data.get('infocode')}"} 64 | 65 | geocodes = data.get("geocodes", []) 66 | results = [] 67 | for geo in geocodes: 68 | results.append({ 69 | "country": geo.get("country"), 70 | "province": geo.get("province"), 71 | "city": geo.get("city"), 72 | "citycode": geo.get("citycode"), 73 | "district": geo.get("district"), 74 | "street": geo.get("street"), 75 | "number": geo.get("number"), 76 | "adcode": geo.get("adcode"), 77 | "location": geo.get("location"), 78 | "level": geo.get("level") 79 | }) 80 | return {"return": results} 81 | except requests.exceptions.RequestException as e: 82 | return {"error": f"Request failed: {str(e)}"} 83 | 84 | @mcp.tool() 85 | def maps_ip_location(ip: str) -> Dict[str, Any]: 86 | """IP 定位根据用户输入的 IP 地址,定位 IP 的所在位置""" 87 | try: 88 | response = requests.get( 89 | "https://restapi.amap.com/v3/ip", 90 | params={ 91 | "key": AMAP_MAPS_API_KEY, 92 | "ip": ip 93 | } 94 | ) 95 | response.raise_for_status() 96 | data = response.json() 97 | 98 | if data["status"] != "1": 99 | return {"error": f"IP Location failed: {data.get('info') or data.get('infocode')}"} 100 | 101 | return { 102 | "province": data.get("province"), 103 | "city": data.get("city"), 104 | "adcode": data.get("adcode"), 105 | "rectangle": data.get("rectangle") 106 | } 107 | except requests.exceptions.RequestException as e: 108 | return {"error": f"Request failed: {str(e)}"} 109 | 110 | @mcp.tool() 111 | def maps_weather(city: str) -> Dict[str, Any]: 112 | """根据城市名称或者标准adcode查询指定城市的天气""" 113 | try: 114 | response = requests.get( 115 | "https://restapi.amap.com/v3/weather/weatherInfo", 116 | params={ 117 | "key": AMAP_MAPS_API_KEY, 118 | "city": city, 119 | "extensions": "all" 120 | } 121 | ) 122 | response.raise_for_status() 123 | data = response.json() 124 | 125 | if data["status"] != "1": 126 | return {"error": f"Get weather failed: {data.get('info') or data.get('infocode')}"} 127 | 128 | forecasts = data.get("forecasts", []) 129 | if not forecasts: 130 | return {"error": "No forecast data available"} 131 | 132 | return { 133 | "city": forecasts[0]["city"], 134 | "forecasts": forecasts[0]["casts"] 135 | } 136 | except requests.exceptions.RequestException as e: 137 | return {"error": f"Request failed: {str(e)}"} 138 | 139 | @mcp.tool() 140 | def maps_bicycling_by_address(origin_address: str, destination_address: str, origin_city: Optional[str] = None, destination_city: Optional[str] = None) -> Dict[str, Any]: 141 | """Plans a bicycle route between two locations using addresses. Unless you have a specific reason to use coordinates, it's recommended to use this tool. 142 | 143 | Args: 144 | origin_address (str): Starting point address (e.g. "北京市朝阳区阜通东大街6号") 145 | destination_address (str): Ending point address (e.g. "北京市海淀区上地十街10号") 146 | origin_city (Optional[str]): Optional city name for the origin address to improve geocoding accuracy 147 | destination_city (Optional[str]): Optional city name for the destination address to improve geocoding accuracy 148 | 149 | Returns: 150 | Dict[str, Any]: Route information including distance, duration, and turn-by-turn instructions. 151 | Considers bridges, one-way streets, and road closures. Supports routes up to 500km. 152 | """ 153 | try: 154 | # Convert origin address to coordinates 155 | origin_result = maps_geo(origin_address, origin_city) 156 | if "error" in origin_result: 157 | return {"error": f"Failed to geocode origin address: {origin_result['error']}"} 158 | 159 | if not origin_result.get("return") or not origin_result["return"]: 160 | return {"error": "No geocoding results found for origin address"} 161 | 162 | origin_location = origin_result["return"][0].get("location") 163 | if not origin_location: 164 | return {"error": "Could not extract coordinates from origin geocoding result"} 165 | 166 | # Convert destination address to coordinates 167 | destination_result = maps_geo(destination_address, destination_city) 168 | if "error" in destination_result: 169 | return {"error": f"Failed to geocode destination address: {destination_result['error']}"} 170 | 171 | if not destination_result.get("return") or not destination_result["return"]: 172 | return {"error": "No geocoding results found for destination address"} 173 | 174 | destination_location = destination_result["return"][0].get("location") 175 | if not destination_location: 176 | return {"error": "Could not extract coordinates from destination geocoding result"} 177 | 178 | # Use the coordinates to plan the bicycle route 179 | route_result = maps_bicycling_by_coordinates(origin_location, destination_location) 180 | 181 | # Add address information to the result 182 | if "error" not in route_result: 183 | route_result["addresses"] = { 184 | "origin": { 185 | "address": origin_address, 186 | "coordinates": origin_location 187 | }, 188 | "destination": { 189 | "address": destination_address, 190 | "coordinates": destination_location 191 | } 192 | } 193 | 194 | return route_result 195 | except Exception as e: 196 | return {"error": f"Route planning failed: {str(e)}"} 197 | 198 | @mcp.tool() 199 | def maps_bicycling_by_coordinates(origin_coordinates: str, destination_coordinates: str) -> Dict[str, Any]: 200 | """Plans a bicycle route between two coordinates. 201 | 202 | Args: 203 | origin_coordinates (str): Starting point coordinates in the format "longitude,latitude" (e.g. "116.434307,39.90909") 204 | destination_coordinates (str): Ending point coordinates in the format "longitude,latitude" (e.g. "116.434307,39.90909") 205 | 206 | Returns: 207 | Dict[str, Any]: Route information including distance, duration, and turn-by-turn instructions. 208 | Considers bridges, one-way streets, and road closures. Supports routes up to 500km. 209 | """ 210 | try: 211 | response = requests.get( 212 | "https://restapi.amap.com/v4/direction/bicycling", 213 | params={ 214 | "key": AMAP_MAPS_API_KEY, 215 | "origin": origin_coordinates, 216 | "destination": destination_coordinates 217 | } 218 | ) 219 | response.raise_for_status() 220 | data = response.json() 221 | 222 | if data.get("errcode") != 0: 223 | return {"error": f"Direction bicycling failed: {data.get('info') or data.get('infocode')}"} 224 | 225 | paths = [] 226 | for path in data["data"]["paths"]: 227 | steps = [] 228 | for step in path["steps"]: 229 | steps.append({ 230 | "instruction": step.get("instruction"), 231 | "road": step.get("road"), 232 | "distance": step.get("distance"), 233 | "orientation": step.get("orientation"), 234 | "duration": step.get("duration") 235 | }) 236 | paths.append({ 237 | "distance": path.get("distance"), 238 | "duration": path.get("duration"), 239 | "steps": steps 240 | }) 241 | 242 | return { 243 | "data": { 244 | "origin": data["data"]["origin"], 245 | "destination": data["data"]["destination"], 246 | "paths": paths 247 | } 248 | } 249 | except requests.exceptions.RequestException as e: 250 | return {"error": f"Request failed: {str(e)}"} 251 | 252 | @mcp.tool() 253 | def maps_direction_walking_by_address(origin_address: str, destination_address: str, origin_city: Optional[str] = None, destination_city: Optional[str] = None) -> Dict[str, Any]: 254 | """Plans a walking route between two locations using addresses. Unless you have a specific reason to use coordinates, it's recommended to use this tool. 255 | 256 | Args: 257 | origin_address (str): Starting point address (e.g. "北京市朝阳区阜通东大街6号") 258 | destination_address (str): Ending point address (e.g. "北京市海淀区上地十街10号") 259 | origin_city (Optional[str]): Optional city name for the origin address to improve geocoding accuracy 260 | destination_city (Optional[str]): Optional city name for the destination address to improve geocoding accuracy 261 | 262 | Returns: 263 | Dict[str, Any]: Route information including distance, duration, and turn-by-turn instructions. 264 | Supports routes up to 100km. 265 | """ 266 | try: 267 | # Convert origin address to coordinates 268 | origin_result = maps_geo(origin_address, origin_city) 269 | if "error" in origin_result: 270 | return {"error": f"Failed to geocode origin address: {origin_result['error']}"} 271 | 272 | if not origin_result.get("return") or not origin_result["return"]: 273 | return {"error": "No geocoding results found for origin address"} 274 | 275 | origin_location = origin_result["return"][0].get("location") 276 | if not origin_location: 277 | return {"error": "Could not extract coordinates from origin geocoding result"} 278 | 279 | # Convert destination address to coordinates 280 | destination_result = maps_geo(destination_address, destination_city) 281 | if "error" in destination_result: 282 | return {"error": f"Failed to geocode destination address: {destination_result['error']}"} 283 | 284 | if not destination_result.get("return") or not destination_result["return"]: 285 | return {"error": "No geocoding results found for destination address"} 286 | 287 | destination_location = destination_result["return"][0].get("location") 288 | if not destination_location: 289 | return {"error": "Could not extract coordinates from destination geocoding result"} 290 | 291 | # Use the coordinates to plan the walking route 292 | route_result = maps_direction_walking_by_coordinates(origin_location, destination_location) 293 | 294 | # Add address information to the result 295 | if "error" not in route_result: 296 | route_result["addresses"] = { 297 | "origin": { 298 | "address": origin_address, 299 | "coordinates": origin_location 300 | }, 301 | "destination": { 302 | "address": destination_address, 303 | "coordinates": destination_location 304 | } 305 | } 306 | 307 | return route_result 308 | except Exception as e: 309 | return {"error": f"Route planning failed: {str(e)}"} 310 | 311 | @mcp.tool() 312 | def maps_direction_walking_by_coordinates(origin: str, destination: str) -> Dict[str, Any]: 313 | """步行路径规划 API 可以根据输入起点终点经纬度坐标规划100km 以内的步行通勤方案,并且返回通勤方案的数据 314 | 315 | Args: 316 | origin (str): 起点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 317 | destination (str): 终点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 318 | 319 | Returns: 320 | Dict[str, Any]: 包含距离、时长和详细导航信息的路线数据 321 | """ 322 | try: 323 | response = requests.get( 324 | "https://restapi.amap.com/v3/direction/walking", 325 | params={ 326 | "key": AMAP_MAPS_API_KEY, 327 | "origin": origin, 328 | "destination": destination 329 | } 330 | ) 331 | response.raise_for_status() 332 | data = response.json() 333 | 334 | if data["status"] != "1": 335 | return {"error": f"Direction Walking failed: {data.get('info') or data.get('infocode')}"} 336 | 337 | paths = [] 338 | for path in data["route"]["paths"]: 339 | steps = [] 340 | for step in path["steps"]: 341 | steps.append({ 342 | "instruction": step.get("instruction"), 343 | "road": step.get("road"), 344 | "distance": step.get("distance"), 345 | "orientation": step.get("orientation"), 346 | "duration": step.get("duration") 347 | }) 348 | paths.append({ 349 | "distance": path.get("distance"), 350 | "duration": path.get("duration"), 351 | "steps": steps 352 | }) 353 | 354 | return { 355 | "route": { 356 | "origin": data["route"]["origin"], 357 | "destination": data["route"]["destination"], 358 | "paths": paths 359 | } 360 | } 361 | except requests.exceptions.RequestException as e: 362 | return {"error": f"Request failed: {str(e)}"} 363 | 364 | @mcp.tool() 365 | def maps_direction_driving_by_address(origin_address: str, destination_address: str, origin_city: Optional[str] = None, destination_city: Optional[str] = None) -> Dict[str, Any]: 366 | """Plans a driving route between two locations using addresses. Unless you have a specific reason to use coordinates, it's recommended to use this tool. 367 | 368 | Args: 369 | origin_address (str): Starting point address (e.g. "北京市朝阳区阜通东大街6号") 370 | destination_address (str): Ending point address (e.g. "北京市海淀区上地十街10号") 371 | origin_city (Optional[str]): Optional city name for the origin address to improve geocoding accuracy 372 | destination_city (Optional[str]): Optional city name for the destination address to improve geocoding accuracy 373 | 374 | Returns: 375 | Dict[str, Any]: Route information including distance, duration, and turn-by-turn instructions. 376 | Considers traffic conditions and road restrictions. 377 | """ 378 | try: 379 | # Convert origin address to coordinates 380 | origin_result = maps_geo(origin_address, origin_city) 381 | if "error" in origin_result: 382 | return {"error": f"Failed to geocode origin address: {origin_result['error']}"} 383 | 384 | if not origin_result.get("return") or not origin_result["return"]: 385 | return {"error": "No geocoding results found for origin address"} 386 | 387 | origin_location = origin_result["return"][0].get("location") 388 | if not origin_location: 389 | return {"error": "Could not extract coordinates from origin geocoding result"} 390 | 391 | # Convert destination address to coordinates 392 | destination_result = maps_geo(destination_address, destination_city) 393 | if "error" in destination_result: 394 | return {"error": f"Failed to geocode destination address: {destination_result['error']}"} 395 | 396 | if not destination_result.get("return") or not destination_result["return"]: 397 | return {"error": "No geocoding results found for destination address"} 398 | 399 | destination_location = destination_result["return"][0].get("location") 400 | if not destination_location: 401 | return {"error": "Could not extract coordinates from destination geocoding result"} 402 | 403 | # Use the coordinates to plan the driving route 404 | route_result = maps_direction_driving_by_coordinates(origin_location, destination_location) 405 | 406 | # Add address information to the result 407 | if "error" not in route_result: 408 | route_result["addresses"] = { 409 | "origin": { 410 | "address": origin_address, 411 | "coordinates": origin_location 412 | }, 413 | "destination": { 414 | "address": destination_address, 415 | "coordinates": destination_location 416 | } 417 | } 418 | 419 | return route_result 420 | except Exception as e: 421 | return {"error": f"Route planning failed: {str(e)}"} 422 | 423 | @mcp.tool() 424 | def maps_direction_driving_by_coordinates(origin: str, destination: str) -> Dict[str, Any]: 425 | """驾车路径规划 API 可以根据用户起终点经纬度坐标规划以小客车、轿车通勤出行的方案,并且返回通勤方案的数据 426 | 427 | Args: 428 | origin (str): 起点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 429 | destination (str): 终点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 430 | 431 | Returns: 432 | Dict[str, Any]: 包含距离、时长和详细导航信息的路线数据 433 | """ 434 | try: 435 | response = requests.get( 436 | "https://restapi.amap.com/v3/direction/driving", 437 | params={ 438 | "key": AMAP_MAPS_API_KEY, 439 | "origin": origin, 440 | "destination": destination 441 | } 442 | ) 443 | response.raise_for_status() 444 | data = response.json() 445 | 446 | if data["status"] != "1": 447 | return {"error": f"Direction Driving failed: {data.get('info') or data.get('infocode')}"} 448 | 449 | paths = [] 450 | for path in data["route"]["paths"]: 451 | steps = [] 452 | for step in path["steps"]: 453 | steps.append({ 454 | "instruction": step.get("instruction"), 455 | "road": step.get("road"), 456 | "distance": step.get("distance"), 457 | "orientation": step.get("orientation"), 458 | "duration": step.get("duration") 459 | }) 460 | paths.append({ 461 | "path": path.get("path"), 462 | "distance": path.get("distance"), 463 | "duration": path.get("duration"), 464 | "steps": steps 465 | }) 466 | 467 | return { 468 | "route": { 469 | "origin": data["route"]["origin"], 470 | "destination": data["route"]["destination"], 471 | "paths": paths 472 | } 473 | } 474 | except requests.exceptions.RequestException as e: 475 | return {"error": f"Request failed: {str(e)}"} 476 | 477 | @mcp.tool() 478 | def maps_direction_transit_integrated_by_address(origin_address: str, destination_address: str, origin_city: str, destination_city: str) -> Dict[str, Any]: 479 | """Plans a public transit route between two locations using addresses. Unless you have a specific reason to use coordinates, it's recommended to use this tool. 480 | 481 | Args: 482 | origin_address (str): Starting point address (e.g. "北京市朝阳区阜通东大街6号") 483 | destination_address (str): Ending point address (e.g. "北京市海淀区上地十街10号") 484 | origin_city (str): City name for the origin address (required for cross-city transit) 485 | destination_city (str): City name for the destination address (required for cross-city transit) 486 | 487 | Returns: 488 | Dict[str, Any]: Route information including distance, duration, and detailed transit instructions. 489 | Considers various public transit options including buses, subways, and trains. 490 | """ 491 | try: 492 | # Convert origin address to coordinates 493 | origin_result = maps_geo(origin_address, origin_city) 494 | if "error" in origin_result: 495 | return {"error": f"Failed to geocode origin address: {origin_result['error']}"} 496 | 497 | if not origin_result.get("return") or not origin_result["return"]: 498 | return {"error": "No geocoding results found for origin address"} 499 | 500 | origin_location = origin_result["return"][0].get("location") 501 | if not origin_location: 502 | return {"error": "Could not extract coordinates from origin geocoding result"} 503 | 504 | # Convert destination address to coordinates 505 | destination_result = maps_geo(destination_address, destination_city) 506 | if "error" in destination_result: 507 | return {"error": f"Failed to geocode destination address: {destination_result['error']}"} 508 | 509 | if not destination_result.get("return") or not destination_result["return"]: 510 | return {"error": "No geocoding results found for destination address"} 511 | 512 | destination_location = destination_result["return"][0].get("location") 513 | if not destination_location: 514 | return {"error": "Could not extract coordinates from destination geocoding result"} 515 | 516 | # Use the coordinates to plan the transit route 517 | route_result = maps_direction_transit_integrated_by_coordinates(origin_location, destination_location, origin_city, destination_city) 518 | 519 | # Add address information to the result 520 | if "error" not in route_result: 521 | route_result["addresses"] = { 522 | "origin": { 523 | "address": origin_address, 524 | "coordinates": origin_location 525 | }, 526 | "destination": { 527 | "address": destination_address, 528 | "coordinates": destination_location 529 | } 530 | } 531 | 532 | return route_result 533 | except Exception as e: 534 | return {"error": f"Route planning failed: {str(e)}"} 535 | 536 | @mcp.tool() 537 | def maps_direction_transit_integrated_by_coordinates(origin: str, destination: str, city: str, cityd: str) -> Dict[str, Any]: 538 | """根据用户起终点经纬度坐标规划综合各类公共(火车、公交、地铁)交通方式的通勤方案,并且返回通勤方案的数据,跨城场景下必须传起点城市与终点城市 539 | 540 | Args: 541 | origin (str): 起点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 542 | destination (str): 终点经纬度坐标,格式为"经度,纬度" (例如:"116.434307,39.90909") 543 | city (str): 起点城市名称 544 | cityd (str): 终点城市名称 545 | 546 | Returns: 547 | Dict[str, Any]: 包含距离、时长和详细公共交通信息的路线数据 548 | """ 549 | try: 550 | response = requests.get( 551 | "https://restapi.amap.com/v3/direction/transit/integrated", 552 | params={ 553 | "key": AMAP_MAPS_API_KEY, 554 | "origin": origin, 555 | "destination": destination, 556 | "city": city, 557 | "cityd": cityd 558 | } 559 | ) 560 | response.raise_for_status() 561 | data = response.json() 562 | 563 | if data["status"] != "1": 564 | return {"error": f"Direction Transit Integrated failed: {data.get('info') or data.get('infocode')}"} 565 | 566 | transits = [] 567 | if data["route"].get("transits"): 568 | for transit in data["route"]["transits"]: 569 | segments = [] 570 | if transit.get("segments"): 571 | for segment in transit["segments"]: 572 | walking_steps = [] 573 | if segment.get("walking", {}).get("steps"): 574 | for step in segment["walking"]["steps"]: 575 | walking_steps.append({ 576 | "instruction": step.get("instruction"), 577 | "road": step.get("road"), 578 | "distance": step.get("distance"), 579 | "action": step.get("action"), 580 | "assistant_action": step.get("assistant_action") 581 | }) 582 | 583 | buslines = [] 584 | if segment.get("bus", {}).get("buslines"): 585 | for busline in segment["bus"]["buslines"]: 586 | via_stops = [] 587 | if busline.get("via_stops"): 588 | for stop in busline["via_stops"]: 589 | via_stops.append({"name": stop.get("name")}) 590 | 591 | buslines.append({ 592 | "name": busline.get("name"), 593 | "departure_stop": {"name": busline.get("departure_stop", {}).get("name")}, 594 | "arrival_stop": {"name": busline.get("arrival_stop", {}).get("name")}, 595 | "distance": busline.get("distance"), 596 | "duration": busline.get("duration"), 597 | "via_stops": via_stops 598 | }) 599 | 600 | segments.append({ 601 | "walking": { 602 | "origin": segment.get("walking", {}).get("origin"), 603 | "destination": segment.get("walking", {}).get("destination"), 604 | "distance": segment.get("walking", {}).get("distance"), 605 | "duration": segment.get("walking", {}).get("duration"), 606 | "steps": walking_steps 607 | }, 608 | "bus": {"buslines": buslines}, 609 | "entrance": {"name": segment.get("entrance", {}).get("name")}, 610 | "exit": {"name": segment.get("exit", {}).get("name")}, 611 | "railway": { 612 | "name": segment.get("railway", {}).get("name"), 613 | "trip": segment.get("railway", {}).get("trip") 614 | } 615 | }) 616 | 617 | transits.append({ 618 | "duration": transit.get("duration"), 619 | "walking_distance": transit.get("walking_distance"), 620 | "segments": segments 621 | }) 622 | 623 | return { 624 | "route": { 625 | "origin": data["route"]["origin"], 626 | "destination": data["route"]["destination"], 627 | "distance": data["route"].get("distance"), 628 | "transits": transits 629 | } 630 | } 631 | except requests.exceptions.RequestException as e: 632 | return {"error": f"Request failed: {str(e)}"} 633 | 634 | @mcp.tool() 635 | def maps_distance(origins: str, destination: str, type: str = "1") -> Dict[str, Any]: 636 | """测量两个经纬度坐标之间的距离,支持驾车、步行以及球面距离测量""" 637 | try: 638 | response = requests.get( 639 | "https://restapi.amap.com/v3/distance", 640 | params={ 641 | "key": AMAP_MAPS_API_KEY, 642 | "origins": origins, 643 | "destination": destination, 644 | "type": type 645 | } 646 | ) 647 | response.raise_for_status() 648 | data = response.json() 649 | 650 | if data["status"] != "1": 651 | return {"error": f"Direction Distance failed: {data.get('info') or data.get('infocode')}"} 652 | 653 | results = [] 654 | for result in data["results"]: 655 | results.append({ 656 | "origin_id": result.get("origin_id"), 657 | "dest_id": result.get("dest_id"), 658 | "distance": result.get("distance"), 659 | "duration": result.get("duration") 660 | }) 661 | 662 | return {"results": results} 663 | except requests.exceptions.RequestException as e: 664 | return {"error": f"Request failed: {str(e)}"} 665 | 666 | @mcp.tool() 667 | def maps_text_search(keywords: str, city: str = "", citylimit: str = "false") -> Dict[str, Any]: 668 | """关键词搜索 API 根据用户输入的关键字进行 POI 搜索,并返回相关的信息""" 669 | try: 670 | response = requests.get( 671 | "https://restapi.amap.com/v3/place/text", 672 | params={ 673 | "key": AMAP_MAPS_API_KEY, 674 | "keywords": keywords, 675 | "city": city, 676 | "citylimit": citylimit 677 | } 678 | ) 679 | response.raise_for_status() 680 | data = response.json() 681 | 682 | if data["status"] != "1": 683 | return {"error": f"Text Search failed: {data.get('info') or data.get('infocode')}"} 684 | 685 | suggestion_cities = [] 686 | if data.get("suggestion", {}).get("cities"): 687 | for city in data["suggestion"]["cities"]: 688 | suggestion_cities.append({"name": city.get("name")}) 689 | 690 | pois = [] 691 | for poi in data.get("pois", []): 692 | pois.append({ 693 | "id": poi.get("id"), 694 | "name": poi.get("name"), 695 | "address": poi.get("address"), 696 | "typecode": poi.get("typecode") 697 | }) 698 | 699 | return { 700 | "suggestion": { 701 | "keywords": data.get("suggestion", {}).get("keywords"), 702 | "cities": suggestion_cities 703 | }, 704 | "pois": pois 705 | } 706 | except requests.exceptions.RequestException as e: 707 | return {"error": f"Request failed: {str(e)}"} 708 | 709 | @mcp.tool() 710 | def maps_around_search(location: str, radius: str = "1000", keywords: str = "") -> Dict[str, Any]: 711 | """周边搜,根据用户传入关键词以及坐标location,搜索出radius半径范围的POI""" 712 | try: 713 | response = requests.get( 714 | "https://restapi.amap.com/v3/place/around", 715 | params={ 716 | "key": AMAP_MAPS_API_KEY, 717 | "location": location, 718 | "radius": radius, 719 | "keywords": keywords 720 | } 721 | ) 722 | response.raise_for_status() 723 | data = response.json() 724 | 725 | if data["status"] != "1": 726 | return {"error": f"Around Search failed: {data.get('info') or data.get('infocode')}"} 727 | 728 | pois = [] 729 | for poi in data.get("pois", []): 730 | pois.append({ 731 | "id": poi.get("id"), 732 | "name": poi.get("name"), 733 | "address": poi.get("address"), 734 | "typecode": poi.get("typecode") 735 | }) 736 | 737 | return {"pois": pois} 738 | except requests.exceptions.RequestException as e: 739 | return {"error": f"Request failed: {str(e)}"} 740 | 741 | @mcp.tool() 742 | def maps_search_detail(id: str) -> Dict[str, Any]: 743 | """查询关键词搜或者周边搜获取到的POI ID的详细信息""" 744 | try: 745 | response = requests.get( 746 | "https://restapi.amap.com/v3/place/detail", 747 | params={ 748 | "key": AMAP_MAPS_API_KEY, 749 | "id": id 750 | } 751 | ) 752 | response.raise_for_status() 753 | data = response.json() 754 | 755 | if data["status"] != "1": 756 | return {"error": f"Get poi detail failed: {data.get('info') or data.get('infocode')}"} 757 | 758 | if not data.get("pois"): 759 | return {"error": "No POI found"} 760 | 761 | poi = data["pois"][0] 762 | result = { 763 | "id": poi.get("id"), 764 | "name": poi.get("name"), 765 | "location": poi.get("location"), 766 | "address": poi.get("address"), 767 | "business_area": poi.get("business_area"), 768 | "city": poi.get("cityname"), 769 | "type": poi.get("type"), 770 | "alias": poi.get("alias") 771 | } 772 | 773 | # Add biz_ext data if available 774 | if poi.get("biz_ext"): 775 | result.update(poi["biz_ext"]) 776 | 777 | return result 778 | except requests.exceptions.RequestException as e: 779 | return {"error": f"Request failed: {str(e)}"} 780 | 781 | if __name__ == "__main__": 782 | # Parse command line arguments 783 | parser = argparse.ArgumentParser(description="Amap MCP Server") 784 | parser.add_argument('transport', nargs='?', default='stdio', choices=['stdio', 'sse', 'streamable-http'], 785 | help='Transport type (stdio, sse, or streamable-http)') 786 | args = parser.parse_args() 787 | 788 | # Run the MCP server with the specified transport 789 | mcp.run(transport=args.transport) -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "amap-mcp-server" 7 | version = "0.1.9" 8 | description = "Python版本高德地图MCP Server" 9 | readme = "README.md" 10 | requires-python = ">=3.10" 11 | classifiers = [ 12 | "Programming Language :: Python :: 3", 13 | "License :: OSI Approved :: MIT License", 14 | "Operating System :: OS Independent", 15 | ] 16 | dependencies = [ 17 | "mcp==1.8.1", 18 | "requests>=2.32.3", 19 | ] 20 | 21 | [project.scripts] 22 | amap-mcp-server = "amap_mcp_server:main" 23 | 24 | [project.urls] 25 | "Homepage" = "https://github.com/sugarforever/amap-mcp-server" 26 | "Bug Tracker" = "https://github.com/sugarforever/amap-mcp-server/issues" 27 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.10" 3 | 4 | [[package]] 5 | name = "amap-mcp-server" 6 | version = "0.1.7" 7 | source = { editable = "." } 8 | dependencies = [ 9 | { name = "mcp" }, 10 | { name = "requests" }, 11 | ] 12 | 13 | [package.metadata] 14 | requires-dist = [ 15 | { name = "mcp", specifier = "==1.8.1" }, 16 | { name = "requests", specifier = ">=2.32.3" }, 17 | ] 18 | 19 | [[package]] 20 | name = "annotated-types" 21 | version = "0.7.0" 22 | source = { registry = "https://pypi.org/simple" } 23 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 26 | ] 27 | 28 | [[package]] 29 | name = "anyio" 30 | version = "4.9.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | dependencies = [ 33 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 34 | { name = "idna" }, 35 | { name = "sniffio" }, 36 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 37 | ] 38 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } 39 | wheels = [ 40 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, 41 | ] 42 | 43 | [[package]] 44 | name = "certifi" 45 | version = "2025.1.31" 46 | source = { registry = "https://pypi.org/simple" } 47 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 48 | wheels = [ 49 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 50 | ] 51 | 52 | [[package]] 53 | name = "charset-normalizer" 54 | version = "3.4.1" 55 | source = { registry = "https://pypi.org/simple" } 56 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } 57 | wheels = [ 58 | { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, 59 | { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, 60 | { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, 61 | { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, 62 | { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, 63 | { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, 64 | { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, 65 | { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, 66 | { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, 67 | { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, 68 | { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, 69 | { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, 70 | { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, 71 | { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, 72 | { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, 73 | { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, 74 | { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, 75 | { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, 76 | { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, 77 | { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, 78 | { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, 79 | { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, 80 | { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, 81 | { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, 82 | { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, 83 | { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, 84 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, 85 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, 86 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, 87 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, 88 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, 89 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, 90 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, 91 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, 92 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, 93 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, 94 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, 95 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, 96 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, 97 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, 98 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, 99 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, 100 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, 101 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, 102 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, 103 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, 104 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, 105 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, 106 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, 107 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, 108 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, 109 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, 110 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, 111 | ] 112 | 113 | [[package]] 114 | name = "click" 115 | version = "8.1.8" 116 | source = { registry = "https://pypi.org/simple" } 117 | dependencies = [ 118 | { name = "colorama", marker = "platform_system == 'Windows'" }, 119 | ] 120 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 121 | wheels = [ 122 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 123 | ] 124 | 125 | [[package]] 126 | name = "colorama" 127 | version = "0.4.6" 128 | source = { registry = "https://pypi.org/simple" } 129 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 130 | wheels = [ 131 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 132 | ] 133 | 134 | [[package]] 135 | name = "exceptiongroup" 136 | version = "1.2.2" 137 | source = { registry = "https://pypi.org/simple" } 138 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } 139 | wheels = [ 140 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, 141 | ] 142 | 143 | [[package]] 144 | name = "h11" 145 | version = "0.14.0" 146 | source = { registry = "https://pypi.org/simple" } 147 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 148 | wheels = [ 149 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 150 | ] 151 | 152 | [[package]] 153 | name = "httpcore" 154 | version = "1.0.8" 155 | source = { registry = "https://pypi.org/simple" } 156 | dependencies = [ 157 | { name = "certifi" }, 158 | { name = "h11" }, 159 | ] 160 | sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 } 161 | wheels = [ 162 | { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 }, 163 | ] 164 | 165 | [[package]] 166 | name = "httpx" 167 | version = "0.28.1" 168 | source = { registry = "https://pypi.org/simple" } 169 | dependencies = [ 170 | { name = "anyio" }, 171 | { name = "certifi" }, 172 | { name = "httpcore" }, 173 | { name = "idna" }, 174 | ] 175 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 176 | wheels = [ 177 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 178 | ] 179 | 180 | [[package]] 181 | name = "httpx-sse" 182 | version = "0.4.0" 183 | source = { registry = "https://pypi.org/simple" } 184 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 185 | wheels = [ 186 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 187 | ] 188 | 189 | [[package]] 190 | name = "idna" 191 | version = "3.10" 192 | source = { registry = "https://pypi.org/simple" } 193 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 194 | wheels = [ 195 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 196 | ] 197 | 198 | [[package]] 199 | name = "mcp" 200 | version = "1.8.1" 201 | source = { registry = "https://pypi.org/simple" } 202 | dependencies = [ 203 | { name = "anyio" }, 204 | { name = "httpx" }, 205 | { name = "httpx-sse" }, 206 | { name = "pydantic" }, 207 | { name = "pydantic-settings" }, 208 | { name = "python-multipart" }, 209 | { name = "sse-starlette" }, 210 | { name = "starlette" }, 211 | { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 212 | ] 213 | sdist = { url = "https://files.pythonhosted.org/packages/7c/13/16b712e8a3be6a736b411df2fc6b4e75eb1d3e99b1cd57a3a1decf17f612/mcp-1.8.1.tar.gz", hash = "sha256:ec0646271d93749f784d2316fb5fe6102fb0d1be788ec70a9e2517e8f2722c0e", size = 265605 } 214 | wheels = [ 215 | { url = "https://files.pythonhosted.org/packages/1c/5d/91cf0d40e40ae9ecf8d4004e0f9611eea86085aa0b5505493e0ff53972da/mcp-1.8.1-py3-none-any.whl", hash = "sha256:948e03783859fa35abe05b9b6c0a1d5519be452fc079dc8d7f682549591c1770", size = 119761 }, 216 | ] 217 | 218 | [[package]] 219 | name = "pydantic" 220 | version = "2.11.3" 221 | source = { registry = "https://pypi.org/simple" } 222 | dependencies = [ 223 | { name = "annotated-types" }, 224 | { name = "pydantic-core" }, 225 | { name = "typing-extensions" }, 226 | { name = "typing-inspection" }, 227 | ] 228 | sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } 229 | wheels = [ 230 | { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, 231 | ] 232 | 233 | [[package]] 234 | name = "pydantic-core" 235 | version = "2.33.1" 236 | source = { registry = "https://pypi.org/simple" } 237 | dependencies = [ 238 | { name = "typing-extensions" }, 239 | ] 240 | sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } 241 | wheels = [ 242 | { url = "https://files.pythonhosted.org/packages/38/ea/5f572806ab4d4223d11551af814d243b0e3e02cc6913def4d1fe4a5ca41c/pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", size = 2044021 }, 243 | { url = "https://files.pythonhosted.org/packages/8c/d1/f86cc96d2aa80e3881140d16d12ef2b491223f90b28b9a911346c04ac359/pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", size = 1861742 }, 244 | { url = "https://files.pythonhosted.org/packages/37/08/fbd2cd1e9fc735a0df0142fac41c114ad9602d1c004aea340169ae90973b/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", size = 1910414 }, 245 | { url = "https://files.pythonhosted.org/packages/7f/73/3ac217751decbf8d6cb9443cec9b9eb0130eeada6ae56403e11b486e277e/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", size = 1996848 }, 246 | { url = "https://files.pythonhosted.org/packages/9a/f5/5c26b265cdcff2661e2520d2d1e9db72d117ea00eb41e00a76efe68cb009/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", size = 2141055 }, 247 | { url = "https://files.pythonhosted.org/packages/5d/14/a9c3cee817ef2f8347c5ce0713e91867a0dceceefcb2973942855c917379/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", size = 2753806 }, 248 | { url = "https://files.pythonhosted.org/packages/f2/68/866ce83a51dd37e7c604ce0050ff6ad26de65a7799df89f4db87dd93d1d6/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", size = 2007777 }, 249 | { url = "https://files.pythonhosted.org/packages/b6/a8/36771f4404bb3e49bd6d4344da4dede0bf89cc1e01f3b723c47248a3761c/pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", size = 2122803 }, 250 | { url = "https://files.pythonhosted.org/packages/18/9c/730a09b2694aa89360d20756369822d98dc2f31b717c21df33b64ffd1f50/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", size = 2086755 }, 251 | { url = "https://files.pythonhosted.org/packages/54/8e/2dccd89602b5ec31d1c58138d02340ecb2ebb8c2cac3cc66b65ce3edb6ce/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", size = 2257358 }, 252 | { url = "https://files.pythonhosted.org/packages/d1/9c/126e4ac1bfad8a95a9837acdd0963695d69264179ba4ede8b8c40d741702/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091", size = 2257916 }, 253 | { url = "https://files.pythonhosted.org/packages/7d/ba/91eea2047e681a6853c81c20aeca9dcdaa5402ccb7404a2097c2adf9d038/pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", size = 1923823 }, 254 | { url = "https://files.pythonhosted.org/packages/94/c0/fcdf739bf60d836a38811476f6ecd50374880b01e3014318b6e809ddfd52/pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", size = 1952494 }, 255 | { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224 }, 256 | { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845 }, 257 | { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029 }, 258 | { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784 }, 259 | { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075 }, 260 | { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849 }, 261 | { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794 }, 262 | { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237 }, 263 | { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351 }, 264 | { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914 }, 265 | { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385 }, 266 | { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, 267 | { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, 268 | { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, 269 | { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640 }, 270 | { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649 }, 271 | { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472 }, 272 | { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509 }, 273 | { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702 }, 274 | { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428 }, 275 | { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753 }, 276 | { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849 }, 277 | { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541 }, 278 | { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225 }, 279 | { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373 }, 280 | { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, 281 | { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, 282 | { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, 283 | { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, 284 | { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, 285 | { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, 286 | { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, 287 | { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, 288 | { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, 289 | { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, 290 | { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, 291 | { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, 292 | { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, 293 | { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, 294 | { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, 295 | { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, 296 | { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, 297 | { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, 298 | { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, 299 | { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, 300 | { url = "https://files.pythonhosted.org/packages/9c/c7/8b311d5adb0fe00a93ee9b4e92a02b0ec08510e9838885ef781ccbb20604/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", size = 2041659 }, 301 | { url = "https://files.pythonhosted.org/packages/8a/d6/4f58d32066a9e26530daaf9adc6664b01875ae0691570094968aaa7b8fcc/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", size = 1873294 }, 302 | { url = "https://files.pythonhosted.org/packages/f7/3f/53cc9c45d9229da427909c751f8ed2bf422414f7664ea4dde2d004f596ba/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", size = 1903771 }, 303 | { url = "https://files.pythonhosted.org/packages/f0/49/bf0783279ce674eb9903fb9ae43f6c614cb2f1c4951370258823f795368b/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", size = 2083558 }, 304 | { url = "https://files.pythonhosted.org/packages/9c/5b/0d998367687f986c7d8484a2c476d30f07bf5b8b1477649a6092bd4c540e/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", size = 2118038 }, 305 | { url = "https://files.pythonhosted.org/packages/b3/33/039287d410230ee125daee57373ac01940d3030d18dba1c29cd3089dc3ca/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", size = 2079315 }, 306 | { url = "https://files.pythonhosted.org/packages/1f/85/6d8b2646d99c062d7da2d0ab2faeb0d6ca9cca4c02da6076376042a20da3/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", size = 2249063 }, 307 | { url = "https://files.pythonhosted.org/packages/17/d7/c37d208d5738f7b9ad8f22ae8a727d88ebf9c16c04ed2475122cc3f7224a/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", size = 2254631 }, 308 | { url = "https://files.pythonhosted.org/packages/13/e0/bafa46476d328e4553b85ab9b2f7409e7aaef0ce4c937c894821c542d347/pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", size = 2080877 }, 309 | { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858 }, 310 | { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745 }, 311 | { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188 }, 312 | { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479 }, 313 | { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415 }, 314 | { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623 }, 315 | { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175 }, 316 | { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674 }, 317 | { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951 }, 318 | ] 319 | 320 | [[package]] 321 | name = "pydantic-settings" 322 | version = "2.9.1" 323 | source = { registry = "https://pypi.org/simple" } 324 | dependencies = [ 325 | { name = "pydantic" }, 326 | { name = "python-dotenv" }, 327 | { name = "typing-inspection" }, 328 | ] 329 | sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 } 330 | wheels = [ 331 | { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, 332 | ] 333 | 334 | [[package]] 335 | name = "python-dotenv" 336 | version = "1.1.0" 337 | source = { registry = "https://pypi.org/simple" } 338 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } 339 | wheels = [ 340 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, 341 | ] 342 | 343 | [[package]] 344 | name = "python-multipart" 345 | version = "0.0.20" 346 | source = { registry = "https://pypi.org/simple" } 347 | sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } 348 | wheels = [ 349 | { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, 350 | ] 351 | 352 | [[package]] 353 | name = "requests" 354 | version = "2.32.3" 355 | source = { registry = "https://pypi.org/simple" } 356 | dependencies = [ 357 | { name = "certifi" }, 358 | { name = "charset-normalizer" }, 359 | { name = "idna" }, 360 | { name = "urllib3" }, 361 | ] 362 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 363 | wheels = [ 364 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 365 | ] 366 | 367 | [[package]] 368 | name = "sniffio" 369 | version = "1.3.1" 370 | source = { registry = "https://pypi.org/simple" } 371 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 372 | wheels = [ 373 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 374 | ] 375 | 376 | [[package]] 377 | name = "sse-starlette" 378 | version = "2.2.1" 379 | source = { registry = "https://pypi.org/simple" } 380 | dependencies = [ 381 | { name = "anyio" }, 382 | { name = "starlette" }, 383 | ] 384 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 385 | wheels = [ 386 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 387 | ] 388 | 389 | [[package]] 390 | name = "starlette" 391 | version = "0.46.2" 392 | source = { registry = "https://pypi.org/simple" } 393 | dependencies = [ 394 | { name = "anyio" }, 395 | ] 396 | sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846 } 397 | wheels = [ 398 | { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 }, 399 | ] 400 | 401 | [[package]] 402 | name = "typing-extensions" 403 | version = "4.13.2" 404 | source = { registry = "https://pypi.org/simple" } 405 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } 406 | wheels = [ 407 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, 408 | ] 409 | 410 | [[package]] 411 | name = "typing-inspection" 412 | version = "0.4.0" 413 | source = { registry = "https://pypi.org/simple" } 414 | dependencies = [ 415 | { name = "typing-extensions" }, 416 | ] 417 | sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } 418 | wheels = [ 419 | { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, 420 | ] 421 | 422 | [[package]] 423 | name = "urllib3" 424 | version = "2.4.0" 425 | source = { registry = "https://pypi.org/simple" } 426 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } 427 | wheels = [ 428 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, 429 | ] 430 | 431 | [[package]] 432 | name = "uvicorn" 433 | version = "0.34.2" 434 | source = { registry = "https://pypi.org/simple" } 435 | dependencies = [ 436 | { name = "click" }, 437 | { name = "h11" }, 438 | { name = "typing-extensions", marker = "python_full_version < '3.11'" }, 439 | ] 440 | sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815 } 441 | wheels = [ 442 | { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483 }, 443 | ] 444 | --------------------------------------------------------------------------------