├── .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 |
17 |
18 | Claude tool anatomy
19 |
20 |
21 |
22 |
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 | [](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 |
--------------------------------------------------------------------------------