From cb8bceb16a7e9fd9d04de0b9f7158e525cdfdddd Mon Sep 17 00:00:00 2001 From: JackSpiece <285515994+JackSpiece@users.noreply.github.com> Date: Wed, 10 Jun 2026 21:30:07 +0000 Subject: [PATCH] chore: delete MCP server and client (#308) --- Readme.md | 81 ------------ agentic_security/mcp/__init__.py | 0 agentic_security/mcp/client.py | 54 -------- agentic_security/mcp/main.py | 167 ------------------------ docs/mcp_client_usage.md | 65 ---------- examples/mcp_client_usage.py | 104 --------------- mkdocs.yml | 1 - poetry.lock | 210 ++----------------------------- pyproject.toml | 1 - tests/unit/test_mcp.py | 20 --- 10 files changed, 11 insertions(+), 692 deletions(-) delete mode 100644 agentic_security/mcp/__init__.py delete mode 100644 agentic_security/mcp/client.py delete mode 100644 agentic_security/mcp/main.py delete mode 100644 docs/mcp_client_usage.md delete mode 100755 examples/mcp_client_usage.py delete mode 100644 tests/unit/test_mcp.py diff --git a/Readme.md b/Readme.md index 8b68aa5..7b45d4f 100644 --- a/Readme.md +++ b/Readme.md @@ -68,25 +68,6 @@ agentic_security --port=PORT --host=HOST booking-screen -## MCP client example - -Agentic Security includes an MCP stdio server in `agentic_security.mcp.main`. -To list the available MCP tools from a local checkout: - -```shell -python examples/mcp_client_usage.py -``` - -To call HTTP-backed tools, run the Agentic Security app first, then point the -MCP server at it: - -```shell -agentic_security --host 127.0.0.1 --port 8718 -python examples/mcp_client_usage.py --agentic-security-url http://127.0.0.1:8718 --call get_spec_templates -``` - -See `docs/mcp_client_usage.md` for the full walkthrough. - ## LLM kwargs Agentic Security uses plain text HTTP spec like: @@ -405,68 +386,6 @@ This setup ensures a continuous integration approach towards maintaining securit The `Module` class is designed to manage prompt processing and interaction with external AI models and tools. It supports fetching, processing, and posting prompts asynchronously for model vulnerabilities. Check out [module.md](https://github.com/msoedov/agentic_security/blob/main/docs/module.md) for details. -## MCP server - -The Agentic Security MCP server exposes the scanner's REST API as callable tools and reusable prompt templates, so any MCP-compatible client (Claude Desktop, Claude Code, custom agents) can drive security scans through natural language. - -### Installation - -```shell -pip install -U mcp - -# From cloned directory -mcp install agentic_security/mcp/main.py -``` - -### Using with Claude Desktop - -1. Start the Agentic Security FastAPI server (default port `8718`): - - ```shell - poetry run agentic_security - ``` - -2. Install the MCP server into Claude Desktop: - - ```shell - mcp install agentic_security/mcp/main.py --name "Agentic Security" - ``` - -3. Open Claude Desktop — the following **tools** are now available: - - | Tool | Description | - |---|---| - | `start_scan` | Launch a security scan against an LLM spec | - | `stop_scan` | Halt an in-progress scan | - | `verify_llm` | Check that an LLM spec is reachable | - | `get_data_config` | Retrieve the current dataset configuration | - | `get_spec_templates` | List available LLM spec templates | - -4. Or kick off a scan using one of the built-in **prompt templates**: - - - **`security_scan_prompt`** — runs a full scan with a configurable probe budget - - **`verify_llm_prompt`** — confirms a spec is reachable before committing to a scan - - **`adversarial_probe_prompt`** — enables multi-step attacks and asks Claude to summarise the worst findings - -### Example conversation with Claude - -``` -You: Use the security_scan_prompt for spec "openai/gpt-4o" with a budget of 500 probes. - -Claude: I'll kick off the scan now. Starting with verify_llm to confirm the spec is - reachable, then launching start_scan with maxBudget=500... -``` - -### Using with Claude Code (CLI) - -```shell -# Add to your project's MCP config -claude mcp add agentic-security -- python agentic_security/mcp/main.py - -# Then interact inline -claude "Run a quick adversarial probe against my local LLM at http://localhost:8080/v1" -``` - ## Documentation For more detailed information on how to use Agentic Security, including advanced features and customization options, please refer to the official documentation. diff --git a/agentic_security/mcp/__init__.py b/agentic_security/mcp/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/agentic_security/mcp/client.py b/agentic_security/mcp/client.py deleted file mode 100644 index b3d9a05..0000000 --- a/agentic_security/mcp/client.py +++ /dev/null @@ -1,54 +0,0 @@ -import asyncio -import sys - -from mcp import ClientSession, StdioServerParameters -from mcp.client.stdio import stdio_client - -from agentic_security.logutils import logger - - -def build_server_params() -> StdioServerParameters: - """Create server parameters for a stdio MCP client session.""" - return StdioServerParameters( - command=sys.executable, - args=["-m", "agentic_security.mcp.main"], - env=None, - ) - - -async def run() -> None: - try: - server_params = build_server_params() - logger.info( - "Starting stdio client session with server parameters: %s", server_params - ) - async with stdio_client(server_params) as (read, write): - async with ClientSession(read, write) as session: - logger.info("Initializing client session...") - await session.initialize() - - logger.info("Listing available prompts...") - prompts = await session.list_prompts() - logger.info(f"Available prompts: {prompts}") - - logger.info("Listing available resources...") - resources = await session.list_resources() - logger.info(f"Available resources: {resources}") - - logger.info("Listing available tools...") - tools = await session.list_tools() - logger.info(f"Available tools: {tools}") - logger.info( - "Available MCP tool names: %s", - ", ".join(tool.name for tool in tools.tools), - ) - - logger.info("Client operations completed successfully.") - return prompts, resources, tools - except Exception as e: - logger.error(f"An error occurred during client operations: {e}", exc_info=True) - raise - - -if __name__ == "__main__": - asyncio.run(run()) diff --git a/agentic_security/mcp/main.py b/agentic_security/mcp/main.py deleted file mode 100644 index 7c3da84..0000000 --- a/agentic_security/mcp/main.py +++ /dev/null @@ -1,167 +0,0 @@ -import os - -import httpx -from mcp.server.fastmcp import FastMCP - -# Initialize MCP server -mcp = FastMCP( - name="Agentic Security MCP Server", - dependencies=["httpx"], -) - -# FastAPI Server Configuration -AGENTIC_SECURITY = os.getenv("AGENTIC_SECURITY_URL", "http://0.0.0.0:8718") - - -# --------------------------------------------------------------------------- -# Prompt templates -# --------------------------------------------------------------------------- - - -@mcp.prompt() -def security_scan_prompt(llm_spec: str, max_budget: int = 1000) -> str: - """Generate a prompt to kick off a full LLM security scan. - - Args: - llm_spec: The LLM specification string identifying the model endpoint. - max_budget: Maximum number of probes to run (defaults to 1000). - """ - return ( - f"Please run a security scan on the following LLM specification:\n\n" - f" Spec: {llm_spec}\n" - f" Max budget: {max_budget} probes\n\n" - f"Use the start_scan tool to initiate the scan, then monitor progress " - f"with get_data_config, and stop it with stop_scan when complete." - ) - - -@mcp.prompt() -def verify_llm_prompt(llm_spec: str) -> str: - """Generate a prompt to verify that an LLM spec is reachable and well-formed. - - Args: - llm_spec: The LLM specification string to verify. - """ - return ( - f"Verify the following LLM specification is valid and reachable:\n\n" - f" Spec: {llm_spec}\n\n" - f"Use the verify_llm tool and report back whether the spec is accepted " - f"by the Agentic Security server." - ) - - -@mcp.prompt() -def adversarial_probe_prompt(llm_spec: str) -> str: - """Generate a prompt for an adversarial probing session with multi-step attacks. - - Args: - llm_spec: The LLM specification string identifying the target model. - """ - return ( - f"Run an adversarial probing session against the LLM described by:\n\n" - f" Spec: {llm_spec}\n\n" - f"Enable multi-step attacks and optimization in the start_scan call. " - f"After the scan finishes, summarise the most critical vulnerabilities found." - ) - - -# --------------------------------------------------------------------------- -# Tools -# --------------------------------------------------------------------------- - - -@mcp.tool() -async def verify_llm(spec: str) -> dict: - """ - Verify an LLM model specification using the FastAPI server - - Returns: - dict: containing the verification result form the FastAPI server - - Args: spect(str): The specification of the LLM model to verify. - - """ - url = f"{AGENTIC_SECURITY}/verify" - async with httpx.AsyncClient() as client: - response = await client.post(url, json={"spec": spec}) - return response.json() - - -@mcp.tool() -async def start_scan( - llmSpec: str, - maxBudget: int, - optimize: bool = False, - enableMultiStepAttack: bool = False, -) -> dict: - """ - Start an LLM security scan via the FastAPI server. - Returns: - dict: The scan initiation result from the FastAPI server. - - Args: - llmSpec (str): The specification of the LLM model. - maxBudget (int): The maximum budget for the scan. - optimize (bool, optional): Whether to enable optimization during scanning. Defaults to False. - enableMultiStepAttack (bool, optional): Whether to enable multi-step attack - - """ - url = f"{AGENTIC_SECURITY}/scan" - payload = { - "llmSpec": llmSpec, - "maxBudget": maxBudget, - "datasets": [], - "optimize": optimize, - "enableMultiStepAttack": enableMultiStepAttack, - "probe_datasets": [], - "secrets": {}, - } - async with httpx.AsyncClient() as client: - response = await client.post(url, json=payload) - return response.json() - - -@mcp.tool() -async def stop_scan() -> dict: - """Stop an ongoing scan via the FastAPI server. - - Returns: - dict: The confirmation from the FastAPI server that the scan has been stopped. - """ - url = f"{AGENTIC_SECURITY}/stop" - async with httpx.AsyncClient() as client: - response = await client.post(url) - return response.json() - - -@mcp.tool() -async def get_data_config() -> list: - """ - Retrieve data configuration from the FastAPI server. - - Returns: - list: The response from the FastAPI server, confirming the scan has been stopped. - """ - url = f"{AGENTIC_SECURITY}/v1/data-config" - async with httpx.AsyncClient() as client: - response = await client.get(url) - return response.json() - - -@mcp.tool() -async def get_spec_templates() -> list: - """ - Retrieve data configuration from the FastAPI server. - - Returns: - list: The LLM specification templates from the FastAPI server. - """ - url = f"{AGENTIC_SECURITY}/v1/llm-specs" - async with httpx.AsyncClient() as client: - response = await client.get(url) - return response.json() - - -# Run the MCP server -if __name__ == "__main__": - mcp.run() diff --git a/docs/mcp_client_usage.md b/docs/mcp_client_usage.md deleted file mode 100644 index 55109d7..0000000 --- a/docs/mcp_client_usage.md +++ /dev/null @@ -1,65 +0,0 @@ -# MCP client usage - -Agentic Security exposes an MCP stdio server in `agentic_security.mcp.main`. -The example client in `examples/mcp_client_usage.py` shows how to connect to -that server, list available tools, and optionally call simple no-argument tools. - -## List MCP tools - -From the repository root: - -```shell -python examples/mcp_client_usage.py -``` - -This starts the MCP server as a subprocess with: - -```shell -python -m agentic_security.mcp.main -``` - -The client initializes an MCP session and prints the available Agentic Security -tools, including `verify_llm`, `start_scan`, `stop_scan`, `get_data_config`, and -`get_spec_templates`. - -## Call an HTTP-backed tool - -Some MCP tools call the Agentic Security HTTP app. Start the app in another -terminal first: - -```shell -agentic_security --host 127.0.0.1 --port 8718 -``` - -Then point the MCP server at that app and call a no-argument tool: - -```shell -python examples/mcp_client_usage.py \ - --agentic-security-url http://127.0.0.1:8718 \ - --call get_spec_templates -``` - -You can also set `AGENTIC_SECURITY_URL` directly: - -```shell -AGENTIC_SECURITY_URL=http://127.0.0.1:8718 python examples/mcp_client_usage.py --call get_data_config -``` - -## Use the package helper - -For tests or quick local checks, `agentic_security.mcp.client.run()` creates the -same stdio session and returns the prompt, resource, and tool list results: - -```python -import asyncio - -from agentic_security.mcp.client import run - - -async def main() -> None: - _prompts, _resources, tools = await run() - print([tool.name for tool in tools.tools]) - - -asyncio.run(main()) -``` diff --git a/examples/mcp_client_usage.py b/examples/mcp_client_usage.py deleted file mode 100755 index c89ca18..0000000 --- a/examples/mcp_client_usage.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python3 -"""Example MCP client for the Agentic Security stdio server. - -The default command lists the tools exposed by ``agentic_security.mcp.main``. -If the Agentic Security HTTP app is running, pass ``--call`` to invoke one of -the no-argument HTTP-backed tools through MCP. -""" - -from __future__ import annotations - -import argparse -import asyncio -import json -import os -import sys -from typing import Any - -from mcp import ClientSession, StdioServerParameters -from mcp.client.stdio import stdio_client - -NO_ARGUMENT_TOOLS = {"get_data_config", "get_spec_templates", "stop_scan"} - - -def _build_server_params(agentic_security_url: str | None) -> StdioServerParameters: - env = os.environ.copy() - if agentic_security_url: - env["AGENTIC_SECURITY_URL"] = agentic_security_url - - return StdioServerParameters( - command=sys.executable, - args=["-m", "agentic_security.mcp.main"], - env=env, - ) - - -def _jsonable(value: Any) -> Any: - if hasattr(value, "model_dump"): - return value.model_dump(mode="json") - if isinstance(value, (list, tuple)): - return [_jsonable(item) for item in value] - if isinstance(value, dict): - return {key: _jsonable(item) for key, item in value.items()} - return value - - -async def run_client(agentic_security_url: str | None, call_tool: str | None) -> None: - server_params = _build_server_params(agentic_security_url) - - async with stdio_client(server_params) as (read, write): - async with ClientSession(read, write) as session: - await session.initialize() - tools = await session.list_tools() - tool_names = [tool.name for tool in tools.tools] - - print("Available Agentic Security MCP tools:") - for tool in tools.tools: - description_lines = (tool.description or "").strip().splitlines() - description = ( - description_lines[0] if description_lines else "No description" - ) - print(f"- {tool.name}: {description}") - - if not call_tool: - return - - if call_tool not in tool_names: - raise ValueError( - f"Unknown tool {call_tool!r}. Available tools: {', '.join(tool_names)}" - ) - if call_tool not in NO_ARGUMENT_TOOLS: - raise ValueError( - f"{call_tool!r} requires arguments. This example only calls " - f"no-argument tools: {', '.join(sorted(NO_ARGUMENT_TOOLS))}" - ) - - result = await session.call_tool(call_tool, arguments={}) - print() - print(f"{call_tool} result:") - print(json.dumps(_jsonable(result), indent=2)) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="List Agentic Security MCP tools and optionally call one.", - ) - parser.add_argument( - "--agentic-security-url", - default=None, - help=( - "Agentic Security HTTP app URL. Defaults to AGENTIC_SECURITY_URL " - "or http://0.0.0.0:8718 in the server." - ), - ) - parser.add_argument( - "--call", - choices=sorted(NO_ARGUMENT_TOOLS), - help="Optional no-argument MCP tool to call after listing tools.", - ) - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - asyncio.run(run_client(args.agentic_security_url, args.call)) diff --git a/mkdocs.yml b/mkdocs.yml index 2317754..9e7ac41 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,7 +26,6 @@ nav: - Dataset Extension: datasets.md - External Modules: external_module.md - CI/CD Integration: ci_cd.md - - MCP Client Usage: mcp_client_usage.md - Bayesian Optimization: optimizer.md - Image Generation: image_generation.md - Stenography Functions: stenography.md diff --git a/poetry.lock b/poetry.lock index ca73b4e..b74e7f8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -451,7 +451,8 @@ version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] +markers = "implementation_name == \"pypy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -538,7 +539,6 @@ files = [ {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] -markers = {main = "platform_python_implementation != \"PyPy\"", dev = "implementation_name == \"pypy\""} [package.dependencies] pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} @@ -778,71 +778,6 @@ mypy = ["bokeh", "contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.17.0)", " test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] -[[package]] -name = "cryptography" -version = "48.0.0" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.9" -groups = ["main"] -files = [ - {file = "cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c"}, - {file = "cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5"}, - {file = "cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321"}, - {file = "cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74"}, - {file = "cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4"}, - {file = "cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7"}, - {file = "cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336"}, - {file = "cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057"}, - {file = "cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae"}, - {file = "cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c"}, - {file = "cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f"}, - {file = "cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12"}, - {file = "cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a"}, - {file = "cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239"}, - {file = "cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c"}, - {file = "cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4"}, - {file = "cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd"}, - {file = "cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355"}, - {file = "cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a"}, - {file = "cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920"}, -] - -[package.dependencies] -cffi = {version = ">=2.0.0", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -ssh = ["bcrypt (>=3.1.5)"] - [[package]] name = "cycler" version = "0.12.1" @@ -1481,18 +1416,6 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "httpx-sse" -version = "0.4.1" -description = "Consume Server-Sent Event (SSE) messages with HTTPX." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "httpx_sse-0.4.1-py3-none-any.whl", hash = "sha256:cba42174344c3a5b06f255ce65b350880f962d99ead85e776f23c6618a377a37"}, - {file = "httpx_sse-0.4.1.tar.gz", hash = "sha256:8f44d34414bc7b21bf3602713005c5df4917884f76072479b21f68befa4ea26e"}, -] - [[package]] name = "huggingface-hub" version = "1.1.6" @@ -1849,7 +1772,7 @@ version = "4.25.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "jsonschema-4.25.0-py3-none-any.whl", hash = "sha256:24c2e8da302de79c8b9382fee3e76b355e44d2a4364bb207159ce10b517bd716"}, {file = "jsonschema-4.25.0.tar.gz", hash = "sha256:e63acf5c11762c0e6672ffb61482bdf57f0876684d8d249c0fe2d730d48bc55f"}, @@ -1871,7 +1794,7 @@ version = "2025.4.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -2386,39 +2309,6 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mcp" -version = "1.27.2" -description = "Model Context Protocol SDK" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "mcp-1.27.2-py3-none-any.whl", hash = "sha256:d6ff5160c6ca65d93013626efb3fc249de683c30b2d8570755ceddd490344de5"}, - {file = "mcp-1.27.2.tar.gz", hash = "sha256:8e02db104096d1c25b28e64bde29a5c32b31bc241710213e12fd4d84985bdfef"}, -] - -[package.dependencies] -anyio = ">=4.5" -httpx = ">=0.27.1,<1.0.0" -httpx-sse = ">=0.4" -jsonschema = ">=4.20.0" -pydantic = ">=2.11.0,<3.0.0" -pydantic-settings = ">=2.5.2" -pyjwt = {version = ">=2.10.1", extras = ["crypto"]} -python-multipart = ">=0.0.9" -pywin32 = {version = ">=310", markers = "sys_platform == \"win32\""} -sse-starlette = ">=1.6.1" -starlette = ">=0.27" -typing-extensions = ">=4.9.0" -typing-inspection = ">=0.4.1" -uvicorn = {version = ">=0.31.1", markers = "sys_platform != \"emscripten\""} - -[package.extras] -cli = ["python-dotenv (>=1.0.0)", "typer (>=0.16.0)"] -rich = ["rich (>=13.9.4)"] -ws = ["websockets (>=15.0.1)"] - [[package]] name = "mdit-py-plugins" version = "0.5.0" @@ -3735,12 +3625,12 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] +markers = "implementation_name == \"pypy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -markers = {main = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"", dev = "implementation_name == \"pypy\""} [[package]] name = "pydantic" @@ -3898,30 +3788,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.14.1" -[[package]] -name = "pydantic-settings" -version = "2.10.1" -description = "Settings management using Pydantic" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796"}, - {file = "pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee"}, -] - -[package.dependencies] -pydantic = ">=2.7.0" -python-dotenv = ">=0.21.0" -typing-inspection = ">=0.4.0" - -[package.extras] -aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] -azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] -gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] -toml = ["tomli (>=2.0.1)"] -yaml = ["pyyaml (>=6.0.1)"] - [[package]] name = "pyfiglet" version = "1.0.4" @@ -3949,24 +3815,6 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] -[[package]] -name = "pyjwt" -version = "2.13.0" -description = "JSON Web Token implementation in Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728"}, - {file = "pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423"}, -] - -[package.dependencies] -cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] - [[package]] name = "pymdown-extensions" version = "10.21.3" @@ -4117,21 +3965,6 @@ platformdirs = ">=4.3.6,<5" docs = ["furo (>=2025.12.19)", "sphinx (>=9.1)", "sphinx-autodoc-typehints (>=3.6.3)", "sphinxcontrib-mermaid (>=2)", "sphinxcontrib-towncrier (>=0.4)", "towncrier (>=25.8)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.5.4)", "pytest (>=8.3.5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] -[[package]] -name = "python-dotenv" -version = "1.2.2" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"}, - {file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - [[package]] name = "python-multipart" version = "0.0.27" @@ -4217,7 +4050,8 @@ version = "311" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["main", "dev"] +groups = ["dev"] +markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"" files = [ {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, @@ -4240,7 +4074,6 @@ files = [ {file = "pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91"}, {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, ] -markers = {main = "sys_platform == \"win32\"", dev = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} [[package]] name = "pyyaml" @@ -4431,7 +4264,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -4489,7 +4322,7 @@ version = "0.27.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "rpds_py-0.27.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:130c1ffa5039a333f5926b09e346ab335f0d4ec393b030a18549a7c7e7c2cea4"}, {file = "rpds_py-0.27.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4cf32a26fa744101b67bfd28c55d992cd19438aff611a46cac7f066afca8fd4"}, @@ -4913,27 +4746,6 @@ files = [ {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, ] -[[package]] -name = "sse-starlette" -version = "3.0.2" -description = "SSE plugin for Starlette" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a"}, - {file = "sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a"}, -] - -[package.dependencies] -anyio = ">=4.7.0" - -[package.extras] -daphne = ["daphne (>=4.2.0)"] -examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "starlette (>=0.41.3)", "uvicorn (>=0.34.0)"] -granian = ["granian (>=2.3.1)"] -uvicorn = ["uvicorn (>=0.34.0)"] - [[package]] name = "stack-data" version = "0.6.3" @@ -5595,4 +5407,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "0361c0e4c13f56bccb90890166ad7afe1b0d679b6d9fb6cad4deeca7996a0842" +content-hash = "dc2edc6c72835e82a8954273da5ae9e1c231686ff73886a9f962410a384b3f1f" diff --git a/pyproject.toml b/pyproject.toml index 2ac9d32..2f0891b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,6 @@ sentry_sdk = "^2.46.0" orjson = "^3.11.4" pyfiglet = "^1.0.4" termcolor = "^3.2.0" -mcp = "^1.22.0" # garak = { version = "*", optional = true } pytest-xdist = "^3.8.0" anthropic = "^0.102.0" diff --git a/tests/unit/test_mcp.py b/tests/unit/test_mcp.py deleted file mode 100644 index 2d0adbb..0000000 --- a/tests/unit/test_mcp.py +++ /dev/null @@ -1,20 +0,0 @@ -import pytest - -from agentic_security.mcp.client import run - - -@pytest.mark.asyncio -async def test_mcp_client_lists_agentic_security_tools(): - """Test that the MCP client can discover the server tools.""" - prompts, resources, tools = await run() - tool_names = {tool.name for tool in tools.tools} - - assert prompts is not None - assert resources is not None - assert { - "verify_llm", - "start_scan", - "stop_scan", - "get_data_config", - "get_spec_templates", - }.issubset(tool_names)