├── .python-version ├── .env.sample ├── app ├── server.py ├── paystack_client.py └── tools.py ├── docs └── images │ └── mcp-inspector.png ├── main.py ├── mcp_server.py ├── Dockerfile ├── LICENSE ├── pyproject.toml ├── CONTRIBUTING.md ├── .gitignore ├── tests └── test_tools.py ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | PAYSTACK_API_KEY= -------------------------------------------------------------------------------- /app/server.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | 3 | # Initialize FastMCP server 4 | mcp = FastMCP("paystack") 5 | -------------------------------------------------------------------------------- /docs/images/mcp-inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisishaykins/paystack-python-mcp/HEAD/docs/images/mcp-inspector.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from app.server import mcp 2 | 3 | # The tools are decorated with the mcp instance, so they need to be imported 4 | # after the mcp object is created. 5 | from app.tools import * 6 | 7 | if __name__ == "__main__": 8 | # Initialize and run the server 9 | mcp.run(transport="stdio") 10 | -------------------------------------------------------------------------------- /mcp_server.py: -------------------------------------------------------------------------------- 1 | from app.server import mcp 2 | 3 | # The tools are decorated with the mcp instance, so they need to be imported 4 | # after the mcp object is created. 5 | from app.tools import * 6 | 7 | if __name__ == "__main__": 8 | # Initialize and run the server 9 | mcp.run(transport="stdio") 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Python runtime as a parent image 2 | FROM python:3.12-slim 3 | 4 | # Set the working directory in the container 5 | WORKDIR /app 6 | 7 | # Install uv, the project's package manager 8 | RUN pip install uv 9 | 10 | # Copy the dependency definition files 11 | COPY pyproject.toml uv.lock ./ 12 | 13 | # Install project dependencies into the system's Python environment 14 | # Using --system is recommended for containers as there's no need for a venv 15 | RUN uv pip install --system --no-cache . 16 | 17 | # Copy the rest of the application source code into the container 18 | COPY . . 19 | 20 | # Set the command to run when the container launches 21 | # CMD ["python", "main.py"] 22 | CMD ["uv", "run", "main.py"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Akinshola Samuel AKINDE 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 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "paystack-python-mcp" 3 | version = "0.1.0" 4 | description = "Paystack Python Model Context Protocol SDK" 5 | readme = "README.md" 6 | requires-python = ">=3.12" 7 | authors = [{ name = "Akinshola Samuel AKINDE." }] 8 | maintainers = [ 9 | { name = "Akinshola Samuel AKINDE", email = "akinsholasamuel@gmail.com" }, 10 | ] 11 | keywords = ["paystack", "payment", "python", "git", "mcp", "llm", "automation"] 12 | license = { text = "MIT" } 13 | classifiers = [ 14 | "Development Status :: 1 - Beta", 15 | "Intended Audience :: Developers", 16 | "License :: OSI Approved :: MIT License", 17 | "Programming Language :: Python :: 3", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "Programming Language :: Python :: 3.13", 22 | ] 23 | dependencies = [ 24 | "httpx>=0.28.1", 25 | "mcp[cli]>=1.15.0", 26 | "paystack-sdk>=0.0.10", 27 | ] 28 | 29 | [dependency-groups] 30 | dev = [ 31 | "pytest>=8.4.2", 32 | "ruff>=0.13.2", 33 | ] 34 | 35 | [project.urls] 36 | Homepage = "https://akinshola.com/paystack-python-mcp" 37 | Repository = "https://github.com/thisishaykins/paystack-python-mcp" 38 | Issues = "https://github.com/thisishaykins/paystack-python-mcp/issues" 39 | 40 | [tool.pytest.ini_options] 41 | pythonpath = ["."] 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing to the Paystack MCP Python SDK! This document provides guidelines and instructions for contributing. 4 | 5 | ## Development Setup 6 | 7 | 1. Make sure you have Python 3.10+ installed 8 | 2. Install [uv](https://docs.astral.sh/uv/getting-started/installation/) 9 | 3. Fork the repository 10 | 4. Clone your fork: `git clone https://github.com/YOUR-USERNAME/paystack-python-mcp.git` 11 | 5. Install dependencies: 12 | 13 | ```bash 14 | uv sync --frozen --all-extras --dev 15 | ``` 16 | 17 | 6. Set up pre-commit hooks: 18 | 19 | ```bash 20 | uv tool install pre-commit --with pre-commit-uv --force-reinstall 21 | ``` 22 | 23 | ## Development Workflow 24 | 25 | 1. Choose the correct branch for your changes: 26 | - For bug fixes to a released version: use the latest release branch (e.g. v1.1.x for 1.1.3) 27 | - For new features: use the main branch (which will become the next minor/major version) 28 | - If unsure, ask in an issue first 29 | 30 | 2. Create a new branch from your chosen base branch 31 | 32 | 3. Make your changes 33 | 34 | 4. Ensure tests pass: 35 | 36 | ```bash 37 | uv run pytest 38 | ``` 39 | 40 | 5. Run linting: 41 | 42 | ```bash 43 | uv run ruff check . 44 | uv run ruff format . 45 | ``` 46 | 47 | 7. Update README snippets if you modified example code: 48 | 49 | ```bash 50 | uv run scripts/update_readme_snippets.py 51 | ``` 52 | 53 | 8. (Optional) Run pre-commit hooks on all files: 54 | 55 | ```bash 56 | pre-commit run --all-files 57 | ``` 58 | 59 | 9. Submit a pull request to the same branch you branched from 60 | 61 | ## Code Style 62 | 63 | - We use `ruff` for linting and formatting 64 | - Follow PEP 8 style guidelines 65 | - Add type hints to all functions 66 | - Include docstrings for public APIs 67 | 68 | ## Pull Request Process 69 | 70 | 1. Update documentation as needed 71 | 2. Add tests for new functionality 72 | 3. Ensure CI passes 73 | 4. Maintainers will review your code 74 | 5. Address review feedback 75 | 76 | ## Code of Conduct 77 | 78 | Please note that this project is released with a [Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 79 | 80 | ## License 81 | 82 | By contributing, you agree that your contributions will be licensed under the MIT License. 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | 12 | 13 | # Other files 14 | # Byte-compiled / optimized / DLL files 15 | __pycache__/ 16 | *.py[codz] 17 | *$py.class 18 | 19 | # C extensions 20 | *.so 21 | 22 | # Distribution / packaging 23 | .Python 24 | build/ 25 | develop-eggs/ 26 | dist/ 27 | downloads/ 28 | eggs/ 29 | .eggs/ 30 | lib/ 31 | lib64/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | wheels/ 36 | share/python-wheels/ 37 | *.egg-info/ 38 | .installed.cfg 39 | *.egg 40 | MANIFEST 41 | 42 | # PyInstaller 43 | # Usually these files are written by a python script from a template 44 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 45 | *.manifest 46 | *.spec 47 | 48 | # Installer logs 49 | pip-log.txt 50 | pip-delete-this-directory.txt 51 | 52 | # Unit test / coverage reports 53 | htmlcov/ 54 | .tox/ 55 | .nox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | *.py.cover 63 | .hypothesis/ 64 | .pytest_cache/ 65 | cover/ 66 | 67 | # Translations 68 | *.mo 69 | *.pot 70 | 71 | # Django stuff: 72 | *.log 73 | local_settings.py 74 | db.sqlite3 75 | db.sqlite3-journal 76 | 77 | # Flask stuff: 78 | instance/ 79 | .webassets-cache 80 | 81 | # Scrapy stuff: 82 | .scrapy 83 | 84 | # Sphinx documentation 85 | docs/_build/ 86 | 87 | # PyBuilder 88 | .pybuilder/ 89 | target/ 90 | 91 | # Jupyter Notebook 92 | .ipynb_checkpoints 93 | 94 | # IPython 95 | profile_default/ 96 | ipython_config.py 97 | 98 | # pyenv 99 | # For a library or package, you might want to ignore these files since the code is 100 | # intended to run in multiple environments; otherwise, check them in: 101 | # .python-version 102 | 103 | # pipenv 104 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 105 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 106 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 107 | # install all needed dependencies. 108 | # Pipfile.lock 109 | 110 | # UV 111 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 112 | # This is especially recommended for binary packages to ensure reproducibility, and is more 113 | # commonly ignored for libraries. 114 | # uv.lock 115 | 116 | # poetry 117 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 118 | # This is especially recommended for binary packages to ensure reproducibility, and is more 119 | # commonly ignored for libraries. 120 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 121 | # poetry.lock 122 | # poetry.toml 123 | 124 | # pdm 125 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 126 | # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. 127 | # https://pdm-project.org/en/latest/usage/project/#working-with-version-control 128 | # pdm.lock 129 | # pdm.toml 130 | .pdm-python 131 | .pdm-build/ 132 | 133 | # pixi 134 | # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. 135 | # pixi.lock 136 | # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one 137 | # in the .venv directory. It is recommended not to include this directory in version control. 138 | .pixi 139 | 140 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 141 | __pypackages__/ 142 | 143 | # Celery stuff 144 | celerybeat-schedule 145 | celerybeat.pid 146 | 147 | # Redis 148 | *.rdb 149 | *.aof 150 | *.pid 151 | 152 | # RabbitMQ 153 | mnesia/ 154 | rabbitmq/ 155 | rabbitmq-data/ 156 | 157 | # ActiveMQ 158 | activemq-data/ 159 | 160 | # SageMath parsed files 161 | *.sage.py 162 | 163 | # Environments 164 | .env 165 | .envrc 166 | .venv 167 | env/ 168 | venv/ 169 | ENV/ 170 | env.bak/ 171 | venv.bak/ 172 | 173 | # Spyder project settings 174 | .spyderproject 175 | .spyproject 176 | 177 | # Rope project settings 178 | .ropeproject 179 | 180 | # mkdocs documentation 181 | /site 182 | 183 | # mypy 184 | .mypy_cache/ 185 | .dmypy.json 186 | dmypy.json 187 | 188 | # Pyre type checker 189 | .pyre/ 190 | 191 | # pytype static type analyzer 192 | .pytype/ 193 | 194 | # Cython debug symbols 195 | cython_debug/ 196 | 197 | # PyCharm 198 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 199 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 200 | # and can be added to the global gitignore or merged into this file. For a more nuclear 201 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 202 | # .idea/ 203 | 204 | # Abstra 205 | # Abstra is an AI-powered process automation framework. 206 | # Ignore directories containing user credentials, local state, and settings. 207 | # Learn more at https://abstra.io/docs 208 | .abstra/ 209 | 210 | # Visual Studio Code 211 | # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore 212 | # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore 213 | # and can be added to the global gitignore or merged into this file. However, if you prefer, 214 | # you could uncomment the following to ignore the entire vscode folder 215 | # .vscode/ 216 | 217 | # Ruff stuff: 218 | .ruff_cache/ 219 | 220 | # PyPI configuration file 221 | .pypirc 222 | 223 | # Marimo 224 | marimo/_static/ 225 | marimo/_lsp/ 226 | __marimo__/ 227 | 228 | # Streamlit 229 | .streamlit/secrets.toml 230 | 231 | 232 | # working mcp client .gitignore to be removed later 233 | mcp_client/__init__.py 234 | mcp_client/__pycache__/ 235 | mcp_client/*.pyc 236 | mcp_client/mcp_client.egg-info/ 237 | mcp_client/dist/ 238 | mcp_client/build/ 239 | mcp_client/.venv/ 240 | mcp_client/.pdm-python/ 241 | mcp_client/pdm.lock 242 | mcp_client/uv.lock 243 | mcp_client/.pdm/ 244 | mcp_client/.pixi/ 245 | mcp_client/.pixi/pixi/ 246 | mcp_client/__pypackages__/ 247 | mcp_client/.mypy_cache/ 248 | mcp_client/.ruff_cache/ 249 | mcp_client/.pytest_cache/ 250 | mcp_client/.vscode/ 251 | mcp_client/.idea/ 252 | mcp_client/.abstra/ 253 | mcp_client/.env 254 | mcp_client/.envrc 255 | mcp_client/.streamlit/secrets.toml 256 | mcp_client/.DS_Store 257 | mcp_client/.coverage 258 | mcp_client/coverage.xml 259 | mcp_client/htmlcov/ 260 | mcp_client/.tox/ 261 | mcp_client/.nox/ 262 | mcp_client/.hypothesis/ 263 | mcp_client/.ropeproject/ 264 | mcp_client/.spyderproject 265 | mcp_client/.spyproject 266 | mcp_client/docs/_build/ 267 | mcp_client/.ipynb_checkpoints 268 | mcp_client/.pytest_cache/ 269 | mcp_client/.pyre/ 270 | mcp_client/.pytype/ 271 | mcp_client/cython_debug/ 272 | mcp_client/.pdm-build/ 273 | mcp_client/.idea/ 274 | weather.py -------------------------------------------------------------------------------- /tests/test_tools.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from unittest.mock import patch, MagicMock 3 | 4 | 5 | @pytest.fixture(autouse=True) 6 | def mock_env_vars(): 7 | """ 8 | Mock environment variables to avoid errors during test collection. 9 | """ 10 | with patch.dict("os.environ", {"PAYSTACK_API_KEY": "test_key"}): 11 | yield 12 | 13 | 14 | @pytest.fixture 15 | def mock_paystack_client(): 16 | """ 17 | Mock the paystack_client to avoid actual API calls during tests. 18 | """ 19 | with patch("app.tools.paystack_client", MagicMock()) as mock_client: 20 | yield mock_client 21 | 22 | 23 | def test_get_balance(mock_paystack_client): 24 | from app.tools import get_balance 25 | 26 | get_balance() 27 | mock_paystack_client.get_balance.assert_called_once() 28 | 29 | 30 | def test_get_balance_ledger(mock_paystack_client): 31 | from app.tools import get_balance_ledger 32 | 33 | get_balance_ledger() 34 | mock_paystack_client.get_balance_ledger.assert_called_once() 35 | 36 | 37 | def test_list_customers(mock_paystack_client): 38 | from app.tools import list_customers 39 | 40 | list_customers() 41 | mock_paystack_client.list_customers.assert_called_once() 42 | 43 | 44 | def test_create_customer(mock_paystack_client): 45 | from app.tools import create_customer 46 | 47 | create_customer("test@example.com", "John", "Doe") 48 | mock_paystack_client.create_customer.assert_called_once() 49 | 50 | 51 | def test_fetch_customer(mock_paystack_client): 52 | from app.tools import fetch_customer 53 | 54 | fetch_customer("CUS_123") 55 | mock_paystack_client.fetch_customer.assert_called_once() 56 | 57 | 58 | def test_update_customer(mock_paystack_client): 59 | from app.tools import update_customer 60 | 61 | update_customer("CUS_123", "John", "Doe") 62 | mock_paystack_client.update_customer.assert_called_once() 63 | 64 | 65 | def test_list_products(mock_paystack_client): 66 | from app.tools import list_products 67 | 68 | list_products() 69 | mock_paystack_client.list_products.assert_called_once() 70 | 71 | 72 | def test_create_product(mock_paystack_client): 73 | from app.tools import create_product 74 | 75 | create_product("Test Product", "A product for testing", 1000, "NGN") 76 | mock_paystack_client.create_product.assert_called_once() 77 | 78 | 79 | def test_fetch_product(mock_paystack_client): 80 | from app.tools import fetch_product 81 | 82 | fetch_product("PROD_123") 83 | mock_paystack_client.fetch_product.assert_called_once() 84 | 85 | 86 | def test_update_product(mock_paystack_client): 87 | from app.tools import update_product 88 | 89 | update_product("PROD_123", name="New Name") 90 | mock_paystack_client.update_product.assert_called_once() 91 | 92 | 93 | def test_delete_product(mock_paystack_client): 94 | from app.tools import delete_product 95 | 96 | delete_product("PROD_123") 97 | mock_paystack_client.delete_product.assert_called_once() 98 | 99 | 100 | def test_list_invoices(mock_paystack_client): 101 | from app.tools import list_invoices 102 | 103 | list_invoices() 104 | mock_paystack_client.list_invoices.assert_called_once() 105 | 106 | 107 | def test_create_invoice(mock_paystack_client): 108 | from app.tools import create_invoice 109 | 110 | create_invoice("CUS_123", 5000) 111 | mock_paystack_client.create_invoice.assert_called_once() 112 | 113 | 114 | def test_list_transactions(mock_paystack_client): 115 | from app.tools import list_transactions 116 | 117 | list_transactions() 118 | mock_paystack_client.list_transactions.assert_called_once() 119 | 120 | 121 | def test_initialize_transaction(mock_paystack_client): 122 | from app.tools import initialize_transaction 123 | 124 | initialize_transaction("test@example.com", 2500, "NGN") 125 | mock_paystack_client.initialize_transaction.assert_called_once() 126 | 127 | 128 | def test_verify_transaction(mock_paystack_client): 129 | from app.tools import verify_transaction 130 | 131 | verify_transaction("REF_123") 132 | mock_paystack_client.verify_transaction.assert_called_once() 133 | 134 | 135 | def test_fetch_transaction(mock_paystack_client): 136 | from app.tools import fetch_transaction 137 | 138 | fetch_transaction("TRANS_123") 139 | mock_paystack_client.fetch_transaction.assert_called_once() 140 | 141 | 142 | def test_get_transaction_timeline(mock_paystack_client): 143 | from app.tools import get_transaction_timeline 144 | 145 | get_transaction_timeline("TRANS_123") 146 | mock_paystack_client.get_transaction_timeline.assert_called_once() 147 | 148 | 149 | def test_download_transactions(mock_paystack_client): 150 | from app.tools import download_transactions 151 | 152 | download_transactions() 153 | mock_paystack_client.download_transactions.assert_called_once() 154 | 155 | 156 | def test_create_refund(mock_paystack_client): 157 | from app.tools import create_refund 158 | 159 | create_refund("TRANS_123") 160 | mock_paystack_client.create_refund.assert_called_once() 161 | 162 | 163 | def test_list_subscriptions(mock_paystack_client): 164 | from app.tools import list_subscriptions 165 | 166 | list_subscriptions() 167 | mock_paystack_client.list_subscriptions.assert_called_once() 168 | 169 | 170 | def test_disable_subscription(mock_paystack_client): 171 | from app.tools import disable_subscription 172 | 173 | disable_subscription("SUB_123", "TOKEN_123") 174 | mock_paystack_client.disable_subscription.assert_called_once() 175 | 176 | 177 | def test_list_disputes(mock_paystack_client): 178 | from app.tools import list_disputes 179 | 180 | list_disputes() 181 | mock_paystack_client.list_disputes.assert_called_once() 182 | 183 | 184 | def test_fetch_dispute(mock_paystack_client): 185 | from app.tools import fetch_dispute 186 | 187 | fetch_dispute("DIS_123") 188 | mock_paystack_client.fetch_dispute.assert_called_once() 189 | 190 | 191 | def test_download_dispute(mock_paystack_client): 192 | from app.tools import download_dispute 193 | 194 | download_dispute() 195 | mock_paystack_client.download_dispute.assert_called_once() 196 | 197 | 198 | def test_resolve_dispute(mock_paystack_client): 199 | from app.tools import resolve_dispute 200 | 201 | resolve_dispute("DIS_123", "resolved", "Message", "1000", "file.pdf") 202 | mock_paystack_client.resolve_dispute.assert_called_once() 203 | 204 | 205 | def test_add_evidence_to_dispute(mock_paystack_client): 206 | from app.tools import add_evidence_to_dispute 207 | 208 | add_evidence_to_dispute( 209 | "DIS_123", "test@example.com", "John Doe", "12345", "Details" 210 | ) 211 | mock_paystack_client.add_evidence_to_dispute.assert_called_once() 212 | 213 | 214 | def test_create_payment_page(mock_paystack_client): 215 | from app.tools import create_payment_page 216 | 217 | create_payment_page("Test Page", 1000) 218 | mock_paystack_client.create_payment_page.assert_called_once() 219 | 220 | 221 | def test_list_payment_pages(mock_paystack_client): 222 | from app.tools import list_payment_pages 223 | 224 | list_payment_pages() 225 | mock_paystack_client.list_payment_pages.assert_called_once() 226 | 227 | 228 | def test_fetch_payment_page(mock_paystack_client): 229 | from app.tools import fetch_payment_page 230 | 231 | fetch_payment_page("PAGE_123") 232 | mock_paystack_client.fetch_payment_page.assert_called_once() 233 | 234 | 235 | def test_update_payment_page(mock_paystack_client): 236 | from app.tools import update_payment_page 237 | 238 | update_payment_page("PAGE_123", name="New Page Name") 239 | mock_paystack_client.update_payment_page.assert_called_once() 240 | 241 | 242 | def test_disable_payment_page(mock_paystack_client): 243 | from app.tools import disable_payment_page 244 | 245 | disable_payment_page("PAGE_123") 246 | mock_paystack_client.disable_payment_page.assert_called_once() 247 | 248 | 249 | def test_enable_payment_page(mock_paystack_client): 250 | from app.tools import enable_payment_page 251 | 252 | enable_payment_page("PAGE_123") 253 | mock_paystack_client.enable_payment_page.assert_called_once() 254 | 255 | 256 | def test_add_products_to_payment_page(mock_paystack_client): 257 | from app.tools import add_products_to_payment_page 258 | 259 | add_products_to_payment_page("PAGE_123", ["PROD_123"]) 260 | mock_paystack_client.add_products_to_payment_page.assert_called_once() 261 | 262 | 263 | def test_create_plan(mock_paystack_client): 264 | from app.tools import create_plan 265 | 266 | create_plan("Test Plan", 1000, "monthly") 267 | mock_paystack_client.create_plan.assert_called_once() 268 | 269 | 270 | def test_list_plans(mock_paystack_client): 271 | from app.tools import list_plans 272 | 273 | list_plans() 274 | mock_paystack_client.list_plans.assert_called_once() 275 | 276 | 277 | def test_fetch_plan(mock_paystack_client): 278 | from app.tools import fetch_plan 279 | 280 | fetch_plan("PLAN_123") 281 | mock_paystack_client.fetch_plan.assert_called_once() 282 | 283 | 284 | def test_fetch_banks(mock_paystack_client): 285 | from app.tools import fetch_banks 286 | 287 | fetch_banks(country="NG") 288 | mock_paystack_client.fetch_banks.assert_called_once() 289 | 290 | 291 | def test_list_avs(mock_paystack_client): 292 | from app.tools import list_avs 293 | 294 | list_avs(country="Nigeria") 295 | mock_paystack_client.list_avs.assert_called_once() 296 | 297 | 298 | def test_list_countries(mock_paystack_client): 299 | from app.tools import list_countries 300 | 301 | list_countries() 302 | mock_paystack_client.list_countries.assert_called_once() 303 | 304 | 305 | def test_resolve_account_number(mock_paystack_client): 306 | from app.tools import resolve_account_number 307 | 308 | resolve_account_number("1234567890", "058") 309 | mock_paystack_client.resolve_account_number.assert_called_once() 310 | 311 | 312 | def test_resolve_card_bin(mock_paystack_client): 313 | from app.tools import resolve_card_bin 314 | 315 | resolve_card_bin("539983") 316 | mock_paystack_client.resolve_card_bin.assert_called_once() 317 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paystack MCP Server 2 | 3 | This project is a [Model Context Protocol](https://modelcontextprotocol.com/) server for the Paystack API. It provides a set of tools that can be used by AI assistants and other automated systems to interact with Paystack in a structured way. This allows for the automation of tasks suchs as creating customers, managing products, initiating transactions, and many other tasks. 4 | 5 | Paystack MCP Server 6 | 7 | 8 | 9 | This server is built using the official `paystack-sdk` for Python and `FastMCP` for the server implementation. 10 | 11 | ## Features 12 | 13 | - A comprehensive set of tools for interacting with the Paystack API. 14 | - Easy to extend with new tools and functionality. 15 | - Includes a `Dockerfile` for easy containerization and deployment. 16 | 17 | ## Installation 18 | 19 | This project uses `uv` for dependency management. To install the necessary packages, follow these steps: 20 | 21 | 1. **Clone the repository:** 22 | ```bash 23 | git clone https://github.com/thisishaykins/paystack-python-mcp 24 | cd paystack-python-mcp 25 | ``` 26 | 27 | 2. **Create a virtual environment:** 28 | ```bash 29 | uv venv 30 | ``` 31 | 32 | 3. **Activate the virtual environment:** 33 | ```bash 34 | source .venv/bin/activate 35 | ``` 36 | 37 | 4. **Install the dependencies:** 38 | ```bash 39 | uv pip install -e . 40 | ``` 41 | 42 | ## Setup 43 | 44 | To use the Paystack MCP server, you need to provide your Paystack API key. The server is configured to read the API key from an environment variable. 45 | 46 | 1. **Create a `.env` file** in the root of the project. 47 | 2. **Add your API key** to the `.env` file: 48 | ``` 49 | PAYSTACK_API_KEY=sk_your_secret_key 50 | ``` 51 | 52 | ## Running the Server 53 | 54 | To run the MCP server, execute the following command from the root of the project: 55 | 56 | ```bash 57 | uv run main.py # or python main.py 58 | ``` 59 | 60 | The server will start and listen for requests on `stdio`. 61 | 62 | ### Or run using MCP inspector or in dev mode 63 | 64 | ```bash 65 | uv run mcp dev main.py 66 | ``` 67 | Paystack MCP Server running on Inspector 68 | 69 | 70 | ## Available Tools 71 | 72 | The following tools are available through the MCP server: 73 | 74 | | Tool | Description | 75 | | --- | --- | 76 | | `balance.read` | Retrieves the balance from a Paystack account. | 77 | | `balance.ledger` | Retrieves the balance ledger from a Paystack account. | 78 | | `customer.create` | Creates a new customer. | 79 | | `customer.list` | Retrieves a list of all customers. | 80 | | `customer.read` | Fetches the details of a specific customer. | 81 | | `customer.update` | Updates the details of a specific customer. | 82 | | `dispute.add_evidence` | Adds evidence to a dispute. | 83 | | `dispute.list` | Retrieves a list of all disputes. | 84 | | `dispute.read` | Fetches the details of a specific dispute. | 85 | | `dispute.download` | Downloads a list of disputes with optional filters. | 86 | | `dispute.resolve` | Resolves a dispute. | 87 | | `invoice.create` | Creates a new invoice. | 88 | | `invoice.list` | Retrieves a list of all invoices. | 89 | | `payment_page.create` | Creates a new payment page. | 90 | | `payment_page.list` | Retrieves a list of all payment pages. | 91 | | `payment_page.read` | Fetches the details of a specific payment page. | 92 | | `payment_page.update` | Updates the details of a specific payment page. | 93 | | `payment_page.disable` | Disables a specific payment page. | 94 | | `payment_page.enable` | Enables a specific payment page. | 95 | | `payment_page.add_products` | Adds products to a specific payment page. | 96 | | `plan.create` | Creates a new subscription plan. | 97 | | `plan.list` | Retrieves a list of all subscription plans. | 98 | | `plan.read` | Fetches the details of a specific subscription plan. | 99 | | `product.create` | Creates a new product. | 100 | | `product.list` | Retrieves a list of all products. | 101 | | `product.read` | Fetches the details of a specific product. | 102 | | `product.update` | Updates the details of a specific product. | 103 | | `product.delete` | Deletes a specific product. | 104 | | `refund.create` | Creates a new refund. | 105 | | `subscription.disable` | Disables a subscription. | 106 | | `subscription.list` | Retrieves a list of all subscriptions. | 107 | | `transaction.initialize` | Initializes a new transaction. | 108 | | `transaction.list` | Retrieves a list of all transactions. | 109 | | `transaction.read` | Fetches the details of a specific transaction. | 110 | | `transaction.verify` | Verifies the status of a transaction. | 111 | | `transaction.timeline` | Retrieves the timeline of a specific transaction. | 112 | | `transaction.download` | Downloads a list of transactions with optional filters. | 113 | | `verification.fetch_banks` | Fetches a list of banks. | 114 | | `verification.list_avs` | Lists all available account verification services. | 115 | | `verification.list_countries` | Retrieves a list of all countries. | 116 | | `verification.resolve_account_number` | Resolves an account number to get the account holder's name. | 117 | | `verification.resolve_bvn` | Resolves a BVN to get the associated account details. | 118 | | `verification.resolve_card_bin` | Resolves a card BIN to get the associated card details. | 119 | 120 | ## Usage with an AI Assistant (e.g., Claude) 121 | 122 | You can connect this MCP server to an AI assistant like Claude to allow it to perform actions on your behalf. The assistant can call the tools by sending a JSON-RPC request to the server. 123 | 124 | 125 | ### Usage with Claude Desktop, cursor, or any other Agent 126 | 127 | Add the following to your `claude_desktop_config.json`. See [here](https://modelcontextprotocol.io/quickstart/user) for more details. 128 | 129 | 130 | ```json 131 | { 132 | "mcpServers": { 133 | "paystack": { 134 | "command": "uv", 135 | "args": [ 136 | "--directory", 137 | "/path/to/paystack-python-mcp", 138 | "run", 139 | "main.py" 140 | ] 141 | } 142 | } 143 | } 144 | 145 | ``` 146 | 147 | Here is an example of how Claude could use the `customer.create` tool: 148 | 149 | **User:** "Please create a new customer with the email address 'test@example.com' and the name 'Test User'." 150 | 151 | **Claude's Thought Process:** "The user wants to create a new customer. I will use the `customer.create` tool. I need to extract the email, first name, and last name from the user's request." 152 | 153 | **Claude's Tool Call:** 154 | ```json 155 | { 156 | "tool_name": "customer.create", 157 | "parameters": { 158 | "email": "test@example.com", 159 | "first_name": "Test", 160 | "last_name": "User" 161 | } 162 | } 163 | ``` 164 | 165 | The MCP server would then execute the tool and return the result to Claude. 166 | 167 | ## Build using Docker 168 | 169 | This project includes a `Dockerfile` to make it easy to build and run the server in a container. 170 | 171 | 1. **Build the Docker image:** 172 | ```bash 173 | docker build -t paystack-mcp-server . 174 | ``` 175 | 176 | 2. **Run the Docker container:** 177 | ```bash 178 | docker run -it --rm -e PAYSTACK_API_KEY=sk_your_secret_key paystack-mcp-server 179 | ``` 180 | 181 | This will start the server inside a Docker container. 182 | 183 | ## Debugging the Server 184 | 185 | To debug your server, you can use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector). 186 | 187 | Run the following command in your terminal: 188 | 189 | ```bash 190 | # Start MCP Inspector and server with all tools 191 | uv run mcp dev main.py 192 | ``` 193 | 194 | ### Instructions 195 | 196 | 1. Replace `PAYSTACK_API_KEY` with your actual Paystack API secret key. 197 | 2. Run the command to start the MCP Inspector. 198 | 3. Open the MCP Inspector UI in your browser and click Connect to start the MCP server. 199 | 4. You can see the list of tools you selected and test each tool individually. 200 | 201 | 202 | ## Advanced Usage 203 | 204 | This Paystack MCP Server can be extended to covering more primitives, capabilities available on the MCP Server. 205 | 206 | ### MCP Primitives 207 | 208 | The MCP protocol defines three core primitives that servers can implement: 209 | 210 | | Primitive | Control | Description | Example Use | 211 | |-----------|-----------------------|-----------------------------------------------------|------------------------------| 212 | | Prompts | User-controlled | Interactive templates invoked by user choice | Slash commands, menu options | 213 | | Resources | Application-controlled| Contextual data managed by the client application | File contents, API responses | 214 | | Tools | Model-controlled | Functions exposed to the LLM to take actions | API calls, data updates | 215 | 216 | ### Server Capabilities 217 | 218 | MCP servers declare capabilities during initialization: 219 | 220 | | Capability | Feature Flag | Description | 221 | |--------------|------------------------------|------------------------------------| 222 | | `prompts` | `listChanged` | Prompt template management | 223 | | `resources` | `subscribe`
`listChanged`| Resource exposure and updates | 224 | | `tools` | `listChanged` | Tool discovery and execution | 225 | | `logging` | - | Server logging configuration | 226 | | `completions`| - | Argument completion suggestions | 227 | 228 | ## Documentation 229 | 230 | - [Paystack Python SDK Reference](https://github.com/PaystackOSS/paystack-python/) 231 | - [Paystack API Reference](https://paystack.com/docs/api/) 232 | - [Model Context Protocol API Reference](https://modelcontextprotocol.github.io/python-sdk/api/) 233 | - [Model Context Protocol documentation](https://modelcontextprotocol.io) 234 | - [Model Context Protocol specification](https://spec.modelcontextprotocol.io) 235 | - [Officially MCP supported servers](https://github.com/modelcontextprotocol/servers) 236 | 237 | ## Contributing 238 | 239 | We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project. See the [contributing guide](CONTRIBUTING.md) to get started. 240 | 241 | ## License 242 | 243 | This project is licensed under the MIT License - see the LICENSE file for details. -------------------------------------------------------------------------------- /app/paystack_client.py: -------------------------------------------------------------------------------- 1 | import os 2 | import paystack 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | 7 | PAYSTACK_API_BASE = "https://api.paystack.co" 8 | 9 | 10 | class PaystackClient: 11 | """A wrapper for the Paystack API""" 12 | 13 | def __init__(self, api_key: str | None = None): 14 | if api_key is None: 15 | api_key = os.environ.get("PAYSTACK_API_KEY") 16 | if api_key is None: 17 | raise ValueError("Paystack API key not provided.") 18 | 19 | self.api_key = api_key 20 | paystack.api_key = self.api_key 21 | 22 | def get_balance(self): 23 | """Get the balance from the Paystack API.""" 24 | return paystack.Balance.fetch() 25 | 26 | def get_balance_ledger(self): 27 | """Get the balance ledger from the Paystack API.""" 28 | return paystack.Balance.ledger() 29 | 30 | def list_customers(self): 31 | """List customers from the Paystack API.""" 32 | return paystack.Customer.list() 33 | 34 | def create_customer( 35 | self, email: str, first_name: str, last_name: str, phone: str | None = None 36 | ): 37 | """Create a customer using the Paystack API.""" 38 | return paystack.Customer.create( 39 | email=email, first_name=first_name, last_name=last_name, phone=phone 40 | ) 41 | 42 | def fetch_customer(self, customer_code: str): 43 | """Fetch a customer's details from the Paystack API.""" 44 | return paystack.Customer.fetch(customer_code) 45 | 46 | def update_customer( 47 | self, code: str, first_name: str, last_name: str, phone: str | None = None 48 | ): 49 | """Update a customer's details using the Paystack API.""" 50 | return paystack.Customer.update( 51 | code=code, first_name=first_name, last_name=last_name, phone=phone 52 | ) 53 | 54 | def list_products(self): 55 | """List products from the Paystack API.""" 56 | return paystack.Product.list() 57 | 58 | def create_product( 59 | self, name: str, description: str, price: int, currency: str, quantity: int = 1 60 | ): 61 | """Create a product using the Paystack API.""" 62 | return paystack.Product.create( 63 | name=name, 64 | description=description, 65 | price=price, 66 | currency=currency, 67 | quantity=quantity, 68 | ) 69 | 70 | def fetch_product(self, product_code: str): 71 | """Fetch a product's details from the Paystack API.""" 72 | return paystack.Product.fetch(product_code) 73 | 74 | def update_product( 75 | self, 76 | product_code: str, 77 | name: str | None, 78 | description: str | None, 79 | price: int | None, 80 | currency: str | None, 81 | quantity: int | None = None, 82 | ): 83 | """Update a product's details using the Paystack API.""" 84 | return paystack.Product.update( 85 | id=product_code, 86 | name=name, 87 | description=description, 88 | price=price, 89 | currency=currency, 90 | quantity=quantity, 91 | ) 92 | 93 | def delete_product(self, product_code: str): 94 | """Delete a product using the Paystack API.""" 95 | return paystack.Product.delete(product_code) 96 | 97 | def list_invoices(self): 98 | """List invoices from the Paystack API.""" 99 | return paystack.PaymentRequest.list() 100 | 101 | def create_invoice(self, customer: str, amount: int): 102 | """Create an invoice using the Paystack API.""" 103 | return paystack.PaymentRequest.create(customer=customer, amount=amount) 104 | 105 | def list_transactions(self): 106 | """List transactions from the Paystack API.""" 107 | return paystack.Transaction.list() 108 | 109 | def initialize_transaction(self, email: str, amount: int, currency: str): 110 | """Initialize a transaction using the Paystack API.""" 111 | return paystack.Transaction.initialize( 112 | email=email, amount=amount, currency=currency 113 | ) 114 | 115 | def verify_transaction(self, reference: str): 116 | """Verify a transaction using the Paystack API.""" 117 | return paystack.Transaction.verify(reference=reference) 118 | 119 | def fetch_transaction(self, transaction_id: str): 120 | """Fetch a transaction's details from the Paystack API.""" 121 | return paystack.Transaction.fetch(transaction_id) 122 | 123 | def get_transaction_timeline(self, id_or_reference: str): 124 | """Get a transaction's timeline from the Paystack API.""" 125 | return paystack.Transaction.timeline(id_or_reference) 126 | 127 | def download_transactions( 128 | self, 129 | per_page: int | None = 50, 130 | page: int | None = 1, 131 | from_date: str | None = None, 132 | to_date: str | None = None, 133 | ): 134 | """Download a transactions receipt from the Paystack API.""" 135 | return paystack.Transaction.download( 136 | per_page=per_page, page=page, _from=from_date, to=to_date 137 | ) 138 | 139 | def create_refund(self, transaction: str, amount: int | None = None): 140 | """Create a refund using the Paystack API.""" 141 | return paystack.Refund.create(transaction=transaction, amount=amount) 142 | 143 | def list_subscriptions(self): 144 | """List subscriptions from the Paystack API.""" 145 | return paystack.Subscription.list() 146 | 147 | def disable_subscription(self, code: str, token: str): 148 | """Disable a subscription using the Paystack API.""" 149 | return paystack.Subscription.disable(code=code, token=token) 150 | 151 | def list_disputes(self): 152 | """List disputes from the Paystack API.""" 153 | return paystack.Dispute.list() 154 | 155 | def add_evidence_to_dispute( 156 | self, 157 | dispute_id: str, 158 | customer_email: str, 159 | customer_name: str, 160 | customer_phone: str, 161 | service_details: str, 162 | ): 163 | """Add evidence to a dispute using the Paystack API.""" 164 | return paystack.Dispute.add_evidence( 165 | id=dispute_id, 166 | customer_email=customer_email, 167 | customer_name=customer_name, 168 | customer_phone=customer_phone, 169 | service_details=service_details, 170 | ) 171 | 172 | def fetch_dispute(self, dispute_id: str): 173 | """Fetch a dispute's details from the Paystack API.""" 174 | return paystack.Dispute.fetch(dispute_id) 175 | 176 | def download_dispute( 177 | self, 178 | per_page: int | None = 50, 179 | page: int | None = 1, 180 | from_date: str | None = None, 181 | to_date: str | None = None, 182 | ): 183 | """Download a dispute receipt from the Paystack API.""" 184 | return paystack.Dispute.download( 185 | per_page=per_page, page=page, _from=from_date, to=to_date 186 | ) 187 | 188 | def resolve_dispute( 189 | self, 190 | dispute_id: str, 191 | resolution: str, 192 | message: str, 193 | refund_amount: str, 194 | uploaded_filename: str, 195 | evidence: str | None = None, 196 | ): 197 | """Resolve a dispute using the Paystack API.""" 198 | return paystack.Dispute.resolve( 199 | dispute_id, resolution, message, refund_amount, uploaded_filename, evidence 200 | ) 201 | 202 | def create_payment_page( 203 | self, name: str, amount: int, description: str | None = None 204 | ): 205 | """Create a payment page using the Paystack API.""" 206 | return paystack.Page.create(name=name, amount=amount, description=description) 207 | 208 | def list_payment_pages(self): 209 | """List payment pages from the Paystack API.""" 210 | return paystack.Page.list() 211 | 212 | def fetch_payment_page(self, id: str): 213 | """Fetch a payment page's details from the Paystack API.""" 214 | return paystack.Page.fetch(id) 215 | 216 | def update_payment_page( 217 | self, 218 | id: str, 219 | name: str | None = None, 220 | description: str | None = None, 221 | amount: int | None = None, 222 | ): 223 | """Update a payment page using the Paystack API.""" 224 | return paystack.Page.update( 225 | id, name=name, description=description, amount=amount 226 | ) 227 | 228 | def disable_payment_page(self, id: str): 229 | """Disable a payment page using the Paystack API.""" 230 | return paystack.Page.update(id, active=False) 231 | 232 | def enable_payment_page(self, id: str): 233 | """Enable a payment page using the Paystack API.""" 234 | return paystack.Page.update(id, active=True) 235 | 236 | def add_products_to_payment_page(self, id: str, products: list[str]): 237 | """Add products to a payment page using the Paystack API.""" 238 | return paystack.Page.add_products(id=id, product=products) 239 | 240 | def create_plan(self, name: str, amount: int, interval: str): 241 | """Create a plan using the Paystack API.""" 242 | return paystack.Plan.create(name=name, amount=amount, interval=interval) 243 | 244 | def list_plans(self): 245 | """List plans from the Paystack API.""" 246 | return paystack.Plan.list() 247 | 248 | def fetch_plan(self, plan_code: str): 249 | """Fetch a plan's details from the Paystack API.""" 250 | return paystack.Plan.fetch(plan_code) 251 | 252 | def resolve_account_number(self, account_number: str, bank_code: str): 253 | """Resolve an account number using the Paystack API.""" 254 | return paystack.Verification.resolve_account_number( 255 | account_number=account_number, bank_code=bank_code 256 | ) 257 | 258 | def list_avs( 259 | self, 260 | country: str, 261 | type: str | None = None, 262 | currency: str | None = None, 263 | ): 264 | """List states for address_verification the Paystack API.""" 265 | return paystack.Verification.avs(type=type, country=country, currency=currency) 266 | 267 | def fetch_banks( 268 | self, 269 | country: str | None = None, 270 | pay_with_bank_transfer: bool | None = None, 271 | use_cursor: bool | None = None, 272 | per_page: int | None = None, 273 | next: str | None = None, 274 | previous: str | None = None, 275 | gateway: str | None = None, 276 | ): 277 | """Fetch a bank's details from the Paystack API.""" 278 | return paystack.Verification.fetch_banks( 279 | country=country, 280 | pay_with_bank_transfer=pay_with_bank_transfer, 281 | use_cursor=use_cursor, 282 | per_page=per_page, 283 | next=next, 284 | previous=previous, 285 | gateway=gateway, 286 | ) 287 | 288 | def list_countries(self): 289 | """List countries from the Paystack API.""" 290 | return paystack.Verification.list_countries() 291 | 292 | def resolve_card_bin(self, card_bin: str): 293 | """Resolve a card bin using the Paystack API.""" 294 | return paystack.Verification.resolve_card_bin(card_bin) 295 | 296 | 297 | # A single client instance to be used by the tools 298 | paystack_client = PaystackClient() 299 | -------------------------------------------------------------------------------- /app/tools.py: -------------------------------------------------------------------------------- 1 | from app.server import mcp 2 | from app.paystack_client import paystack_client 3 | 4 | 5 | @mcp.tool(name="balance.read") 6 | def get_balance(): 7 | """ 8 | Retrieves the balance from a Paystack account. 9 | """ 10 | return paystack_client.get_balance() 11 | 12 | 13 | @mcp.tool(name="balance.ledger") 14 | def get_balance_ledger(): 15 | """ 16 | Retrieves the balance ledger from a Paystack account. 17 | """ 18 | return paystack_client.get_balance_ledger() 19 | 20 | 21 | @mcp.tool(name="customer.list") 22 | def list_customers(): 23 | """ 24 | Retrieves a list of all customers. 25 | """ 26 | return paystack_client.list_customers() 27 | 28 | 29 | @mcp.tool(name="customer.create") 30 | def create_customer( 31 | email: str, first_name: str, last_name: str, phone: str | None = None 32 | ): 33 | """ 34 | Creates a new customer. 35 | 36 | Args: 37 | email: The customer's email address. 38 | first_name: The customer's first name. 39 | last_name: The customer's last name. 40 | phone: The customer's phone number (optional). 41 | """ 42 | return paystack_client.create_customer(email, first_name, last_name, phone) 43 | 44 | 45 | @mcp.tool(name="customer.read") 46 | def fetch_customer(customer_code: str): 47 | """ 48 | Fetches the details of a specific customer. 49 | 50 | Args: 51 | customer_code: The code of the customer to fetch. 52 | """ 53 | return paystack_client.fetch_customer(customer_code) 54 | 55 | 56 | @mcp.tool(name="customer.update") 57 | def update_customer( 58 | code: str, first_name: str, last_name: str, phone: str | None = None 59 | ): 60 | """ 61 | Updates the details of a specific customer. 62 | 63 | Args: 64 | code: The code of the customer to update. 65 | first_name: The customer's new first name. 66 | last_name: The customer's new last name. 67 | phone: The customer's new phone number (optional). 68 | """ 69 | return paystack_client.update_customer(code, first_name, last_name, phone) 70 | 71 | 72 | @mcp.tool(name="product.list") 73 | def list_products(): 74 | """ 75 | Retrieves a list of all products. 76 | """ 77 | return paystack_client.list_products() 78 | 79 | 80 | @mcp.tool(name="product.create") 81 | def create_product( 82 | name: str, description: str, price: int, currency: str, quantity: int = 1 83 | ): 84 | """ 85 | Creates a new product. 86 | 87 | Args: 88 | name: The name of the product. 89 | description: A description of the product. 90 | price: The price of the product in the smallest currency unit (e.g., kobo). 91 | currency: The currency of the price (e.g., NGN). 92 | quantity: The available quantity of the product (default is 1). 93 | """ 94 | return paystack_client.create_product(name, description, price, currency, quantity) 95 | 96 | 97 | @mcp.tool(name="product.read") 98 | def fetch_product(product_code: str): 99 | """ 100 | Fetches the details of a specific product. 101 | 102 | Args: 103 | product_code: The code of the product to fetch. 104 | """ 105 | return paystack_client.fetch_product(product_code) 106 | 107 | 108 | @mcp.tool(name="product.update") 109 | def update_product( 110 | product_code: str, 111 | name: str | None = None, 112 | description: str | None = None, 113 | price: int | None = None, 114 | currency: str | None = None, 115 | quantity: int | None = None, 116 | ): 117 | """ 118 | Updates the details of a specific product. 119 | Args: 120 | product_code: The code of the product to update. 121 | name: The new name of the product (optional). 122 | description: The new description of the product (optional). 123 | price: The new price of the product in the smallest currency unit (e.g., kobo) (optional). 124 | currency: The new currency of the price (e.g., NGN) (optional). 125 | quantity: The new available quantity of the product (optional). 126 | """ 127 | return paystack_client.update_product( 128 | product_code, name, description, price, currency, quantity 129 | ) 130 | 131 | 132 | @mcp.tool(name="product.delete") 133 | def delete_product(product_code: str): 134 | """ 135 | Deletes a specific product. 136 | Args: 137 | product_code: The code of the product to delete. 138 | """ 139 | return paystack_client.delete_product(product_code) 140 | 141 | 142 | @mcp.tool(name="invoice.list") 143 | def list_invoices(): 144 | """ 145 | Retrieves a list of all invoices. 146 | """ 147 | return paystack_client.list_invoices() 148 | 149 | 150 | @mcp.tool(name="invoice.create") 151 | def create_invoice(customer: str, amount: int): 152 | """ 153 | Creates a new invoice. 154 | 155 | Args: 156 | customer: The customer's code or email address. 157 | amount: The amount of the invoice in the smallest currency unit (e.g., kobo). 158 | """ 159 | return paystack_client.create_invoice(customer, amount) 160 | 161 | 162 | @mcp.tool(name="transaction.list") 163 | def list_transactions(): 164 | """ 165 | Retrieves a list of all transactions. 166 | """ 167 | return paystack_client.list_transactions() 168 | 169 | 170 | @mcp.tool(name="transaction.initialize") 171 | def initialize_transaction(email: str, amount: int, currency: str): 172 | """ 173 | Initializes a new transaction. 174 | 175 | Args: 176 | email: The customer's email address. 177 | amount: The amount of the transaction in the smallest currency unit (e.g., kobo). 178 | currency: The currency of the transaction (e.g., NGN). 179 | """ 180 | return paystack_client.initialize_transaction(email, amount, currency) 181 | 182 | 183 | @mcp.tool(name="transaction.verify") 184 | def verify_transaction(reference: str): 185 | """ 186 | Verifies the status of a transaction. 187 | 188 | Args: 189 | reference: The reference of the transaction to verify. 190 | """ 191 | return paystack_client.verify_transaction(reference) 192 | 193 | 194 | @mcp.tool(name="transaction.read") 195 | def fetch_transaction(transaction_id: str): 196 | """ 197 | Fetches the details of a specific transaction. 198 | 199 | Args: 200 | transaction_id: The ID of the transaction to fetch. 201 | """ 202 | return paystack_client.fetch_transaction(transaction_id) 203 | 204 | 205 | @mcp.tool(name="transaction.timeline") 206 | def get_transaction_timeline(transaction_id_or_reference: str): 207 | """ 208 | Retrieves the timeline of a specific transaction. 209 | 210 | Args: 211 | transaction_id_or_reference: The ID/Reference of the transaction to get the timeline for. 212 | """ 213 | return paystack_client.get_transaction_timeline(transaction_id_or_reference) 214 | 215 | 216 | @mcp.tool(name="transaction.download") 217 | def download_transactions( 218 | per_page: int | None = 50, 219 | page: int | None = 1, 220 | from_date: str | None = None, 221 | to_date: str | None = None, 222 | ): 223 | """ 224 | Downloads a list of transactions with optional filters. 225 | 226 | Args: 227 | per_page: Number of records to fetch per page (default is 50). 228 | page: The page number to retrieve (default is 1). 229 | from_date: The start date for filtering transactions (optional, format: 'YYYY-MM-DD'). 230 | to_date: The end date for filtering transactions (optional, format: 'YYYY-MM-DD'). 231 | """ 232 | return paystack_client.download_transactions(per_page, page, from_date, to_date) 233 | 234 | 235 | @mcp.tool(name="refund.create") 236 | def create_refund(transaction: str, amount: int | None = None): 237 | """ 238 | Creates a new refund. 239 | 240 | Args: 241 | transaction: The transaction reference or ID to refund. 242 | amount: The amount to refund in the smallest currency unit (e.g., kobo). 243 | If not provided, a full refund will be issued. 244 | """ 245 | return paystack_client.create_refund(transaction, amount) 246 | 247 | 248 | @mcp.tool(name="subscription.list") 249 | def list_subscriptions(): 250 | """ 251 | Retrieves a list of all subscriptions. 252 | """ 253 | return paystack_client.list_subscriptions() 254 | 255 | 256 | @mcp.tool(name="subscription.disable") 257 | def disable_subscription(code: str, token: str): 258 | """ 259 | Disables a subscription. 260 | 261 | Args: 262 | code: The subscription code. 263 | token: The email token of the customer. 264 | """ 265 | return paystack_client.disable_subscription(code, token) 266 | 267 | 268 | @mcp.tool(name="dispute.list") 269 | def list_disputes(): 270 | """ 271 | Retrieves a list of all disputes. 272 | """ 273 | return paystack_client.list_disputes() 274 | 275 | 276 | @mcp.tool(name="dispute.read") 277 | def fetch_dispute(dispute_id: str): 278 | """ 279 | Fetches the details of a specific dispute. 280 | 281 | Args: 282 | dispute_id: The ID of the dispute to fetch. 283 | """ 284 | return paystack_client.fetch_dispute(dispute_id) 285 | 286 | 287 | @mcp.tool(name="dispute.download") 288 | def download_dispute( 289 | per_page: int | None = 50, 290 | page: int | None = 1, 291 | from_date: str | None = None, 292 | to_date: str | None = None, 293 | ): 294 | """ 295 | Downloads a list of dispute with optional filters. 296 | 297 | Args: 298 | per_page: Number of records to fetch per page (default is 50). 299 | page: The page number to retrieve (default is 1). 300 | from_date: The start date for filtering dispute (optional, format: 'YYYY-MM-DD'). 301 | to_date: The end date for filtering dispute (optional, format: 'YYYY-MM-DD'). 302 | """ 303 | return paystack_client.download_dispute(per_page, page, from_date, to_date) 304 | 305 | 306 | @mcp.tool(name="dispute.resolve") 307 | def resolve_dispute( 308 | dispute_id: str, 309 | resolution: str, 310 | message: str, 311 | refund_amount: str, 312 | uploaded_filename: str, 313 | evidence: str | None = None, 314 | ): 315 | """ 316 | Resolves a dispute. 317 | 318 | Args: 319 | dispute_id: 'id_example' # str | Dispute ID 320 | resolution: 'resolution_example' # str | Dispute resolution. Accepted values, merchant-accepted, declined 321 | message: 'message_example' # str | Reason for resolving 322 | refund_amount: 'refund_amount_example' # str | The amount to refund, in kobo if currency is NGN, pesewas, if currency is GHS, and cents, if currency is ZAR 323 | uploaded_filename: 'uploaded_filename_example' # str | Filename of attachment returned via response from the Dispute upload URL 324 | evidence: 'evidence_example' # str | Evidence Id for fraud claims (optional) 325 | 326 | """ 327 | return paystack_client.resolve_dispute( 328 | dispute_id, resolution, message, refund_amount, uploaded_filename, evidence 329 | ) 330 | 331 | 332 | @mcp.tool(name="dispute.add_evidence") 333 | def add_evidence_to_dispute( 334 | dispute_id: str, 335 | customer_email: str, 336 | customer_name: str, 337 | customer_phone: str, 338 | service_details: str, 339 | ): 340 | """ 341 | Adds evidence to a dispute. 342 | 343 | Args: 344 | dispute_id: The ID of the dispute. 345 | customer_email: The email address of the customer. 346 | customer_name: The name of the customer. 347 | customer_phone: The phone number of the customer. 348 | service_details: Details of the service provided. 349 | """ 350 | return paystack_client.add_evidence_to_dispute( 351 | dispute_id, customer_email, customer_name, customer_phone, service_details 352 | ) 353 | 354 | 355 | @mcp.tool(name="payment_page.create") 356 | def create_payment_page(name: str, amount: int): 357 | """ 358 | Creates a new payment page. 359 | 360 | Args: 361 | name: The name of the payment page. 362 | amount: The amount for the payment page in the smallest currency unit (e.g., kobo). 363 | """ 364 | return paystack_client.create_payment_page(name, amount) 365 | 366 | 367 | @mcp.tool(name="payment_page.list") 368 | def list_payment_pages(): 369 | """ 370 | Retrieves a list of all payment pages. 371 | """ 372 | return paystack_client.list_payment_pages() 373 | 374 | 375 | @mcp.tool(name="payment_page.read") 376 | def fetch_payment_page(id: str): 377 | """ 378 | Fetches the details of a specific payment page. 379 | 380 | Args: 381 | id: The id of the payment page to fetch. 382 | """ 383 | return paystack_client.fetch_payment_page(id) 384 | 385 | 386 | @mcp.tool(name="payment_page.update") 387 | def update_payment_page( 388 | id: str, 389 | name: str | None = None, 390 | description: str | None = None, 391 | amount: int | None = None, 392 | ): 393 | """ 394 | Updates the details of a specific payment page. 395 | Args: 396 | id: The id of the payment page to update. 397 | name: The new name of the payment page (optional). 398 | description: The new description of the payment page (optional). 399 | amount: The new amount for the payment page in the smallest currency unit (e.g., kobo) (optional). 400 | """ 401 | return paystack_client.update_payment_page(id, name, description, amount) 402 | 403 | 404 | @mcp.tool(name="payment_page.disable") 405 | def disable_payment_page(id: str): 406 | """ 407 | Disables a specific payment page. 408 | Args: 409 | id: The id of the payment page to disable. 410 | """ 411 | return paystack_client.disable_payment_page(id) 412 | 413 | 414 | @mcp.tool(name="payment_page.enable") 415 | def enable_payment_page(id: str): 416 | """ 417 | Enables a specific payment page. 418 | Args: 419 | id: The id of the payment page to enable. 420 | """ 421 | return paystack_client.enable_payment_page(id) 422 | 423 | 424 | @mcp.tool(name="payment_page.add_products") 425 | def add_products_to_payment_page(id: str, products: list[str]): 426 | """ 427 | Adds products to a specific payment page. 428 | Args: 429 | id: The id of the payment page to add products to. 430 | products: A list of product codes to add to the payment page. 431 | """ 432 | return paystack_client.add_products_to_payment_page(id, products) 433 | 434 | 435 | @mcp.tool(name="plan.create") 436 | def create_plan(name: str, amount: int, interval: str): 437 | """ 438 | Creates a new subscription plan. 439 | 440 | Args: 441 | name: The name of the plan. 442 | amount: The amount for the plan in the smallest currency unit (e.g., kobo). 443 | interval: The frequency of the plan (e.g., 'daily', 'weekly', 'monthly'). 444 | """ 445 | return paystack_client.create_plan(name, amount, interval) 446 | 447 | 448 | @mcp.tool(name="plan.list") 449 | def list_plans(): 450 | """ 451 | Retrieves a list of all subscription plans. 452 | """ 453 | return paystack_client.list_plans() 454 | 455 | 456 | @mcp.tool(name="plan.read") 457 | def fetch_plan(plan_code: str): 458 | """ 459 | Fetches the details of a specific subscription plan. 460 | 461 | Args: 462 | plan_code: The code of the plan to fetch. 463 | """ 464 | return paystack_client.fetch_plan(plan_code) 465 | 466 | 467 | @mcp.tool(name="verification.fetch_banks") 468 | def fetch_banks( 469 | country: str | None = None, 470 | pay_with_bank_transfer: bool | None = None, 471 | use_cursor: bool | None = None, 472 | per_page: int | None = None, 473 | next: str | None = None, 474 | previous: str | None = None, 475 | gateway: str | None = None, 476 | ): 477 | """ 478 | Fetches a list of banks. 479 | 480 | Args: 481 | country: The country code to filter banks by (optional). 482 | pay_with_bank_transfer: Filter banks that support bank transfer (optional). 483 | use_cursor: Whether to use cursor-based pagination (optional). 484 | per_page: Number of records to fetch per page (optional). 485 | next: The cursor for the next page (optional). 486 | previous: The cursor for the previous page (optional). 487 | gateway: Filter banks by payment gateway (optional). 488 | """ 489 | return paystack_client.fetch_banks( 490 | country, pay_with_bank_transfer, use_cursor, per_page, next, previous, gateway 491 | ) 492 | 493 | 494 | @mcp.tool(name="verification.list_avs") 495 | def list_avs(country: str, type: str | None = None, currency: str | None = None): 496 | """ 497 | Lists all available account verification services. 498 | Args: 499 | country: The country code to filter by. 500 | type: The type of verification service to filter by (optional). 501 | currency: The currency code to filter by (optional). 502 | """ 503 | return paystack_client.list_avs(type, country, currency) 504 | 505 | 506 | @mcp.tool(name="verification.list_countries") 507 | def list_countries(): 508 | """ 509 | Retrieves a list of all countries. 510 | """ 511 | return paystack_client.list_countries() 512 | 513 | 514 | @mcp.tool(name="verification.resolve_account_number") 515 | def resolve_account_number(account_number: str, bank_code: str): 516 | """ 517 | Resolves an account number to get the account holder's name. 518 | 519 | Args: 520 | account_number: The account number to resolve. 521 | bank_code: The bank code of the account's bank. 522 | """ 523 | return paystack_client.resolve_account_number(account_number, bank_code) 524 | 525 | 526 | @mcp.tool(name="verification.resolve_card_bin") 527 | def resolve_card_bin(card_bin: str): 528 | """ 529 | Resolves a card BIN to get the associated card details. 530 | 531 | Args: 532 | card_bin: The card BIN to resolve. 533 | """ 534 | return paystack_client.resolve_card_bin(card_bin) 535 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.12" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 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, upload-time = "2024-05-20T21:33:24.1Z" }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.11.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/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, 26 | ] 27 | 28 | [[package]] 29 | name = "attrs" 30 | version = "25.3.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, 35 | ] 36 | 37 | [[package]] 38 | name = "certifi" 39 | version = "2025.8.3" 40 | source = { registry = "https://pypi.org/simple" } 41 | sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } 42 | wheels = [ 43 | { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, 44 | ] 45 | 46 | [[package]] 47 | name = "click" 48 | version = "8.3.0" 49 | source = { registry = "https://pypi.org/simple" } 50 | dependencies = [ 51 | { name = "colorama", marker = "sys_platform == 'win32'" }, 52 | ] 53 | sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } 54 | wheels = [ 55 | { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, 56 | ] 57 | 58 | [[package]] 59 | name = "colorama" 60 | version = "0.4.6" 61 | source = { registry = "https://pypi.org/simple" } 62 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } 63 | wheels = [ 64 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, 65 | ] 66 | 67 | [[package]] 68 | name = "h11" 69 | version = "0.16.0" 70 | source = { registry = "https://pypi.org/simple" } 71 | sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } 72 | wheels = [ 73 | { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, 74 | ] 75 | 76 | [[package]] 77 | name = "httpcore" 78 | version = "1.0.9" 79 | source = { registry = "https://pypi.org/simple" } 80 | dependencies = [ 81 | { name = "certifi" }, 82 | { name = "h11" }, 83 | ] 84 | sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } 85 | wheels = [ 86 | { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, 87 | ] 88 | 89 | [[package]] 90 | name = "httpx" 91 | version = "0.28.1" 92 | source = { registry = "https://pypi.org/simple" } 93 | dependencies = [ 94 | { name = "anyio" }, 95 | { name = "certifi" }, 96 | { name = "httpcore" }, 97 | { name = "idna" }, 98 | ] 99 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } 100 | wheels = [ 101 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, 102 | ] 103 | 104 | [[package]] 105 | name = "httpx-sse" 106 | version = "0.4.1" 107 | source = { registry = "https://pypi.org/simple" } 108 | sdist = { url = "https://files.pythonhosted.org/packages/6e/fa/66bd985dd0b7c109a3bcb89272ee0bfb7e2b4d06309ad7b38ff866734b2a/httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e", size = 12998, upload-time = "2025-06-24T13:21:05.71Z" } 109 | wheels = [ 110 | { url = "https://files.pythonhosted.org/packages/25/0a/6269e3473b09aed2dab8aa1a600c70f31f00ae1349bee30658f7e358a159/httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37", size = 8054, upload-time = "2025-06-24T13:21:04.772Z" }, 111 | ] 112 | 113 | [[package]] 114 | name = "idna" 115 | version = "3.10" 116 | source = { registry = "https://pypi.org/simple" } 117 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } 118 | wheels = [ 119 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, 120 | ] 121 | 122 | [[package]] 123 | name = "iniconfig" 124 | version = "2.1.0" 125 | source = { registry = "https://pypi.org/simple" } 126 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } 127 | wheels = [ 128 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, 129 | ] 130 | 131 | [[package]] 132 | name = "jsonschema" 133 | version = "4.25.1" 134 | source = { registry = "https://pypi.org/simple" } 135 | dependencies = [ 136 | { name = "attrs" }, 137 | { name = "jsonschema-specifications" }, 138 | { name = "referencing" }, 139 | { name = "rpds-py" }, 140 | ] 141 | sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } 142 | wheels = [ 143 | { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, 144 | ] 145 | 146 | [[package]] 147 | name = "jsonschema-specifications" 148 | version = "2025.9.1" 149 | source = { registry = "https://pypi.org/simple" } 150 | dependencies = [ 151 | { name = "referencing" }, 152 | ] 153 | sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } 154 | wheels = [ 155 | { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, 156 | ] 157 | 158 | [[package]] 159 | name = "markdown-it-py" 160 | version = "4.0.0" 161 | source = { registry = "https://pypi.org/simple" } 162 | dependencies = [ 163 | { name = "mdurl" }, 164 | ] 165 | sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } 166 | wheels = [ 167 | { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, 168 | ] 169 | 170 | [[package]] 171 | name = "mcp" 172 | version = "1.15.0" 173 | source = { registry = "https://pypi.org/simple" } 174 | dependencies = [ 175 | { name = "anyio" }, 176 | { name = "httpx" }, 177 | { name = "httpx-sse" }, 178 | { name = "jsonschema" }, 179 | { name = "pydantic" }, 180 | { name = "pydantic-settings" }, 181 | { name = "python-multipart" }, 182 | { name = "pywin32", marker = "sys_platform == 'win32'" }, 183 | { name = "sse-starlette" }, 184 | { name = "starlette" }, 185 | { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 186 | ] 187 | sdist = { url = "https://files.pythonhosted.org/packages/0c/9e/e65114795f359f314d7061f4fcb50dfe60026b01b52ad0b986b4631bf8bb/mcp-1.15.0.tar.gz", hash = "sha256:5bda1f4d383cf539d3c035b3505a3de94b20dbd7e4e8b4bd071e14634eeb2d72", size = 469622, upload-time = "2025-09-25T15:39:51.995Z" } 188 | wheels = [ 189 | { url = "https://files.pythonhosted.org/packages/c9/82/4d0df23d5ff5bb982a59ad597bc7cb9920f2650278ccefb8e0d85c5ce3d4/mcp-1.15.0-py3-none-any.whl", hash = "sha256:314614c8addc67b663d6c3e4054db0a5c3dedc416c24ef8ce954e203fdc2333d", size = 166963, upload-time = "2025-09-25T15:39:50.538Z" }, 190 | ] 191 | 192 | [package.optional-dependencies] 193 | cli = [ 194 | { name = "python-dotenv" }, 195 | { name = "typer" }, 196 | ] 197 | 198 | [[package]] 199 | name = "mdurl" 200 | version = "0.1.2" 201 | source = { registry = "https://pypi.org/simple" } 202 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } 203 | wheels = [ 204 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, 205 | ] 206 | 207 | [[package]] 208 | name = "packaging" 209 | version = "25.0" 210 | source = { registry = "https://pypi.org/simple" } 211 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 212 | wheels = [ 213 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 214 | ] 215 | 216 | [[package]] 217 | name = "paystack-python-mcp" 218 | version = "0.1.0" 219 | source = { virtual = "." } 220 | dependencies = [ 221 | { name = "httpx" }, 222 | { name = "mcp", extra = ["cli"] }, 223 | { name = "paystack-sdk" }, 224 | ] 225 | 226 | [package.dev-dependencies] 227 | dev = [ 228 | { name = "pytest" }, 229 | { name = "ruff" }, 230 | ] 231 | 232 | [package.metadata] 233 | requires-dist = [ 234 | { name = "httpx", specifier = ">=0.28.1" }, 235 | { name = "mcp", extras = ["cli"], specifier = ">=1.15.0" }, 236 | { name = "paystack-sdk", specifier = ">=0.0.10" }, 237 | ] 238 | 239 | [package.metadata.requires-dev] 240 | dev = [ 241 | { name = "pytest", specifier = ">=8.4.2" }, 242 | { name = "ruff", specifier = ">=0.13.2" }, 243 | ] 244 | 245 | [[package]] 246 | name = "paystack-sdk" 247 | version = "1.0.1" 248 | source = { registry = "https://pypi.org/simple" } 249 | dependencies = [ 250 | { name = "python-dateutil" }, 251 | { name = "six" }, 252 | { name = "urllib3" }, 253 | ] 254 | sdist = { url = "https://files.pythonhosted.org/packages/e7/49/ee4c8b99bd51816b33893236df967786957691642c07d5b583cb249bb9b7/paystack-sdk-1.0.1.tar.gz", hash = "sha256:6fb11fe9e70a7593500897470b89a51be0e39ed87f36cada8c2f95f1689caf46", size = 73568, upload-time = "2022-09-28T10:40:34.935Z" } 255 | 256 | [[package]] 257 | name = "pluggy" 258 | version = "1.6.0" 259 | source = { registry = "https://pypi.org/simple" } 260 | sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } 261 | wheels = [ 262 | { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, 263 | ] 264 | 265 | [[package]] 266 | name = "pydantic" 267 | version = "2.11.9" 268 | source = { registry = "https://pypi.org/simple" } 269 | dependencies = [ 270 | { name = "annotated-types" }, 271 | { name = "pydantic-core" }, 272 | { name = "typing-extensions" }, 273 | { name = "typing-inspection" }, 274 | ] 275 | sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } 276 | wheels = [ 277 | { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, 278 | ] 279 | 280 | [[package]] 281 | name = "pydantic-core" 282 | version = "2.33.2" 283 | source = { registry = "https://pypi.org/simple" } 284 | dependencies = [ 285 | { name = "typing-extensions" }, 286 | ] 287 | sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } 288 | wheels = [ 289 | { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, 290 | { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, 291 | { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, 292 | { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, 293 | { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, 294 | { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, 295 | { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, 296 | { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, 297 | { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, 298 | { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, 299 | { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, 300 | { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, 301 | { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, 302 | { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, 303 | { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, 304 | { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, 305 | { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, 306 | { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, 307 | { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, 308 | { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, 309 | { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, 310 | { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, 311 | { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, 312 | { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, 313 | { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, 314 | { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, 315 | { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, 316 | { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, 317 | { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, 318 | { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, 319 | { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, 320 | ] 321 | 322 | [[package]] 323 | name = "pydantic-settings" 324 | version = "2.11.0" 325 | source = { registry = "https://pypi.org/simple" } 326 | dependencies = [ 327 | { name = "pydantic" }, 328 | { name = "python-dotenv" }, 329 | { name = "typing-inspection" }, 330 | ] 331 | sdist = { url = "https://files.pythonhosted.org/packages/20/c5/dbbc27b814c71676593d1c3f718e6cd7d4f00652cefa24b75f7aa3efb25e/pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180", size = 188394, upload-time = "2025-09-24T14:19:11.764Z" } 332 | wheels = [ 333 | { url = "https://files.pythonhosted.org/packages/83/d6/887a1ff844e64aa823fb4905978d882a633cfe295c32eacad582b78a7d8b/pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c", size = 48608, upload-time = "2025-09-24T14:19:10.015Z" }, 334 | ] 335 | 336 | [[package]] 337 | name = "pygments" 338 | version = "2.19.2" 339 | source = { registry = "https://pypi.org/simple" } 340 | sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } 341 | wheels = [ 342 | { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, 343 | ] 344 | 345 | [[package]] 346 | name = "pytest" 347 | version = "8.4.2" 348 | source = { registry = "https://pypi.org/simple" } 349 | dependencies = [ 350 | { name = "colorama", marker = "sys_platform == 'win32'" }, 351 | { name = "iniconfig" }, 352 | { name = "packaging" }, 353 | { name = "pluggy" }, 354 | { name = "pygments" }, 355 | ] 356 | sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } 357 | wheels = [ 358 | { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, 359 | ] 360 | 361 | [[package]] 362 | name = "python-dateutil" 363 | version = "2.9.0.post0" 364 | source = { registry = "https://pypi.org/simple" } 365 | dependencies = [ 366 | { name = "six" }, 367 | ] 368 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } 369 | wheels = [ 370 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, 371 | ] 372 | 373 | [[package]] 374 | name = "python-dotenv" 375 | version = "1.1.1" 376 | source = { registry = "https://pypi.org/simple" } 377 | sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } 378 | wheels = [ 379 | { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, 380 | ] 381 | 382 | [[package]] 383 | name = "python-multipart" 384 | version = "0.0.20" 385 | source = { registry = "https://pypi.org/simple" } 386 | sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } 387 | wheels = [ 388 | { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, 389 | ] 390 | 391 | [[package]] 392 | name = "pywin32" 393 | version = "311" 394 | source = { registry = "https://pypi.org/simple" } 395 | wheels = [ 396 | { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, 397 | { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, 398 | { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, 399 | { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, 400 | { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, 401 | { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, 402 | { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, 403 | { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, 404 | { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, 405 | ] 406 | 407 | [[package]] 408 | name = "referencing" 409 | version = "0.36.2" 410 | source = { registry = "https://pypi.org/simple" } 411 | dependencies = [ 412 | { name = "attrs" }, 413 | { name = "rpds-py" }, 414 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 415 | ] 416 | sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } 417 | wheels = [ 418 | { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, 419 | ] 420 | 421 | [[package]] 422 | name = "rich" 423 | version = "14.1.0" 424 | source = { registry = "https://pypi.org/simple" } 425 | dependencies = [ 426 | { name = "markdown-it-py" }, 427 | { name = "pygments" }, 428 | ] 429 | sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" } 430 | wheels = [ 431 | { url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" }, 432 | ] 433 | 434 | [[package]] 435 | name = "rpds-py" 436 | version = "0.27.1" 437 | source = { registry = "https://pypi.org/simple" } 438 | sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } 439 | wheels = [ 440 | { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, 441 | { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, 442 | { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, 443 | { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, 444 | { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, 445 | { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, 446 | { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, 447 | { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, 448 | { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, 449 | { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, 450 | { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, 451 | { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, 452 | { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, 453 | { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, 454 | { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, 455 | { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, 456 | { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, 457 | { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, 458 | { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, 459 | { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, 460 | { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, 461 | { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, 462 | { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, 463 | { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, 464 | { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, 465 | { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, 466 | { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, 467 | { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, 468 | { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, 469 | { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, 470 | { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, 471 | { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, 472 | { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, 473 | { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, 474 | { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, 475 | { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, 476 | { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, 477 | { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, 478 | { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, 479 | { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, 480 | { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, 481 | { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, 482 | { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, 483 | { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, 484 | { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" }, 485 | { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" }, 486 | { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" }, 487 | { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" }, 488 | { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" }, 489 | { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" }, 490 | { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" }, 491 | { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" }, 492 | { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" }, 493 | { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" }, 494 | { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" }, 495 | { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" }, 496 | { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" }, 497 | { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" }, 498 | { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" }, 499 | { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" }, 500 | { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" }, 501 | { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" }, 502 | { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" }, 503 | { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" }, 504 | { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" }, 505 | { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" }, 506 | { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" }, 507 | { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" }, 508 | { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" }, 509 | { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" }, 510 | { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, 511 | { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, 512 | { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, 513 | ] 514 | 515 | [[package]] 516 | name = "ruff" 517 | version = "0.13.2" 518 | source = { registry = "https://pypi.org/simple" } 519 | sdist = { url = "https://files.pythonhosted.org/packages/02/df/8d7d8c515d33adfc540e2edf6c6021ea1c5a58a678d8cfce9fae59aabcab/ruff-0.13.2.tar.gz", hash = "sha256:cb12fffd32fb16d32cef4ed16d8c7cdc27ed7c944eaa98d99d01ab7ab0b710ff", size = 5416417, upload-time = "2025-09-25T14:54:09.936Z" } 520 | wheels = [ 521 | { url = "https://files.pythonhosted.org/packages/6e/84/5716a7fa4758e41bf70e603e13637c42cfb9dbf7ceb07180211b9bbf75ef/ruff-0.13.2-py3-none-linux_armv6l.whl", hash = "sha256:3796345842b55f033a78285e4f1641078f902020d8450cade03aad01bffd81c3", size = 12343254, upload-time = "2025-09-25T14:53:27.784Z" }, 522 | { url = "https://files.pythonhosted.org/packages/9b/77/c7042582401bb9ac8eff25360e9335e901d7a1c0749a2b28ba4ecb239991/ruff-0.13.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ff7e4dda12e683e9709ac89e2dd436abf31a4d8a8fc3d89656231ed808e231d2", size = 13040891, upload-time = "2025-09-25T14:53:31.38Z" }, 523 | { url = "https://files.pythonhosted.org/packages/c6/15/125a7f76eb295cb34d19c6778e3a82ace33730ad4e6f28d3427e134a02e0/ruff-0.13.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c75e9d2a2fafd1fdd895d0e7e24b44355984affdde1c412a6f6d3f6e16b22d46", size = 12243588, upload-time = "2025-09-25T14:53:33.543Z" }, 524 | { url = "https://files.pythonhosted.org/packages/9e/eb/0093ae04a70f81f8be7fd7ed6456e926b65d238fc122311293d033fdf91e/ruff-0.13.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cceac74e7bbc53ed7d15d1042ffe7b6577bf294611ad90393bf9b2a0f0ec7cb6", size = 12491359, upload-time = "2025-09-25T14:53:35.892Z" }, 525 | { url = "https://files.pythonhosted.org/packages/43/fe/72b525948a6956f07dad4a6f122336b6a05f2e3fd27471cea612349fedb9/ruff-0.13.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae3f469b5465ba6d9721383ae9d49310c19b452a161b57507764d7ef15f4b07", size = 12162486, upload-time = "2025-09-25T14:53:38.171Z" }, 526 | { url = "https://files.pythonhosted.org/packages/6a/e3/0fac422bbbfb2ea838023e0d9fcf1f30183d83ab2482800e2cb892d02dfe/ruff-0.13.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f8f9e3cd6714358238cd6626b9d43026ed19c0c018376ac1ef3c3a04ffb42d8", size = 13871203, upload-time = "2025-09-25T14:53:41.943Z" }, 527 | { url = "https://files.pythonhosted.org/packages/6b/82/b721c8e3ec5df6d83ba0e45dcf00892c4f98b325256c42c38ef136496cbf/ruff-0.13.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c6ed79584a8f6cbe2e5d7dbacf7cc1ee29cbdb5df1172e77fbdadc8bb85a1f89", size = 14929635, upload-time = "2025-09-25T14:53:43.953Z" }, 528 | { url = "https://files.pythonhosted.org/packages/c4/a0/ad56faf6daa507b83079a1ad7a11694b87d61e6bf01c66bd82b466f21821/ruff-0.13.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aed130b2fde049cea2019f55deb939103123cdd191105f97a0599a3e753d61b0", size = 14338783, upload-time = "2025-09-25T14:53:46.205Z" }, 529 | { url = "https://files.pythonhosted.org/packages/47/77/ad1d9156db8f99cd01ee7e29d74b34050e8075a8438e589121fcd25c4b08/ruff-0.13.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1887c230c2c9d65ed1b4e4cfe4d255577ea28b718ae226c348ae68df958191aa", size = 13355322, upload-time = "2025-09-25T14:53:48.164Z" }, 530 | { url = "https://files.pythonhosted.org/packages/64/8b/e87cfca2be6f8b9f41f0bb12dc48c6455e2d66df46fe61bb441a226f1089/ruff-0.13.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bcb10276b69b3cfea3a102ca119ffe5c6ba3901e20e60cf9efb53fa417633c3", size = 13354427, upload-time = "2025-09-25T14:53:50.486Z" }, 531 | { url = "https://files.pythonhosted.org/packages/7f/df/bf382f3fbead082a575edb860897287f42b1b3c694bafa16bc9904c11ed3/ruff-0.13.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:afa721017aa55a555b2ff7944816587f1cb813c2c0a882d158f59b832da1660d", size = 13537637, upload-time = "2025-09-25T14:53:52.887Z" }, 532 | { url = "https://files.pythonhosted.org/packages/51/70/1fb7a7c8a6fc8bd15636288a46e209e81913b87988f26e1913d0851e54f4/ruff-0.13.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1dbc875cf3720c64b3990fef8939334e74cb0ca65b8dbc61d1f439201a38101b", size = 12340025, upload-time = "2025-09-25T14:53:54.88Z" }, 533 | { url = "https://files.pythonhosted.org/packages/4c/27/1e5b3f1c23ca5dd4106d9d580e5c13d9acb70288bff614b3d7b638378cc9/ruff-0.13.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939a1b2a960e9742e9a347e5bbc9b3c3d2c716f86c6ae273d9cbd64f193f22", size = 12133449, upload-time = "2025-09-25T14:53:57.089Z" }, 534 | { url = "https://files.pythonhosted.org/packages/2d/09/b92a5ccee289f11ab128df57d5911224197d8d55ef3bd2043534ff72ca54/ruff-0.13.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:50e2d52acb8de3804fc5f6e2fa3ae9bdc6812410a9e46837e673ad1f90a18736", size = 13051369, upload-time = "2025-09-25T14:53:59.124Z" }, 535 | { url = "https://files.pythonhosted.org/packages/89/99/26c9d1c7d8150f45e346dc045cc49f23e961efceb4a70c47dea0960dea9a/ruff-0.13.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3196bc13ab2110c176b9a4ae5ff7ab676faaa1964b330a1383ba20e1e19645f2", size = 13523644, upload-time = "2025-09-25T14:54:01.622Z" }, 536 | { url = "https://files.pythonhosted.org/packages/f7/00/e7f1501e81e8ec290e79527827af1d88f541d8d26151751b46108978dade/ruff-0.13.2-py3-none-win32.whl", hash = "sha256:7c2a0b7c1e87795fec3404a485096bcd790216c7c146a922d121d8b9c8f1aaac", size = 12245990, upload-time = "2025-09-25T14:54:03.647Z" }, 537 | { url = "https://files.pythonhosted.org/packages/ee/bd/d9f33a73de84fafd0146c6fba4f497c4565fe8fa8b46874b8e438869abc2/ruff-0.13.2-py3-none-win_amd64.whl", hash = "sha256:17d95fb32218357c89355f6f6f9a804133e404fc1f65694372e02a557edf8585", size = 13324004, upload-time = "2025-09-25T14:54:06.05Z" }, 538 | { url = "https://files.pythonhosted.org/packages/c3/12/28fa2f597a605884deb0f65c1b1ae05111051b2a7030f5d8a4ff7f4599ba/ruff-0.13.2-py3-none-win_arm64.whl", hash = "sha256:da711b14c530412c827219312b7d7fbb4877fb31150083add7e8c5336549cea7", size = 12484437, upload-time = "2025-09-25T14:54:08.022Z" }, 539 | ] 540 | 541 | [[package]] 542 | name = "shellingham" 543 | version = "1.5.4" 544 | source = { registry = "https://pypi.org/simple" } 545 | sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } 546 | wheels = [ 547 | { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, 548 | ] 549 | 550 | [[package]] 551 | name = "six" 552 | version = "1.17.0" 553 | source = { registry = "https://pypi.org/simple" } 554 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } 555 | wheels = [ 556 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, 557 | ] 558 | 559 | [[package]] 560 | name = "sniffio" 561 | version = "1.3.1" 562 | source = { registry = "https://pypi.org/simple" } 563 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 564 | wheels = [ 565 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 566 | ] 567 | 568 | [[package]] 569 | name = "sse-starlette" 570 | version = "3.0.2" 571 | source = { registry = "https://pypi.org/simple" } 572 | dependencies = [ 573 | { name = "anyio" }, 574 | ] 575 | sdist = { url = "https://files.pythonhosted.org/packages/42/6f/22ed6e33f8a9e76ca0a412405f31abb844b779d52c5f96660766edcd737c/sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a", size = 20985, upload-time = "2025-07-27T09:07:44.565Z" } 576 | wheels = [ 577 | { url = "https://files.pythonhosted.org/packages/ef/10/c78f463b4ef22eef8491f218f692be838282cd65480f6e423d7730dfd1fb/sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a", size = 11297, upload-time = "2025-07-27T09:07:43.268Z" }, 578 | ] 579 | 580 | [[package]] 581 | name = "starlette" 582 | version = "0.48.0" 583 | source = { registry = "https://pypi.org/simple" } 584 | dependencies = [ 585 | { name = "anyio" }, 586 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 587 | ] 588 | sdist = { url = "https://files.pythonhosted.org/packages/a7/a5/d6f429d43394057b67a6b5bbe6eae2f77a6bf7459d961fdb224bf206eee6/starlette-0.48.0.tar.gz", hash = "sha256:7e8cee469a8ab2352911528110ce9088fdc6a37d9876926e73da7ce4aa4c7a46", size = 2652949, upload-time = "2025-09-13T08:41:05.699Z" } 589 | wheels = [ 590 | { url = "https://files.pythonhosted.org/packages/be/72/2db2f49247d0a18b4f1bb9a5a39a0162869acf235f3a96418363947b3d46/starlette-0.48.0-py3-none-any.whl", hash = "sha256:0764ca97b097582558ecb498132ed0c7d942f233f365b86ba37770e026510659", size = 73736, upload-time = "2025-09-13T08:41:03.869Z" }, 591 | ] 592 | 593 | [[package]] 594 | name = "typer" 595 | version = "0.19.2" 596 | source = { registry = "https://pypi.org/simple" } 597 | dependencies = [ 598 | { name = "click" }, 599 | { name = "rich" }, 600 | { name = "shellingham" }, 601 | { name = "typing-extensions" }, 602 | ] 603 | sdist = { url = "https://files.pythonhosted.org/packages/21/ca/950278884e2ca20547ff3eb109478c6baf6b8cf219318e6bc4f666fad8e8/typer-0.19.2.tar.gz", hash = "sha256:9ad824308ded0ad06cc716434705f691d4ee0bfd0fb081839d2e426860e7fdca", size = 104755, upload-time = "2025-09-23T09:47:48.256Z" } 604 | wheels = [ 605 | { url = "https://files.pythonhosted.org/packages/00/22/35617eee79080a5d071d0f14ad698d325ee6b3bf824fc0467c03b30e7fa8/typer-0.19.2-py3-none-any.whl", hash = "sha256:755e7e19670ffad8283db353267cb81ef252f595aa6834a0d1ca9312d9326cb9", size = 46748, upload-time = "2025-09-23T09:47:46.777Z" }, 606 | ] 607 | 608 | [[package]] 609 | name = "typing-extensions" 610 | version = "4.15.0" 611 | source = { registry = "https://pypi.org/simple" } 612 | sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } 613 | wheels = [ 614 | { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, 615 | ] 616 | 617 | [[package]] 618 | name = "typing-inspection" 619 | version = "0.4.1" 620 | source = { registry = "https://pypi.org/simple" } 621 | dependencies = [ 622 | { name = "typing-extensions" }, 623 | ] 624 | sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } 625 | wheels = [ 626 | { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, 627 | ] 628 | 629 | [[package]] 630 | name = "urllib3" 631 | version = "2.5.0" 632 | source = { registry = "https://pypi.org/simple" } 633 | sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } 634 | wheels = [ 635 | { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, 636 | ] 637 | 638 | [[package]] 639 | name = "uvicorn" 640 | version = "0.37.0" 641 | source = { registry = "https://pypi.org/simple" } 642 | dependencies = [ 643 | { name = "click" }, 644 | { name = "h11" }, 645 | ] 646 | sdist = { url = "https://files.pythonhosted.org/packages/71/57/1616c8274c3442d802621abf5deb230771c7a0fec9414cb6763900eb3868/uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13", size = 80367, upload-time = "2025-09-23T13:33:47.486Z" } 647 | wheels = [ 648 | { url = "https://files.pythonhosted.org/packages/85/cd/584a2ceb5532af99dd09e50919e3615ba99aa127e9850eafe5f31ddfdb9a/uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c", size = 67976, upload-time = "2025-09-23T13:33:45.842Z" }, 649 | ] 650 | --------------------------------------------------------------------------------