├── .env.example ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── img ├── background.jpg ├── claude_tool_anatomy.png └── cursor_tool_anatomy.png ├── pyproject.toml ├── ruff.toml ├── src ├── __init__.py ├── __main__.py └── server.py ├── test ├── __init__.py └── test_server.py └── uv.lock /.env.example: -------------------------------------------------------------------------------- 1 | SCENARIO_ID=QUICKCHAT_SCENARIO_ID 2 | API_KEY=QUICKCHAT_API_KEY -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main CI/CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | run_tests_with_pytest: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: [ '3.11', '3.12', '3.13' ] 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install uv 21 | uses: astral-sh/setup-uv@v5 22 | with: 23 | enable-cache: true 24 | - name: Install Dependencies 25 | run: 26 | uv sync --all-extras 27 | 28 | - name: Test with pytest 29 | run: | 30 | uv run pytest 31 | 32 | run_ruff_lint: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout code 36 | uses: actions/checkout@v4 37 | 38 | - name: Run ruff 39 | uses: astral-sh/ruff-action@v3 40 | with: 41 | args: "check" 42 | version-file: "./ruff.toml" 43 | 44 | run_ruff_fmt_check: 45 | runs-on: ubuntu-latest 46 | steps: 47 | - name: Checkout code 48 | uses: actions/checkout@v4 49 | - name: Run ruff 50 | uses: astral-sh/ruff-action@v3 51 | with: 52 | args: "format --check" 53 | version-file: "./ruff.toml" 54 | 55 | 56 | run_uv_build_and_pypi_upload: 57 | needs: [ run_tests_with_pytest, run_ruff_lint, run_ruff_fmt_check ] 58 | runs-on: ubuntu-latest 59 | environment: 60 | name: pypi 61 | url: https://pypi.org/p/quickchat-ai-mcp 62 | permissions: 63 | id-token: write 64 | contents: read 65 | packages: write 66 | if: github.ref == 'refs/heads/main' 67 | steps: 68 | - uses: actions/checkout@v4 69 | - name: Set up Python 70 | uses: actions/setup-python@v4 71 | with: 72 | python-version: '3.13' 73 | 74 | - name: Install uv 75 | uses: astral-sh/setup-uv@v5 76 | with: 77 | enable-cache: true 78 | 79 | - name: Install Dependencies 80 | run: uv sync 81 | 82 | - name: debug uv 83 | run: uv --help 84 | 85 | - name: Build with uv 86 | run: uv build 87 | 88 | - name: Publish to PyPI 89 | uses: pypa/gh-action-pypi-publish@release/v1 90 | with: 91 | password: ${{ secrets.PYPI_API_TOKEN }} 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | .venv/ 6 | env/ 7 | venv/ 8 | .DS_Store 9 | Thumbs.db 10 | .env 11 | .idea/ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Quickchat AI 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 |

2 | 3 |

4 | 5 | # Quickchat AI MCP server 6 | 7 | The [Quickchat AI](https://quickchat.ai) MCP ([Model Context Protocol](https://modelcontextprotocol.io/)) server allows you to let anyone plug in your Quickchat AI Agent into their favourite AI app such as Claude Desktop, Cursor, VS Code, Windsurf and [more](https://modelcontextprotocol.io/clients#feature-support-matrix). 8 | 9 | ## Quickstart 10 | 1. Create a [Quickchat AI account](https://app.quickchat.ai) and start a 7-day trial of any plan. 11 | 2. Set up your AI's Knowledge Base, capabilities and settings. 12 | 3. Go to the MCP page to activate your MCP. Give it **Name**, **Description** and (optional) **Command**. They are important - AI apps need to understand when to contact your AI, what its capabilities and knowledge are. 13 | 4. That's it! Now you're ready to test your Quickchat AI via any AI app and show it to the world! 14 | 15 |

16 | Claude tool anatomy 17 |
18 | Claude tool anatomy 19 |

20 | 21 |

22 | Cursor tool anatomy 23 |
24 | Cursor tool anatomy 25 |

26 | 27 | ## Useful links 28 | - Quickstart video [youtube.com/watch?v=JE3dNiyZO8w](https://www.youtube.com/watch?v=JE3dNiyZO8w) 29 | - Quickstart blog post: [quickchat.ai/post/how-to-launch-your-quickchat-ai-mcp](https://www.quickchat.ai/post/how-to-launch-your-quickchat-ai-mcp) 30 | - MCP (Model Context Protocol) explained: [quickchat.ai/post/mcp-explained](https://www.quickchat.ai/post/mcp-explained) 31 | - The Quickchat AI MCP package on PyPI: [pypi.org/project/quickchat-ai-mcp](https://pypi.org/project/quickchat-ai-mcp) 32 | - The Quickchat AI MCP GitHub repo: [github.com/quickchatai/quickchat-ai-mcp](https://github.com/quickchatai/quickchat-ai-mcp) 33 | 34 | ## Prerequisite 35 | Install `uv` using: 36 | ```commandline 37 | curl -LsSf https://astral.sh/uv/install.sh | sh 38 | ``` 39 | 40 | or read more [here](https://docs.astral.sh/uv/getting-started/installation/). 41 | 42 | ## Test with Claude Desktop 43 | 44 | ### Configuration 45 | Go to `Settings > Developer > Edit` Config. Open the _claude_desktop_config.json_ file in a text editor. If you're just starting out, the file is going to look like this: 46 | 47 | ```JSON 48 | { 49 | "mcpServers": {} 50 | } 51 | ``` 52 | 53 | This is where you can define all the MCPs your Claude Desktop has access to. Here is how you add your Quickchat AI MCP: 54 | 55 | ```JSON 56 | { 57 | "mcpServers": { 58 | "< QUICKCHAT AI MCP NAME >": { 59 | "command": "uvx", 60 | "args": ["quickchat-ai-mcp"], 61 | "env": { 62 | "SCENARIO_ID": "< QUICKCHAT AI SCENARIO ID >", 63 | "API_KEY": "< QUICKCHAT AI API KEY >" 64 | } 65 | } 66 | } 67 | } 68 | ``` 69 | 70 | Go to the `Quickchat AI app > MCP > Integration` to find the above snippet with the values of MCP Name, SCENARIO_ID and API_KEY filled out. 71 | 72 | ## Test with Cursor 73 | 74 | ### Configuration 75 | Go to `Settings > Cursor Settings > MCP > Add new global MCP server` and include the Quickchat AI MCP snippet: 76 | 77 | ```JSON 78 | { 79 | "mcpServers": { 80 | "< QUICKCHAT AI MCP NAME >": { 81 | "command": "uvx", 82 | "args": ["quickchat-ai-mcp"], 83 | "env": { 84 | "SCENARIO_ID": "< QUICKCHAT AI SCENARIO ID >", 85 | "API_KEY": "< QUICKCHAT AI API KEY >" 86 | } 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | As before, you can find values for MCP Name, SCENARIO_ID and API_KEY at `Quickchat AI app > MCP > Integration`. 93 | 94 | ## Test with other AI apps 95 | 96 | Other AI apps will most likely require the same configuration but the actual steps to include it in the App itself will be different. We will be expanding this README as we go along. 97 | 98 | ## Launch your Quickchat AI MCP to the world! 99 | 100 | ``` 101 | ⛔️ Do not publish your Quickchat API key to your users! 102 | ``` 103 | 104 | Once you're ready to let other users connect your Quickchat AI MCP to their AI apps, share configuration snippet with them! However, you need to make sure they can use your Quickchat AI MCP **without your Quickchat API key**. Here is how to do that: 105 | 1. On the Quickchat App MCP page, turn the **Require API key** toggle **OFF**. 106 | 2. Share the configuration snippet _without the API key_: 107 | 108 | ```JSON 109 | { 110 | "mcpServers": { 111 | "< QUICKCHAT AI MCP NAME >": { 112 | "command": "uvx", 113 | "args": ["quickchat-ai-mcp"], 114 | "env": { 115 | "SCENARIO_ID": "< QUICKCHAT AI SCENARIO ID >" 116 | } 117 | } 118 | } 119 | } 120 | ``` 121 | --- 122 | 123 | ## Cool features 124 | - You can control all aspects of your MCP from the Quickchat AI dashboard. _One click and your change is deployed_. That includes the MCP name and description - all your users need to do is refresh their MCP connection. 125 | - View all conversations in the Quickchat Inbox. Remember: those won't be the exact messages your users send to their AI app but rather the transcript of the AI <> AI interaction between their AI app and your Quickchat AI. 🤯 126 | - Unlike most MCP implementations, this isn't a static tool handed to an AI. It's an open-ended way to send messages to Quickchat AI Agents you create. 🙌 127 | 128 | --- 129 | 130 | ## Running from source 131 | 132 | ### Debugging with the [MCP inspector](https://modelcontextprotocol.io/docs/tools/inspector) 133 | 134 | ```commandline 135 | uv run mcp dev src/__main__.py 136 | ``` 137 | 138 | ### Debugging with Claude Desktop, Cursor or other AI apps 139 | 140 | Use the following JSON configuration: 141 | 142 | ```JSON 143 | { 144 | "mcpServers": { 145 | "< QUICKCHAT AI MCP NAME >": { 146 | "command": "uv", 147 | "args": [ 148 | "run", 149 | "--with", 150 | "mcp[cli]", 151 | "--with", 152 | "requests", 153 | "mcp", 154 | "run", 155 | "< YOUR PATH>/quickchat-ai-mcp/src/__main__.py" 156 | ], 157 | "env": { 158 | "SCENARIO_ID": "< QUICKCHAT AI SCENARIO ID >", 159 | "API_KEY": "< QUICKCHAT AI API KEY >" 160 | } 161 | } 162 | } 163 | } 164 | ``` 165 | 166 | ### Testing 167 | 168 | Make sure your code is properly formatted and all tests are passing: 169 | 170 | ```commandline 171 | ruff check --fix 172 | ruff format 173 | uv run pytest 174 | ``` 175 | 176 | ## GitHub Star History 177 | 178 | [![Star History Chart](https://api.star-history.com/svg?repos=quickchatai/quickchat-ai-mcp&type=Date)](https://www.star-history.com/#quickchatai/quickchat-ai-mcp&Date) -------------------------------------------------------------------------------- /img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quickchatai/quickchat-ai-mcp/9931bb7e1c42d0883bc9320cfa08ffd7278c1cc4/img/background.jpg -------------------------------------------------------------------------------- /img/claude_tool_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quickchatai/quickchat-ai-mcp/9931bb7e1c42d0883bc9320cfa08ffd7278c1cc4/img/claude_tool_anatomy.png -------------------------------------------------------------------------------- /img/cursor_tool_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quickchatai/quickchat-ai-mcp/9931bb7e1c42d0883bc9320cfa08ffd7278c1cc4/img/cursor_tool_anatomy.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "quickchat-ai-mcp" 3 | version = "0.0.11" 4 | description = "Quickchat AI MCP Server" 5 | authors = [ 6 | { name = "Piotr Grudzien", email = "piotr@quickchat.ai" }, 7 | { name = "Mateusz Jakubczak", email = "mateusz@quickchat.ai" } 8 | ] 9 | readme = "README.md" 10 | requires-python = ">=3.11" 11 | dependencies = [ 12 | "mcp[cli]>=1.6.0", 13 | "requests>=2.31.0", 14 | "mcp>=1.0.0", 15 | "python-dotenv>=1.1.0", 16 | ] 17 | keywords = [ 18 | "quickchat", 19 | "mcp", 20 | "ai-agent" 21 | ] 22 | 23 | [project.optional-dependencies] 24 | test = [ 25 | "pytest", 26 | "pytest-asyncio", 27 | ] 28 | 29 | [build-system] 30 | requires = ["hatchling"] 31 | build-backend = "hatchling.build" 32 | 33 | [tool.hatch.build.targets.wheel] 34 | packages = ["src"] 35 | 36 | [tool.setuptools.packages.find] 37 | where = ["src"] 38 | 39 | [project.scripts] 40 | quickchat-ai-mcp = "src.__main__:run" 41 | 42 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | # Exclude a variety of commonly ignored directories. 2 | exclude = [ 3 | ".bzr", 4 | ".direnv", 5 | ".eggs", 6 | ".git", 7 | ".git-rewrite", 8 | ".hg", 9 | ".ipynb_checkpoints", 10 | ".mypy_cache", 11 | ".nox", 12 | ".pants.d", 13 | ".pyenv", 14 | ".pytest_cache", 15 | ".pytype", 16 | ".ruff_cache", 17 | ".svn", 18 | ".tox", 19 | ".venv", 20 | ".vscode", 21 | "__pypackages__", 22 | "_build", 23 | "buck-out", 24 | "build", 25 | "dist", 26 | "node_modules", 27 | "site-packages", 28 | "venv", 29 | ] 30 | 31 | # Same as Black. 32 | line-length = 88 33 | indent-width = 4 34 | 35 | # Assume Python 3.11 36 | target-version = "py311" 37 | 38 | [lint] 39 | # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. 40 | select = ["E4", "E7", "E9", "F", "I"] 41 | ignore = [] 42 | 43 | # Allow fix for all enabled rules (when `--fix`) is provided. 44 | fixable = ["ALL"] 45 | unfixable = [] 46 | 47 | # Allow unused variables when underscore-prefixed. 48 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 49 | 50 | [format] 51 | # Like Black, use double quotes for strings. 52 | quote-style = "double" 53 | 54 | # Like Black, indent with spaces, rather than tabs. 55 | indent-style = "space" 56 | 57 | # Like Black, respect magic trailing commas. 58 | skip-magic-trailing-comma = false 59 | 60 | # Like Black, automatically detect the appropriate line ending. 61 | line-ending = "auto" 62 | 63 | [lint.isort] 64 | combine-as-imports = true 65 | known-first-party = ["appquickchat"] 66 | force-sort-within-sections = true -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quickchatai/quickchat-ai-mcp/9931bb7e1c42d0883bc9320cfa08ffd7278c1cc4/src/__init__.py -------------------------------------------------------------------------------- /src/__main__.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | import os 3 | import sys 4 | 5 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 6 | 7 | from mcp.server import FastMCP 8 | 9 | from src.server import ( 10 | app_lifespan, 11 | fetch_mcp_settings, 12 | send_message, 13 | ) 14 | 15 | SCENARIO_ID: str = os.getenv("SCENARIO_ID") 16 | 17 | if SCENARIO_ID is None: 18 | raise ValueError("Please provide SCENARIO_ID.") 19 | 20 | API_KEY: str = os.getenv("API_KEY") 21 | 22 | mcp_name, mcp_command, send_message_tool_description = fetch_mcp_settings( 23 | SCENARIO_ID, API_KEY 24 | ) 25 | 26 | mcp = FastMCP(mcp_name, lifespan=app_lifespan) 27 | 28 | send_message = partial(send_message, scenario_id=SCENARIO_ID, api_key=API_KEY) 29 | if mcp_command: 30 | send_message.__name__ = mcp_command 31 | else: 32 | send_message.__name__ = "send_message" 33 | 34 | # Register tools by hand 35 | mcp.add_tool( 36 | fn=send_message, 37 | name=send_message.__name__, 38 | description=send_message_tool_description, 39 | ) 40 | 41 | 42 | def run(): 43 | print("Starting Quickchat mcp server") 44 | mcp.run() 45 | -------------------------------------------------------------------------------- /src/server.py: -------------------------------------------------------------------------------- 1 | from collections.abc import AsyncIterator 2 | from contextlib import asynccontextmanager 3 | from dataclasses import dataclass, field 4 | import json 5 | import os 6 | 7 | from dotenv import load_dotenv 8 | from mcp.server.fastmcp import Context, FastMCP 9 | import requests 10 | 11 | load_dotenv() 12 | 13 | 14 | BASE_URL: str = os.getenv("BASE_URL", "https://app.quickchat.ai") 15 | SCENARIO_ID_TO_CONV_ID: dict[str, str] = {} 16 | 17 | 18 | CHAT_ENDPOINT = f"{BASE_URL}/v1/api/mcp/chat" 19 | SETTINGS_ENDPOINT = f"{BASE_URL}/v1/api/mcp/settings" 20 | 21 | 22 | def fetch_mcp_settings(scenario_id: str, api_key: str | None = None): 23 | response = requests.get( 24 | url=SETTINGS_ENDPOINT, 25 | headers={"scenario-id": scenario_id, "X-API-Key": api_key}, 26 | ) 27 | 28 | if response.status_code != 200: 29 | raise ValueError( 30 | "Configuration error. Please check your API key and scenario ID." 31 | ) 32 | 33 | data = json.loads(response.content) 34 | 35 | try: 36 | mcp_active, mcp_name, mcp_command, mcp_description = ( 37 | data["active"], 38 | data["name"], 39 | data["command"], 40 | data["description"], 41 | ) 42 | except KeyError: 43 | raise ValueError("Configuration error") 44 | 45 | if not mcp_active: 46 | raise ValueError("Quickchat MCP not active.") 47 | 48 | if any(not len(x) > 0 for x in (mcp_name, mcp_description)): 49 | raise ValueError("MCP name and description cannot be empty.") 50 | 51 | return mcp_name, mcp_command, mcp_description 52 | 53 | 54 | @dataclass 55 | class AppContext: 56 | scenario_to_conv_id: dict[str, str] = field(default_factory=dict) 57 | 58 | 59 | @asynccontextmanager 60 | async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]: 61 | yield AppContext(scenario_to_conv_id=SCENARIO_ID_TO_CONV_ID) 62 | 63 | 64 | async def send_message( 65 | message: str, context: Context, scenario_id: str, api_key: str | None = None 66 | ) -> str: 67 | mcp_client_name = context.request_context.session.client_params.clientInfo.name 68 | 69 | response = requests.post( 70 | url=CHAT_ENDPOINT, 71 | headers={"scenario-id": scenario_id, "X-API-Key": api_key}, 72 | json={ 73 | "conv_id": context.request_context.lifespan_context.scenario_to_conv_id.get( 74 | scenario_id 75 | ), 76 | "text": message, 77 | "mcp_client_name": mcp_client_name, 78 | }, 79 | ) 80 | 81 | if response.status_code == 401: 82 | await context.request_context.session.send_log_message( 83 | level="error", 84 | data="Unauthorized access. Double-check your scenario_id and api_key.", 85 | ) 86 | raise ValueError("Configuration error.") 87 | elif response.status_code != 200: 88 | await context.request_context.session.send_log_message( 89 | level="error", data=f"Server error: {response.content}" 90 | ) 91 | raise ValueError("Server error. Please try again.") 92 | else: 93 | data = json.loads(response.content) 94 | 95 | if ( 96 | context.request_context.lifespan_context.scenario_to_conv_id.get( 97 | scenario_id 98 | ) 99 | is None 100 | ): 101 | context.request_context.lifespan_context.scenario_to_conv_id[ 102 | scenario_id 103 | ] = data["conv_id"] 104 | 105 | return data["reply"] 106 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quickchatai/quickchat-ai-mcp/9931bb7e1c42d0883bc9320cfa08ffd7278c1cc4/test/__init__.py -------------------------------------------------------------------------------- /test/test_server.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from unittest.mock import AsyncMock, MagicMock, patch 4 | 5 | import pytest 6 | 7 | from src.server import ( 8 | app_lifespan, 9 | fetch_mcp_settings, 10 | send_message, 11 | ) 12 | 13 | TEST_SCENARIO_ID = "test_scenario_id" 14 | TEST_API_KEY = "test_api_key" 15 | 16 | 17 | @pytest.fixture(scope="session", autouse=True) 18 | def mock_env(): 19 | os.environ["API_KEY"] = "test_api_key" 20 | os.environ["SCENARIO_ID"] = "test_scenario" 21 | 22 | 23 | @pytest.fixture 24 | def mock_response(): 25 | """Fixture to create a mock response object""" 26 | mock = MagicMock() 27 | mock.status_code = 200 28 | mock.content = json.dumps( 29 | { 30 | "active": True, 31 | "name": "Test MCP", 32 | "command": "Test Command", 33 | "description": "Test Description", 34 | "conv_id": "test-conv-id", 35 | "reply": "This is a test reply", 36 | } 37 | ).encode() 38 | return mock 39 | 40 | 41 | @pytest.fixture 42 | def mock_error_response(): 43 | """Fixture for error responses""" 44 | mock = MagicMock() 45 | mock.status_code = 400 46 | mock.content = json.dumps({"error": "Test Error"}).encode() 47 | return mock 48 | 49 | 50 | @pytest.fixture 51 | def mock_unauthorized_response(): 52 | """Fixture for unauthorized responses""" 53 | mock = MagicMock() 54 | mock.status_code = 401 55 | mock.content = json.dumps({"error": "Unauthorized"}).encode() 56 | return mock 57 | 58 | 59 | @pytest.fixture 60 | def mock_context(): 61 | """Fixture to create a mock context object for MCP""" 62 | context = MagicMock() 63 | context.request_context.session.client_params.clientInfo.name = "Test Client" 64 | context.request_context.session.send_log_message = AsyncMock() 65 | context.request_context.lifespan_context.scenario_to_conv_id = {} 66 | return context 67 | 68 | 69 | # Tests for the fetch_mcp_settings function 70 | @patch("requests.get") 71 | def test_fetch_mcp_settings_success(mock_get, mock_response): 72 | """Test successful MCP settings fetch""" 73 | mock_get.return_value = mock_response 74 | name, command, description = fetch_mcp_settings("test-scenario", "test-key") 75 | 76 | mock_get.assert_called_once() 77 | assert name == "Test MCP" 78 | assert command == "Test Command" 79 | assert description == "Test Description" 80 | 81 | 82 | @patch("requests.get") 83 | def test_fetch_mcp_settings_error_response(mock_get, mock_error_response): 84 | """Test error response handling""" 85 | mock_get.return_value = mock_error_response 86 | 87 | with pytest.raises(ValueError, match="Configuration error"): 88 | fetch_mcp_settings("test-scenario", "test-key") 89 | 90 | mock_get.assert_called_once() 91 | 92 | 93 | @patch("requests.get") 94 | def test_fetch_mcp_settings_inactive_mcp(mock_get, mock_response): 95 | """Test when MCP is not active""" 96 | mock_response.content = json.dumps( 97 | { 98 | "active": False, 99 | "name": "Test MCP", 100 | "command": "Test Command", 101 | "description": "Test Description", 102 | } 103 | ).encode() 104 | mock_get.return_value = mock_response 105 | 106 | with pytest.raises(ValueError, match="Quickchat MCP not active"): 107 | fetch_mcp_settings("test-scenario", "test-key") 108 | 109 | mock_get.assert_called_once() 110 | 111 | 112 | @patch("requests.get") 113 | def test_fetch_mcp_settings_empty_name_description(mock_get, mock_response): 114 | """Test when name or description is empty""" 115 | mock_response.content = json.dumps( 116 | { 117 | "active": True, 118 | "name": "", 119 | "command": "Test Command", 120 | "description": "Test Description", 121 | } 122 | ).encode() 123 | mock_get.return_value = mock_response 124 | 125 | with pytest.raises(ValueError, match="MCP name and description cannot be empty"): 126 | fetch_mcp_settings("test-scenario", "test-key") 127 | 128 | mock_get.assert_called_once() 129 | 130 | 131 | # Tests for the send_message function 132 | @pytest.mark.asyncio 133 | @patch("requests.post") 134 | async def test_send_message_success(mock_post, mock_response, mock_context): 135 | """Test successful message sending""" 136 | mock_post.return_value = mock_response 137 | 138 | result = await send_message("Hello", mock_context, TEST_SCENARIO_ID, TEST_API_KEY) 139 | 140 | mock_post.assert_called_once() 141 | assert result == "This is a test reply" 142 | assert ( 143 | mock_context.request_context.lifespan_context.scenario_to_conv_id.get( 144 | TEST_SCENARIO_ID 145 | ) 146 | == "test-conv-id" 147 | ) 148 | 149 | 150 | @pytest.mark.asyncio 151 | @patch("requests.post") 152 | async def test_send_message_unauthorized( 153 | mock_post, mock_unauthorized_response, mock_context 154 | ): 155 | """Test unauthorized error handling""" 156 | mock_post.return_value = mock_unauthorized_response 157 | 158 | with pytest.raises(ValueError, match="Configuration error"): 159 | await send_message("Hello", mock_context, TEST_SCENARIO_ID, TEST_API_KEY) 160 | 161 | mock_post.assert_called_once() 162 | mock_context.request_context.session.send_log_message.assert_called_once() 163 | 164 | 165 | @pytest.mark.asyncio 166 | @patch("requests.post") 167 | async def test_send_message_server_error(mock_post, mock_error_response, mock_context): 168 | """Test server error handling""" 169 | mock_post.return_value = mock_error_response 170 | 171 | with pytest.raises(ValueError, match="Server error"): 172 | await send_message("Hello", mock_context, TEST_SCENARIO_ID, TEST_API_KEY) 173 | 174 | mock_post.assert_called_once() 175 | mock_context.request_context.session.send_log_message.assert_called_once() 176 | 177 | 178 | # Tests for the app_lifespan context manager 179 | @pytest.mark.asyncio 180 | async def test_app_lifespan(): 181 | """Test the app_lifespan context manager""" 182 | mock_server = MagicMock() 183 | 184 | async with app_lifespan(mock_server) as context: 185 | assert context.scenario_to_conv_id == {} 186 | 187 | 188 | @pytest.mark.asyncio 189 | @patch("requests.post") 190 | async def test_multiple_conv_ids(mock_post, mock_response, mock_context): 191 | """Test correct handling of requests with multiple scenario_ids and conv_ids""" 192 | assert mock_context.request_context.lifespan_context.scenario_to_conv_id == {} 193 | 194 | mock_response.content = json.dumps( 195 | { 196 | "conv_id": "conv_id1", 197 | "reply": "This is a test reply", 198 | } 199 | ).encode() 200 | mock_post.return_value = mock_response 201 | await send_message("Hello", mock_context, "scenario_id1", TEST_API_KEY) 202 | assert mock_context.request_context.lifespan_context.scenario_to_conv_id == { 203 | "scenario_id1": "conv_id1" 204 | } 205 | 206 | mock_response.content = json.dumps( 207 | { 208 | "conv_id": "conv_id2", 209 | "reply": "This is a test reply", 210 | } 211 | ).encode() 212 | mock_post.return_value = mock_response 213 | await send_message("Hello", mock_context, "scenario_id2", TEST_API_KEY) 214 | assert mock_context.request_context.lifespan_context.scenario_to_conv_id == { 215 | "scenario_id1": "conv_id1", 216 | "scenario_id2": "conv_id2", 217 | } 218 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 1 3 | requires-python = ">=3.11" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.9.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "idna" }, 20 | { name = "sniffio" }, 21 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 22 | ] 23 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, 26 | ] 27 | 28 | [[package]] 29 | name = "certifi" 30 | version = "2025.1.31" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 35 | ] 36 | 37 | [[package]] 38 | name = "charset-normalizer" 39 | version = "3.4.1" 40 | source = { registry = "https://pypi.org/simple" } 41 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } 42 | wheels = [ 43 | { 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 }, 44 | { 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 }, 45 | { 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 }, 46 | { 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 }, 47 | { 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 }, 48 | { 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 }, 49 | { 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 }, 50 | { 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 }, 51 | { 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 }, 52 | { 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 }, 53 | { 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 }, 54 | { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, 55 | { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, 56 | { 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 }, 57 | { 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 }, 58 | { 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 }, 59 | { 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 }, 60 | { 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 }, 61 | { 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 }, 62 | { 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 }, 63 | { 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 }, 64 | { 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 }, 65 | { 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 }, 66 | { 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 }, 67 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, 68 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, 69 | { 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 }, 70 | { 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 }, 71 | { 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 }, 72 | { 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 }, 73 | { 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 }, 74 | { 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 }, 75 | { 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 }, 76 | { 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 }, 77 | { 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 }, 78 | { 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 }, 79 | { 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 }, 80 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, 81 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, 82 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, 83 | ] 84 | 85 | [[package]] 86 | name = "click" 87 | version = "8.1.8" 88 | source = { registry = "https://pypi.org/simple" } 89 | dependencies = [ 90 | { name = "colorama", marker = "sys_platform == 'win32'" }, 91 | ] 92 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 93 | wheels = [ 94 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 95 | ] 96 | 97 | [[package]] 98 | name = "colorama" 99 | version = "0.4.6" 100 | source = { registry = "https://pypi.org/simple" } 101 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 102 | wheels = [ 103 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 104 | ] 105 | 106 | [[package]] 107 | name = "h11" 108 | version = "0.14.0" 109 | source = { registry = "https://pypi.org/simple" } 110 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 111 | wheels = [ 112 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 113 | ] 114 | 115 | [[package]] 116 | name = "httpcore" 117 | version = "1.0.7" 118 | source = { registry = "https://pypi.org/simple" } 119 | dependencies = [ 120 | { name = "certifi" }, 121 | { name = "h11" }, 122 | ] 123 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 124 | wheels = [ 125 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 126 | ] 127 | 128 | [[package]] 129 | name = "httpx" 130 | version = "0.28.1" 131 | source = { registry = "https://pypi.org/simple" } 132 | dependencies = [ 133 | { name = "anyio" }, 134 | { name = "certifi" }, 135 | { name = "httpcore" }, 136 | { name = "idna" }, 137 | ] 138 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 139 | wheels = [ 140 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 141 | ] 142 | 143 | [[package]] 144 | name = "httpx-sse" 145 | version = "0.4.0" 146 | source = { registry = "https://pypi.org/simple" } 147 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 148 | wheels = [ 149 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 150 | ] 151 | 152 | [[package]] 153 | name = "idna" 154 | version = "3.10" 155 | source = { registry = "https://pypi.org/simple" } 156 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 157 | wheels = [ 158 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 159 | ] 160 | 161 | [[package]] 162 | name = "iniconfig" 163 | version = "2.1.0" 164 | source = { registry = "https://pypi.org/simple" } 165 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } 166 | wheels = [ 167 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, 168 | ] 169 | 170 | [[package]] 171 | name = "markdown-it-py" 172 | version = "3.0.0" 173 | source = { registry = "https://pypi.org/simple" } 174 | dependencies = [ 175 | { name = "mdurl" }, 176 | ] 177 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 178 | wheels = [ 179 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 180 | ] 181 | 182 | [[package]] 183 | name = "mcp" 184 | version = "1.6.0" 185 | source = { registry = "https://pypi.org/simple" } 186 | dependencies = [ 187 | { name = "anyio" }, 188 | { name = "httpx" }, 189 | { name = "httpx-sse" }, 190 | { name = "pydantic" }, 191 | { name = "pydantic-settings" }, 192 | { name = "sse-starlette" }, 193 | { name = "starlette" }, 194 | { name = "uvicorn" }, 195 | ] 196 | sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 } 197 | wheels = [ 198 | { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 }, 199 | ] 200 | 201 | [package.optional-dependencies] 202 | cli = [ 203 | { name = "python-dotenv" }, 204 | { name = "typer" }, 205 | ] 206 | 207 | [[package]] 208 | name = "mdurl" 209 | version = "0.1.2" 210 | source = { registry = "https://pypi.org/simple" } 211 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 212 | wheels = [ 213 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 214 | ] 215 | 216 | [[package]] 217 | name = "packaging" 218 | version = "25.0" 219 | source = { registry = "https://pypi.org/simple" } 220 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } 221 | wheels = [ 222 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, 223 | ] 224 | 225 | [[package]] 226 | name = "pluggy" 227 | version = "1.5.0" 228 | source = { registry = "https://pypi.org/simple" } 229 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 230 | wheels = [ 231 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 232 | ] 233 | 234 | [[package]] 235 | name = "pydantic" 236 | version = "2.11.3" 237 | source = { registry = "https://pypi.org/simple" } 238 | dependencies = [ 239 | { name = "annotated-types" }, 240 | { name = "pydantic-core" }, 241 | { name = "typing-extensions" }, 242 | { name = "typing-inspection" }, 243 | ] 244 | sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } 245 | wheels = [ 246 | { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, 247 | ] 248 | 249 | [[package]] 250 | name = "pydantic-core" 251 | version = "2.33.1" 252 | source = { registry = "https://pypi.org/simple" } 253 | dependencies = [ 254 | { name = "typing-extensions" }, 255 | ] 256 | sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } 257 | wheels = [ 258 | { 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 }, 259 | { 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 }, 260 | { 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 }, 261 | { 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 }, 262 | { 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 }, 263 | { 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 }, 264 | { 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 }, 265 | { 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 }, 266 | { 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 }, 267 | { 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 }, 268 | { 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 }, 269 | { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, 270 | { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, 271 | { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, 272 | { 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 }, 273 | { 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 }, 274 | { 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 }, 275 | { 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 }, 276 | { 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 }, 277 | { 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 }, 278 | { 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 }, 279 | { 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 }, 280 | { 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 }, 281 | { 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 }, 282 | { 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 }, 283 | { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, 284 | { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, 285 | { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, 286 | { 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 }, 287 | { 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 }, 288 | { 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 }, 289 | { 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 }, 290 | { 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 }, 291 | { 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 }, 292 | { 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 }, 293 | { 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 }, 294 | { 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 }, 295 | { 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 }, 296 | { 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 }, 297 | { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, 298 | { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, 299 | { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, 300 | { 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 }, 301 | { 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 }, 302 | { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, 303 | { 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 }, 304 | { 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 }, 305 | { 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 }, 306 | { 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 }, 307 | { 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 }, 308 | { 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 }, 309 | { 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 }, 310 | { 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 }, 311 | { 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 }, 312 | ] 313 | 314 | [[package]] 315 | name = "pydantic-settings" 316 | version = "2.8.1" 317 | source = { registry = "https://pypi.org/simple" } 318 | dependencies = [ 319 | { name = "pydantic" }, 320 | { name = "python-dotenv" }, 321 | ] 322 | sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } 323 | wheels = [ 324 | { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, 325 | ] 326 | 327 | [[package]] 328 | name = "pygments" 329 | version = "2.19.1" 330 | source = { registry = "https://pypi.org/simple" } 331 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 332 | wheels = [ 333 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 334 | ] 335 | 336 | [[package]] 337 | name = "pytest" 338 | version = "8.3.5" 339 | source = { registry = "https://pypi.org/simple" } 340 | dependencies = [ 341 | { name = "colorama", marker = "sys_platform == 'win32'" }, 342 | { name = "iniconfig" }, 343 | { name = "packaging" }, 344 | { name = "pluggy" }, 345 | ] 346 | sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } 347 | wheels = [ 348 | { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, 349 | ] 350 | 351 | [[package]] 352 | name = "pytest-asyncio" 353 | version = "0.26.0" 354 | source = { registry = "https://pypi.org/simple" } 355 | dependencies = [ 356 | { name = "pytest" }, 357 | ] 358 | sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 } 359 | wheels = [ 360 | { url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 }, 361 | ] 362 | 363 | [[package]] 364 | name = "python-dotenv" 365 | version = "1.1.0" 366 | source = { registry = "https://pypi.org/simple" } 367 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } 368 | wheels = [ 369 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, 370 | ] 371 | 372 | [[package]] 373 | name = "quickchat-ai-mcp" 374 | version = "0.0.11" 375 | source = { editable = "." } 376 | dependencies = [ 377 | { name = "mcp", extra = ["cli"] }, 378 | { name = "python-dotenv" }, 379 | { name = "requests" }, 380 | ] 381 | 382 | [package.optional-dependencies] 383 | test = [ 384 | { name = "pytest" }, 385 | { name = "pytest-asyncio" }, 386 | ] 387 | 388 | [package.metadata] 389 | requires-dist = [ 390 | { name = "mcp", specifier = ">=1.0.0" }, 391 | { name = "mcp", extras = ["cli"], specifier = ">=1.6.0" }, 392 | { name = "pytest", marker = "extra == 'test'" }, 393 | { name = "pytest-asyncio", marker = "extra == 'test'" }, 394 | { name = "python-dotenv", specifier = ">=1.1.0" }, 395 | { name = "requests", specifier = ">=2.31.0" }, 396 | ] 397 | provides-extras = ["test"] 398 | 399 | [[package]] 400 | name = "requests" 401 | version = "2.32.3" 402 | source = { registry = "https://pypi.org/simple" } 403 | dependencies = [ 404 | { name = "certifi" }, 405 | { name = "charset-normalizer" }, 406 | { name = "idna" }, 407 | { name = "urllib3" }, 408 | ] 409 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } 410 | wheels = [ 411 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, 412 | ] 413 | 414 | [[package]] 415 | name = "rich" 416 | version = "14.0.0" 417 | source = { registry = "https://pypi.org/simple" } 418 | dependencies = [ 419 | { name = "markdown-it-py" }, 420 | { name = "pygments" }, 421 | ] 422 | sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } 423 | wheels = [ 424 | { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, 425 | ] 426 | 427 | [[package]] 428 | name = "shellingham" 429 | version = "1.5.4" 430 | source = { registry = "https://pypi.org/simple" } 431 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } 432 | wheels = [ 433 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, 434 | ] 435 | 436 | [[package]] 437 | name = "sniffio" 438 | version = "1.3.1" 439 | source = { registry = "https://pypi.org/simple" } 440 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 441 | wheels = [ 442 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 443 | ] 444 | 445 | [[package]] 446 | name = "sse-starlette" 447 | version = "2.2.1" 448 | source = { registry = "https://pypi.org/simple" } 449 | dependencies = [ 450 | { name = "anyio" }, 451 | { name = "starlette" }, 452 | ] 453 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 454 | wheels = [ 455 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 456 | ] 457 | 458 | [[package]] 459 | name = "starlette" 460 | version = "0.46.1" 461 | source = { registry = "https://pypi.org/simple" } 462 | dependencies = [ 463 | { name = "anyio" }, 464 | ] 465 | sdist = { url = "https://files.pythonhosted.org/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 } 466 | wheels = [ 467 | { url = "https://files.pythonhosted.org/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 }, 468 | ] 469 | 470 | [[package]] 471 | name = "typer" 472 | version = "0.15.2" 473 | source = { registry = "https://pypi.org/simple" } 474 | dependencies = [ 475 | { name = "click" }, 476 | { name = "rich" }, 477 | { name = "shellingham" }, 478 | { name = "typing-extensions" }, 479 | ] 480 | sdist = { url = "https://files.pythonhosted.org/packages/8b/6f/3991f0f1c7fcb2df31aef28e0594d8d54b05393a0e4e34c65e475c2a5d41/typer-0.15.2.tar.gz", hash = "sha256:ab2fab47533a813c49fe1f16b1a370fd5819099c00b119e0633df65f22144ba5", size = 100711 } 481 | wheels = [ 482 | { url = "https://files.pythonhosted.org/packages/7f/fc/5b29fea8cee020515ca82cc68e3b8e1e34bb19a3535ad854cac9257b414c/typer-0.15.2-py3-none-any.whl", hash = "sha256:46a499c6107d645a9c13f7ee46c5d5096cae6f5fc57dd11eccbbb9ae3e44ddfc", size = 45061 }, 483 | ] 484 | 485 | [[package]] 486 | name = "typing-extensions" 487 | version = "4.13.1" 488 | source = { registry = "https://pypi.org/simple" } 489 | sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 } 490 | wheels = [ 491 | { url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 }, 492 | ] 493 | 494 | [[package]] 495 | name = "typing-inspection" 496 | version = "0.4.0" 497 | source = { registry = "https://pypi.org/simple" } 498 | dependencies = [ 499 | { name = "typing-extensions" }, 500 | ] 501 | sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } 502 | wheels = [ 503 | { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, 504 | ] 505 | 506 | [[package]] 507 | name = "urllib3" 508 | version = "2.3.0" 509 | source = { registry = "https://pypi.org/simple" } 510 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } 511 | wheels = [ 512 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, 513 | ] 514 | 515 | [[package]] 516 | name = "uvicorn" 517 | version = "0.34.0" 518 | source = { registry = "https://pypi.org/simple" } 519 | dependencies = [ 520 | { name = "click" }, 521 | { name = "h11" }, 522 | ] 523 | sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 } 524 | wheels = [ 525 | { url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 }, 526 | ] 527 | --------------------------------------------------------------------------------