├── .python-version ├── cyberchef_api_mcp_server ├── __init__.py ├── __main__.py ├── cyberchefoperations.py └── server.py ├── pyproject.toml ├── tests ├── test_cyberchefoperations.py └── test_server.py ├── LICENSE ├── Dockerfile ├── README.md ├── .gitignore └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.11 2 | -------------------------------------------------------------------------------- /cyberchef_api_mcp_server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cyberchef_api_mcp_server/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from cyberchef_api_mcp_server.server import main 3 | 4 | sys.exit(main()) -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "cyberchef-api-mcp-server" 3 | version = "0.1.0" 4 | description = "MCP server that has the ability to interact with the CyberChef server RESTful API" 5 | readme = "README.md" 6 | requires-python = ">=3.11" 7 | keywords = ["mcp", "llm", "cyberchef", "cyber", "web", "fetch"] 8 | license = { text = "MIT"} 9 | dependencies = [ 10 | "httpx>=0.28.1", 11 | "mcp>=1.6.0", 12 | ] 13 | 14 | [project.scripts] 15 | cyberchef-api-mcp-server = "cyberchef_api_mcp_server.server:main" 16 | 17 | [build-system] 18 | requires = ["hatchling"] 19 | build-backend = "hatchling.build" 20 | 21 | [tool.hatch.build.targets.wheel] 22 | packages = ["cyberchef_api_mcp_server"] 23 | 24 | [tool.uv] 25 | dev-dependencies = ["pytest>=8.3.5"] -------------------------------------------------------------------------------- /tests/test_cyberchefoperations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from cyberchef_api_mcp_server.cyberchefoperations import CyberChefOperations 5 | 6 | cyberchef_ops = CyberChefOperations() 7 | 8 | 9 | def test_get_all_data(): 10 | all_data = cyberchef_ops.get_all_data() 11 | assert len(all_data) > 1 12 | 13 | 14 | def test_get_all_categories(): 15 | all_cats = cyberchef_ops.get_all_categories() 16 | assert len(all_cats) > 1 17 | 18 | 19 | def test_get_all_operations(): 20 | ops = cyberchef_ops.get_all_operations() 21 | assert len(ops) > 1 22 | 23 | 24 | def test_get_all_operations_by_category_pass(): 25 | ops_by_cat = cyberchef_ops.get_operations_by_category(category="Data Format") 26 | assert len(ops_by_cat) > 1 27 | 28 | 29 | def test_get_all_operations_by_category_fail(): 30 | ops_by_cat = cyberchef_ops.get_operations_by_category(category="Not A Category") 31 | assert len(ops_by_cat) == 0 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 alex 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 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # First, build the application in the `/app` directory. 2 | # See `Dockerfile` for details. 3 | FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS builder 4 | ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy 5 | 6 | # Disable Python downloads, because we want to use the system interpreter 7 | # across both images. If using a managed Python version, it needs to be 8 | # copied from the build image into the final image; see `standalone.Dockerfile` 9 | # for an example. 10 | ENV UV_PYTHON_DOWNLOADS=0 11 | 12 | WORKDIR /app 13 | RUN --mount=type=cache,target=/root/.cache/uv \ 14 | --mount=type=bind,source=uv.lock,target=uv.lock \ 15 | --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 16 | uv sync --frozen --no-install-project --no-dev 17 | ADD . /app 18 | RUN --mount=type=cache,target=/root/.cache/uv \ 19 | uv sync --frozen --no-dev --no-editable 20 | 21 | # Then, use a final image without uv 22 | FROM python:3.11-slim-bookworm 23 | 24 | # Copy the application from the builder 25 | COPY --from=builder --chown=app:app /app /app 26 | 27 | # Place executables in the environment at the front of the path 28 | ENV PATH="/app/.venv/bin:$PATH" 29 | 30 | # Run the MCP server 31 | CMD ["cyberchef-api-mcp-server"] -------------------------------------------------------------------------------- /tests/test_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from cyberchef_api_mcp_server.server import ( 5 | bake_recipe, batch_bake_recipe, perform_magic_operation, CyberChefRecipeOperation 6 | ) 7 | 8 | 9 | def test_bake_recipe(): 10 | test_input = "64 47 56 7a 64 41 3d 3d" 11 | test_recipe = [ 12 | CyberChefRecipeOperation(op="From Hex", args=["Auto"]), 13 | CyberChefRecipeOperation(op="From Base64") 14 | ] 15 | recipe_response = bake_recipe(input_data=test_input, recipe=test_recipe) 16 | 17 | assert recipe_response["value"] == "test" 18 | 19 | 20 | def test_batch_bake_recipe(): 21 | test_input = [ 22 | "64 47 56 7a 64 41 3d 3d", 23 | "64 47 56 7a 64 44 49 3d" 24 | ] 25 | test_recipe = [ 26 | CyberChefRecipeOperation(op="From Hex", args=["Auto"]), 27 | CyberChefRecipeOperation(op="From Base64") 28 | ] 29 | recipe_response = batch_bake_recipe(batch_input_data=test_input, recipe=test_recipe) 30 | recipe_response_parse = [value.get("value") for value in recipe_response] 31 | 32 | assert recipe_response_parse == ["test", "test2"] 33 | 34 | 35 | def test_perform_magic_operation(): 36 | test_input = "64 47 56 7a 64 41 3d 3d" 37 | recipe_response = perform_magic_operation(input_data=test_input) 38 | 39 | assert recipe_response["value"][0]["data"] == "test" 40 | assert recipe_response["value"][1]["data"] == "dGVzdA==" 41 | assert recipe_response["value"][2]["data"] == test_input 42 | -------------------------------------------------------------------------------- /cyberchef_api_mcp_server/cyberchefoperations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import httpx 5 | import logging 6 | 7 | log = logging.getLogger(__name__) 8 | logging.basicConfig(level=logging.INFO) 9 | 10 | 11 | class CyberChefOperations: 12 | def __init__(self): 13 | """ 14 | Get all Cyber Chef operations, either by category or in entirety 15 | """ 16 | self.ops_url = "https://raw.githubusercontent.com/gchq/CyberChef/refs/heads/master/src/core/config/Categories.json" 17 | self.request_headers = { 18 | "Accept": "application/json", 19 | "Content-Type": "application/json" 20 | } 21 | 22 | def get_all_data(self) -> dict: 23 | """Get all categories and operations data from the Cyber Chef GitHub project""" 24 | try: 25 | # Get JSON file from GitHub repo 26 | response = httpx.get(url=self.ops_url, headers=self.request_headers) 27 | response.raise_for_status() 28 | response_dict = response.json() 29 | return response_dict 30 | except httpx.RequestError as req_exc: 31 | log.error(f"Exception raised during HTTP GET request to {self.ops_url} - {req_exc}") 32 | log.warning("Continuing without grabbing updated CyberChef operations data...") 33 | 34 | def get_all_categories(self) -> list: 35 | """Get a list of all categories that CyberChef groups the operations by""" 36 | # Get all the data 37 | response_dict = self.get_all_data() 38 | 39 | # Parse response 40 | cyberchef_categories = [] 41 | for category in response_dict: 42 | cyberchef_categories.append(category["name"]) 43 | 44 | return cyberchef_categories 45 | 46 | def get_all_operations(self) -> list: 47 | """Get a list of all operations Cyber Chef can perform""" 48 | # Get all the data 49 | response_dict = self.get_all_data() 50 | 51 | # Parse response 52 | cyberchef_operations = [] 53 | for category in response_dict: 54 | cyberchef_operations.extend(category["ops"]) 55 | 56 | return cyberchef_operations 57 | 58 | def get_operations_by_category(self, category: str) -> list: 59 | """ 60 | Get a list of operations belonging to a Cyber Chef category 61 | 62 | :param category: operations belonging to this category 63 | :return: 64 | """ 65 | # Get all the data 66 | response_dict = self.get_all_data() 67 | 68 | # Parse response 69 | operations = [] 70 | for category_obj in response_dict: 71 | if category_obj["name"].lower() == category.lower(): 72 | operations.extend(category_obj["ops"]) 73 | 74 | return operations 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CyberChef API MCP Server 2 | 3 | This model context protocol (MCP) server interfaces with the [CyberChef Server](https://github.com/gchq/CyberChef-server) API. Allowing you to use any LLM/MCP client of your choosing to utilise the tools and resources within CyberChef. 4 | 5 | 🧰 Available Tools and Resources 6 | --- 7 | - `get_cyberchef_operations_categories`: __resource__ - gets updated Cyber Chef categories for additional context / selection of the correct operations 8 | - `get_cyberchef_operation_by_category`: __resource__ - gets list of Cyber Chef operations for a selected category 9 | - `bake_recipe`: __tool__ - bake (execute) a recipe (a list of operations) in order to derive an outcome from the input data 10 | - `batch_bake_recipe`: __tool__ - bake (execute) a recipe (a list of operations) in order to derive an outcome from a batch of input data 11 | - `perform_magic_operation`: __tool__ - perform CyberChef's magic operation which is designed to automatically detect how your data is encoded and which operations can be used to decode it 12 | 13 | 📝 Usage 14 | --- 15 | Start the server using the default stdio transport and specifying an environment variable pointing to a CyberChef API 16 | 17 | ```bash 18 | CYBERCHEF_API_URL="your-cyberchef-api-url" uv run cyberchef_api_mcp_server 19 | ``` 20 | 21 | 🧑‍💻Usage (Development) 22 | --- 23 | Start the server and test it with the MCP inspector 24 | 25 | ```bash 26 | uv add "mcp[cli]" 27 | mcp dev server.py 28 | ``` 29 | 30 | 📚 Client Configuration 31 | --- 32 | The following commands will generate a client configuration file, the location will depend on your operating system 33 | 34 | ```bash 35 | uv add "mcp[cli]" 36 | mcp install server.py --name "CyberChef API MCP Server" 37 | ``` 38 | 39 | > [!TIP] 40 | > After running the above command you can then tweak the client configuration to include the environment variable for the CyberChef API URL 41 | 42 | ```json 43 | { 44 | "mcpServers": { 45 | "CyberChef API MCP Server": { 46 | "command": "uv", 47 | "args": [ 48 | "run", 49 | "--with", 50 | "mcp[cli]", 51 | "--directory", 52 | "cyberchef-api-mcp-server/cyberchef_api_mcp_server/", 53 | "mcp", 54 | "run", 55 | "server.py" 56 | ], 57 | "env": { 58 | "CYBERCHEF_API_URL": "your-cyberchef-api-url" 59 | } 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | 🔍 Demo 66 | --- 67 | Using the MCP server in this example use case, the following prerequisites apply: 68 | - You must have Claude desktop installed 69 | - Have a running CyberChef API instance or one you are able to use 70 | 71 | Here is a basic prompt being solved using the MCP server tools: 72 | 73 | 74 | 🙇 References 75 | --- 76 | - [CyberChef](https://github.com/gchq/CyberChef) 77 | - [CyberChef Server](https://github.com/gchq/CyberChef-server) 78 | - [Model Context Protocol](https://github.com/modelcontextprotocol) 79 | - [FastMCP](https://github.com/jlowin/fastmcp) 80 | 81 | 🪪 License 82 | --- 83 | MIT License 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | .idea/ 169 | 170 | # Ruff stuff: 171 | .ruff_cache/ 172 | 173 | # PyPI configuration file 174 | .pypirc 175 | -------------------------------------------------------------------------------- /cyberchef_api_mcp_server/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import httpx 6 | import logging 7 | from typing import Optional 8 | from pydantic import BaseModel 9 | from mcp.server.fastmcp import FastMCP 10 | from cyberchef_api_mcp_server.cyberchefoperations import CyberChefOperations 11 | 12 | log = logging.getLogger(__name__) 13 | logging.basicConfig(level=logging.INFO) 14 | 15 | # Create an MCP server 16 | mcp = FastMCP("CyberChef API MCP Server") 17 | 18 | # Determine the CyberChef API URL 19 | cyberchef_api_url = os.getenv("CYBERCHEF_API_URL") 20 | if cyberchef_api_url is None: 21 | log.warning("There is no environment variable CYBERCHEF_API_URL defaulting to http://localhost:3000/") 22 | cyberchef_api_url = "http://localhost:3000/" 23 | 24 | 25 | class CyberChefRecipeOperation(BaseModel): 26 | """Model for a recipe operation with or without arguments""" 27 | op: str 28 | args: Optional[list] = None 29 | 30 | 31 | def create_api_request(endpoint: str, request_data: dict) -> dict: 32 | """ 33 | Send a POST request to one of the CyberChef API endpoints to process request data and retrieve the response 34 | 35 | :param endpoint: API endpoint to retrieve data from 36 | :param request_data: data to send with the POST request 37 | :return: dict object of response data 38 | """ 39 | api_url = f"{cyberchef_api_url}{endpoint}" 40 | request_headers = { 41 | "Accept": "application/json", 42 | "Content-Type": "application/json" 43 | } 44 | 45 | try: 46 | log.info(f"Attempting to send POST request to {api_url}") 47 | response = httpx.post( 48 | url=api_url, 49 | headers=request_headers, 50 | json=request_data 51 | ) 52 | response.raise_for_status() 53 | return response.json() 54 | except httpx.RequestError as req_exc: 55 | log.error(f"Exception raised during HTTP POST request to {api_url} - {req_exc}") 56 | return {"error": f"Exception raised during HTTP POST request to {api_url} - {req_exc}"} 57 | 58 | 59 | @mcp.resource("data://cyberchef-operations-categories") 60 | def get_cyberchef_operations_categories() -> list: 61 | """Get updated Cyber Chef categories for additional context / selection of the correct operations""" 62 | cyberchef_ops = CyberChefOperations() 63 | return cyberchef_ops.get_all_categories() 64 | 65 | 66 | @mcp.resource("data://cyberchef-operations-by-category/{category}") 67 | def get_cyberchef_operation_by_category(category: str) -> list: 68 | """ 69 | Get list of Cyber Chef operations for a selected category 70 | 71 | :param category: cyber chef category to get operations for 72 | :return: 73 | """ 74 | cyberchef_ops = CyberChefOperations() 75 | return cyberchef_ops.get_operations_by_category(category=category) 76 | 77 | 78 | @mcp.tool() 79 | def bake_recipe(input_data: str, recipe: list[CyberChefRecipeOperation]) -> dict: 80 | """ 81 | Bake (execute) a recipe (a list of operations) in order to derive an outcome from the input data 82 | 83 | :param input_data: the data in which to perform the recipe operation(s) on 84 | :param recipe: a pydantic model of operations to 'bake'/execute on the input data 85 | :return: 86 | """ 87 | request_data = { 88 | "input": input_data, 89 | "recipe": [op.model_dump() for op in recipe] 90 | } 91 | response_data = create_api_request(endpoint="bake", request_data=request_data) 92 | 93 | # If the response has a byte array, decode and return 94 | data_type = response_data.get("type") 95 | if data_type is not None and data_type == "byteArray": 96 | decoded_value = bytes(response_data["value"]).decode() 97 | response_data["value"] = decoded_value 98 | response_data["type"] = "string" 99 | return response_data 100 | else: 101 | return response_data 102 | 103 | 104 | @mcp.tool() 105 | def batch_bake_recipe(batch_input_data: list[str], recipe: list[CyberChefRecipeOperation]) -> dict: 106 | """ 107 | Bake (execute) a recipe (a list of operations) in order to derive an outcome from a batch of input data 108 | 109 | :param batch_input_data: the batch of data in which to perform the recipe operation(s) on 110 | :param recipe: a list of operations to 'bake'/execute on the input data 111 | :return: 112 | """ 113 | request_data = { 114 | "input": batch_input_data, 115 | "recipe": [op.model_dump() for op in recipe] 116 | } 117 | response_data = create_api_request(endpoint="batch/bake", request_data=request_data) 118 | 119 | # If any of the responses have a byte array, decode and return 120 | for response in response_data: 121 | data_type = response.get("type") 122 | if data_type is not None and data_type == "byteArray": 123 | decoded_value = bytes(response["value"]).decode() 124 | response["value"] = decoded_value 125 | response["type"] = "string" 126 | 127 | return response_data 128 | 129 | 130 | @mcp.tool() 131 | def perform_magic_operation( 132 | input_data: str, 133 | depth: int = 3, 134 | intensive_mode: bool = False, 135 | extensive_language_support: bool = False, 136 | crib_str: str = "" 137 | ) -> dict: 138 | """ 139 | CyberChef's magic operation is designed to automatically detect how your data is encoded and which operations can be 140 | used to decode it 141 | 142 | :param input_data: the data in which to perform the magic operation on 143 | :param depth: how many levels of recursion to attempt pattern matching and speculative execution on the input data 144 | :param intensive_mode: optional argument which will run additional operations and take considerably longer to run 145 | :param extensive_language_support: if this is true all 245 languages are supported opposed to the top 38 by default 146 | :param crib_str: argument for any known plaintext string or regex 147 | :return: 148 | """ 149 | request_data = { 150 | "input": input_data, 151 | "args": { 152 | "depth": depth, 153 | "intensive_mode": intensive_mode, 154 | "extensive_language_support": extensive_language_support, 155 | "crib": crib_str 156 | } 157 | } 158 | return create_api_request(endpoint="magic", request_data=request_data) 159 | 160 | 161 | def main(): 162 | """Initialize and run the server""" 163 | log.info("Starting the CyberChef MCP server") 164 | mcp.run(transport="stdio") 165 | 166 | 167 | if __name__ == "__main__": 168 | main() 169 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 1 3 | requires-python = ">=3.11" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.9.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "idna" }, 20 | { name = "sniffio" }, 21 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 22 | ] 23 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, 26 | ] 27 | 28 | [[package]] 29 | name = "certifi" 30 | version = "2025.1.31" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 35 | ] 36 | 37 | [[package]] 38 | name = "click" 39 | version = "8.1.8" 40 | source = { registry = "https://pypi.org/simple" } 41 | dependencies = [ 42 | { name = "colorama", marker = "sys_platform == 'win32'" }, 43 | ] 44 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 45 | wheels = [ 46 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 47 | ] 48 | 49 | [[package]] 50 | name = "colorama" 51 | version = "0.4.6" 52 | source = { registry = "https://pypi.org/simple" } 53 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 54 | wheels = [ 55 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 56 | ] 57 | 58 | [[package]] 59 | name = "cyberchef-api-mcp-server" 60 | version = "0.1.0" 61 | source = { editable = "." } 62 | dependencies = [ 63 | { name = "httpx" }, 64 | { name = "mcp" }, 65 | ] 66 | 67 | [package.dev-dependencies] 68 | dev = [ 69 | { name = "pytest" }, 70 | ] 71 | 72 | [package.metadata] 73 | requires-dist = [ 74 | { name = "httpx", specifier = ">=0.28.1" }, 75 | { name = "mcp", specifier = ">=1.6.0" }, 76 | ] 77 | 78 | [package.metadata.requires-dev] 79 | dev = [{ name = "pytest", specifier = ">=8.3.5" }] 80 | 81 | [[package]] 82 | name = "h11" 83 | version = "0.14.0" 84 | source = { registry = "https://pypi.org/simple" } 85 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 86 | wheels = [ 87 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 88 | ] 89 | 90 | [[package]] 91 | name = "httpcore" 92 | version = "1.0.8" 93 | source = { registry = "https://pypi.org/simple" } 94 | dependencies = [ 95 | { name = "certifi" }, 96 | { name = "h11" }, 97 | ] 98 | sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 } 99 | wheels = [ 100 | { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 }, 101 | ] 102 | 103 | [[package]] 104 | name = "httpx" 105 | version = "0.28.1" 106 | source = { registry = "https://pypi.org/simple" } 107 | dependencies = [ 108 | { name = "anyio" }, 109 | { name = "certifi" }, 110 | { name = "httpcore" }, 111 | { name = "idna" }, 112 | ] 113 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 114 | wheels = [ 115 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 116 | ] 117 | 118 | [[package]] 119 | name = "httpx-sse" 120 | version = "0.4.0" 121 | source = { registry = "https://pypi.org/simple" } 122 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 123 | wheels = [ 124 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 125 | ] 126 | 127 | [[package]] 128 | name = "idna" 129 | version = "3.10" 130 | source = { registry = "https://pypi.org/simple" } 131 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 132 | wheels = [ 133 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 134 | ] 135 | 136 | [[package]] 137 | name = "iniconfig" 138 | version = "2.1.0" 139 | source = { registry = "https://pypi.org/simple" } 140 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } 141 | wheels = [ 142 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, 143 | ] 144 | 145 | [[package]] 146 | name = "mcp" 147 | version = "1.6.0" 148 | source = { registry = "https://pypi.org/simple" } 149 | dependencies = [ 150 | { name = "anyio" }, 151 | { name = "httpx" }, 152 | { name = "httpx-sse" }, 153 | { name = "pydantic" }, 154 | { name = "pydantic-settings" }, 155 | { name = "sse-starlette" }, 156 | { name = "starlette" }, 157 | { name = "uvicorn" }, 158 | ] 159 | sdist = { url = "https://files.pythonhosted.org/packages/95/d2/f587cb965a56e992634bebc8611c5b579af912b74e04eb9164bd49527d21/mcp-1.6.0.tar.gz", hash = "sha256:d9324876de2c5637369f43161cd71eebfd803df5a95e46225cab8d280e366723", size = 200031 } 160 | wheels = [ 161 | { url = "https://files.pythonhosted.org/packages/10/30/20a7f33b0b884a9d14dd3aa94ff1ac9da1479fe2ad66dd9e2736075d2506/mcp-1.6.0-py3-none-any.whl", hash = "sha256:7bd24c6ea042dbec44c754f100984d186620d8b841ec30f1b19eda9b93a634d0", size = 76077 }, 162 | ] 163 | 164 | [[package]] 165 | name = "packaging" 166 | version = "24.2" 167 | source = { registry = "https://pypi.org/simple" } 168 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 169 | wheels = [ 170 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 171 | ] 172 | 173 | [[package]] 174 | name = "pluggy" 175 | version = "1.5.0" 176 | source = { registry = "https://pypi.org/simple" } 177 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 178 | wheels = [ 179 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 180 | ] 181 | 182 | [[package]] 183 | name = "pydantic" 184 | version = "2.11.3" 185 | source = { registry = "https://pypi.org/simple" } 186 | dependencies = [ 187 | { name = "annotated-types" }, 188 | { name = "pydantic-core" }, 189 | { name = "typing-extensions" }, 190 | { name = "typing-inspection" }, 191 | ] 192 | sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } 193 | wheels = [ 194 | { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, 195 | ] 196 | 197 | [[package]] 198 | name = "pydantic-core" 199 | version = "2.33.1" 200 | source = { registry = "https://pypi.org/simple" } 201 | dependencies = [ 202 | { name = "typing-extensions" }, 203 | ] 204 | sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } 205 | wheels = [ 206 | { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224 }, 207 | { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845 }, 208 | { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029 }, 209 | { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784 }, 210 | { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075 }, 211 | { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849 }, 212 | { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794 }, 213 | { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237 }, 214 | { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351 }, 215 | { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914 }, 216 | { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385 }, 217 | { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, 218 | { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, 219 | { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, 220 | { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640 }, 221 | { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649 }, 222 | { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472 }, 223 | { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509 }, 224 | { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702 }, 225 | { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428 }, 226 | { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753 }, 227 | { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849 }, 228 | { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541 }, 229 | { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225 }, 230 | { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373 }, 231 | { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, 232 | { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, 233 | { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, 234 | { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, 235 | { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, 236 | { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, 237 | { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, 238 | { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, 239 | { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, 240 | { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, 241 | { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, 242 | { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, 243 | { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, 244 | { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, 245 | { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, 246 | { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, 247 | { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, 248 | { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, 249 | { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, 250 | { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, 251 | { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858 }, 252 | { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745 }, 253 | { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188 }, 254 | { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479 }, 255 | { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415 }, 256 | { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623 }, 257 | { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175 }, 258 | { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674 }, 259 | { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951 }, 260 | ] 261 | 262 | [[package]] 263 | name = "pydantic-settings" 264 | version = "2.8.1" 265 | source = { registry = "https://pypi.org/simple" } 266 | dependencies = [ 267 | { name = "pydantic" }, 268 | { name = "python-dotenv" }, 269 | ] 270 | sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } 271 | wheels = [ 272 | { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, 273 | ] 274 | 275 | [[package]] 276 | name = "pytest" 277 | version = "8.3.5" 278 | source = { registry = "https://pypi.org/simple" } 279 | dependencies = [ 280 | { name = "colorama", marker = "sys_platform == 'win32'" }, 281 | { name = "iniconfig" }, 282 | { name = "packaging" }, 283 | { name = "pluggy" }, 284 | ] 285 | sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } 286 | wheels = [ 287 | { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, 288 | ] 289 | 290 | [[package]] 291 | name = "python-dotenv" 292 | version = "1.1.0" 293 | source = { registry = "https://pypi.org/simple" } 294 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } 295 | wheels = [ 296 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, 297 | ] 298 | 299 | [[package]] 300 | name = "sniffio" 301 | version = "1.3.1" 302 | source = { registry = "https://pypi.org/simple" } 303 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 304 | wheels = [ 305 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 306 | ] 307 | 308 | [[package]] 309 | name = "sse-starlette" 310 | version = "2.2.1" 311 | source = { registry = "https://pypi.org/simple" } 312 | dependencies = [ 313 | { name = "anyio" }, 314 | { name = "starlette" }, 315 | ] 316 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 317 | wheels = [ 318 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 319 | ] 320 | 321 | [[package]] 322 | name = "starlette" 323 | version = "0.46.2" 324 | source = { registry = "https://pypi.org/simple" } 325 | dependencies = [ 326 | { name = "anyio" }, 327 | ] 328 | sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846 } 329 | wheels = [ 330 | { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 }, 331 | ] 332 | 333 | [[package]] 334 | name = "typing-extensions" 335 | version = "4.13.2" 336 | source = { registry = "https://pypi.org/simple" } 337 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } 338 | wheels = [ 339 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, 340 | ] 341 | 342 | [[package]] 343 | name = "typing-inspection" 344 | version = "0.4.0" 345 | source = { registry = "https://pypi.org/simple" } 346 | dependencies = [ 347 | { name = "typing-extensions" }, 348 | ] 349 | sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } 350 | wheels = [ 351 | { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, 352 | ] 353 | 354 | [[package]] 355 | name = "uvicorn" 356 | version = "0.34.1" 357 | source = { registry = "https://pypi.org/simple" } 358 | dependencies = [ 359 | { name = "click" }, 360 | { name = "h11" }, 361 | ] 362 | sdist = { url = "https://files.pythonhosted.org/packages/86/37/dd92f1f9cedb5eaf74d9999044306e06abe65344ff197864175dbbd91871/uvicorn-0.34.1.tar.gz", hash = "sha256:af981725fc4b7ffc5cb3b0e9eda6258a90c4b52cb2a83ce567ae0a7ae1757afc", size = 76755 } 363 | wheels = [ 364 | { url = "https://files.pythonhosted.org/packages/5f/38/a5801450940a858c102a7ad9e6150146a25406a119851c993148d56ab041/uvicorn-0.34.1-py3-none-any.whl", hash = "sha256:984c3a8c7ca18ebaad15995ee7401179212c59521e67bfc390c07fa2b8d2e065", size = 62404 }, 365 | ] 366 | --------------------------------------------------------------------------------