
Prowler MCP Testing
FreeStreamlined testing for Prowler MCP Server components.
Free · Opens the source repo
What Prowler MCP Testing does
Prowler MCP Testing provides a structured approach to testing components within the Prowler MCP Server environment. This skill is designed specifically for developers working with the Prowler API, enabling them to create robust tests for tools, models, and API clients. It leverages in-memory FastMCP clients and includes a suite of fixtures to facilitate accurate and efficient testing without the need for real network connections.
The skill emphasizes best practices for writing tests, such as using the mock_api_client fixture to avoid direct instantiation of the ProwlerAPIClient. It also establishes critical rules for managing tool parameters and environment variables, ensuring that tests are deterministic and do not inadvertently affect each other. By adhering to these guidelines, developers can avoid common pitfalls that lead to hard-to-diagnose issues, particularly with asynchronous operations.
Included in the skill are example test files that demonstrate how to implement tool tests, model tests, and contract tests. These examples serve as a practical reference for developers, showcasing how to register routes, assert on structured output, and manage dependencies effectively. The skill also outlines the necessary directory structure for organizing tests, ensuring that they remain modular and maintainable.
Overall, Prowler MCP Testing is an essential tool for any developer working with the Prowler MCP Server, providing them with the resources needed to ensure their code is reliable and well-tested.
When to use it
Use this skill when developing or testing components of the Prowler MCP Server, particularly when you need to ensure the reliability of your API interactions.
When not to use it
This skill is not suitable for testing components outside the Prowler MCP ecosystem or for scenarios requiring real network interactions.
What you can build with it
Testing API Clients
Utilize the mock API client fixture to test interactions with the Prowler API without making real network calls.
Contract Testing
Ensure the API contract remains consistent by writing contract tests that validate the expected behavior of your tools.
Model Validation
Test your data models using the provided helpers to assert that they correctly handle API responses.
How to install Prowler MCP Testing
View source1. Install with the skills CLI
npx skills add prowler-cloud/prowler/prowler-test-mcp --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by prowler-cloudCritical Rules
- ALWAYS drive tools through an in-memory client:
async with Client(mcp_root_server). Tool parameters use pydanticField(default=...), and only FastMCP's wrapper resolves those defaults. Calling a tool method directly with an argument omitted leaves it as a rawFieldInfo— which is truthy, soif email:silently builds a filter out of theFieldInforepr. Direct calls MUST pass every argument. - NEVER open a
fastmcp.Clientinside a fixture. FastMCP warns this causes hard-to-diagnose event-loop issues; open it inline in the test. - ALWAYS use the
mock_api_clientfixture; NEVER construct aProwlerAPIClient. Tool instances captured the singleton by reference at import time, so only an in-place patch of.clientreaches them. - NEVER clear
SingletonMeta._instances. It orphans every registered tool on an instance holding a realhttpx.AsyncClient. Useisolated_api_clientif you genuinely need a fresh instance. - NEVER strip
PROWLER_API_KEY. Tools are built at import time and a construction failure is swallowed, so the wholeprowler_*namespace silently drops to zero tools. It is pinned in[tool.pytest_env]. - For
ProwlerAppAuth, passmode=/base_url=explicitly. Those are resolved in default arguments, evaluated once at module import, somonkeypatch.setenvhas no effect on them. - NEVER assert an exact tool count — every future branch would have to bump it.
- Assert on
result.data(structured output), notresult.content[0].text. - Tests are
test_*.py(prefix), like the API — not the SDK's*_test.pysuffix. __init__.pyIS required in everytests/subdirectory here (unlike the SDK's repo-roottests/), or same-named modules collide under pytest's import mode.- Async tests need no marker (
asyncio_mode = "auto"). Do not use@pytest.mark.anyio. - Use only obviously-fake credentials from
tests.helpers.tokens(TruffleHog). - One behaviour per test; keep tests self-contained and order-independent.
1. Layout
Mirror the source tree below the package root — drop the prowler_mcp_server/
level, exactly as the SDK maps prowler/providers/... to tests/providers/....
So prowler_mcp_server/prowler_app/tools/ is tested in tests/prowler_app/tools/.
mcp_server/tests/
├── conftest.py # all shared fixtures
├── helpers/ # jsonapi.py, http.py, assertions.py, tokens.py
├── test_server.py # mounted-server contract
├── test_health.py
├── prowler_app/{models,tools,utils}/
├── prowler_hub/
└── prowler_documentation/
2. Fixtures
| Fixture | Autouse | What it gives you |
|---|---|---|
_pinned_environment | yes | Deterministic env; blocks a developer's .env from leaking |
_no_real_network | yes | Any real socket connect raises RuntimeError |
_singleton_registry_guard | yes | Snapshots/restores SingletonMeta._instances |
mock_router | no | Route registry + request recorder |
api_client | no | The live ProwlerAPIClient singleton |
mock_api_client | no | The workhorse — singleton with a mocked transport |
isolated_api_client | no | Evicts the singleton, for construction/identity tests |
mcp_root_server | no | The mounted root server (session-scoped) |
health_client | no | Starlette TestClient for /health |
http_request_headers | no | Injects headers for HTTP-mode auth |
hub_router | no | Mocks the Hub sub-server's two sync clients |
docs_router | no | Mocks the docs search engine's two sync clients |
MockRouter
mock_router.add("GET", "/api/v1/users", json=jsonapi_collection([...]))
mock_router.add("GET", "/api/v1/tasks/t1", json=task_document("t1", "completed"))
mock_router.request_for("GET", "/api/v1/users") # last request, for header asserts
mock_router.query_params("GET", "/api/v1/users") # decoded query string
mock_router.paths() # everything requested so far
Register a route more than once to return a sequence — the last response repeats.
That is how you drive poll_task_until_complete (executing, executing,
completed). An unregistered request raises, listing what was registered.
3. Patterns
Tool test — see assets/mcp_tool_test.py. Register routes, call through the
in-memory client, assert on result.data and on the recorded request. When a
tool chooses between endpoints, assert mock_router.paths() — a wrong choice is
invisible in the response body.
Model test — see assets/mcp_model_test.py. Build the document with the
jsonapi helpers, run from_api_response(), assert on both the model and
model_dump(). MinimalSerializerMixin makes those differ, and an absent
relationship (None) must never be conflated with an empty one ([]).
Contract test — see assets/mcp_contract_test.py. Namespacing and
description coverage across every registered tool.
The worked example in the repo is findings, covered across both layers in
tests/prowler_app/{models,tools}/test_findings.py. Read those first — they
exercise every foundation capability in one feature.
Reading coverage
Coverage has a meaningless high floor. Model modules are almost entirely class-body
Field(...) declarations that execute at import, and prowler_app/server.py imports
every model module at import time. Importing the package with zero tests already
reports 36% overall, and individual model modules 54–84%.
So a model module at ~68% with no tests has none of its logic covered — the missing
ranges are the from_api_response() bodies, which is the only part worth testing.
Compare against the import-only floor, never against zero, and do not set a Codecov
target from the raw total.
Where fixture data lives
tests/helpers/ is feature-agnostic and must stay that way: it holds the JSON:API
shape, not any feature's data. Per-feature attribute dictionaries
(FINDING_ATTRIBUTES, CHECK_METADATA, …) belong as module-level constants in
the test module that uses them. Do not add feature fixtures to helpers/.
4. Commands
From mcp_server/:
cd mcp_server
uv run pytest # whole suite
uv run pytest tests/prowler_app/models # one area
uv run pytest --cov=./prowler_mcp_server # with coverage
From the repository root:
make test-mcp # runs the MCP suite exactly as CI does
5. Reference
- Fixtures and the reasoning behind them:
mcp_server/tests/conftest.py - Testing section of
docs/developer-guide/mcp-server.mdx - Official FastMCP testing guide: https://gofastmcp.com/development/tests
Frequently asked questions about Prowler MCP Testing
Similar skills
Spring Boot Testing
Master testing techniques for Spring Boot 4 applications.
GitHub Issues
Manage GitHub issues efficiently with MCP tools.
Geofeed Tuner
Optimize your IP geolocation feeds in CSV format.
Batch Files
Master Windows batch scripting for automation and task management.
Adobe Illustrator Scripting
Automate your Illustrator workflows with ExtendScript.
Plugin Structure
Create and organize Claude Code plugins effectively.
