framework-compliance-t…
Make a cloud account compliant with a security or industry framework using Prowler Cloud.
Testing patterns for the Prowler MCP Server: in-memory FastMCP clients, the ProwlerAPIClient singleton, JSON:API model builders and mocked httpx transports. Trigger: When writing tests under mcp_server/tests/ (tools, models, api_client, auth, sub-servers).
$ npx -y skills add prowler-cloud/prowler --skill prowler-test-mcp --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/prowler-test-mcpContext preview
The summary Claude sees to decide when to auto-load this skill.
Testing patterns for the Prowler MCP Server: in-memory FastMCP clients, the ProwlerAPIClient singleton, JSON:API model builders and mocked httpx transports. Trigger: When writing tests under mcp_server/tests/ (tools, models, api_client, auth, sub-servers).
name: prowler-test-mcp
description: >
Testing patterns for the Prowler MCP Server: in-memory FastMCP clients, the
ProwlerAPIClient singleton, JSON:API model builders and mocked httpx transports.
Trigger: When writing tests under mcp_server/tests/ (tools, models, api_client, auth, sub-servers).
license: Apache-2.0
metadata:
author: prowler-cloud
version: "1.0.0"
scope: [root, mcp_server]
auto_invoke:
- "Writing Prowler MCP server tests"
- "Testing MCP tools or models"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, TaskTool parameters use pydantic `Field(default=...)`, and only FastMCP's wrapper resolves those defaults. Calling a tool method directly with an argument omitted leaves it as a raw `FieldInfo` — which is truthy, so `if email:` silently builds a filter out of the `FieldInfo` repr. Direct calls MUST pass every argument.
hard-to-diagnose event-loop issues; open it inline in the test.
Tool instances captured the singleton by reference at import time, so only an in-place patch of `.client` reaches them.
instance holding a real `httpx.AsyncClient`. Use `isolated_api_client` if you genuinely need a fresh instance.
failure is swallowed, so the whole `prowler_*` namespace silently drops to zero tools. It is pinned in `[tool.pytest_env]`.
default arguments, evaluated once at module import, so `monkeypatch.setenv` has no effect on them.
repo-root `tests/`), or same-named modules collide under pytest's import mode.
---
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/---
| 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 |
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 farRegister 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.
---
**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.
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
Prowler is the world’s most widely used Open-Source Cloud Security Platform that automates security and compliance across any cloud environment.
Repo: prowler-cloud/prowler
Make a cloud account compliant with a security or industry framework using Prowler Cloud.
Vercel AI SDK 5 patterns. Trigger: When building AI features with AI SDK v5 (chat, streaming, tools/function calling, UIMessage parts), including migration…
Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API…
Reviews Django migration files for PostgreSQL best practices specific to Prowler. Trigger: When creating migrations, running makemigrations/pgmakemigrations,…
Create and maintain GitHub Agentic Workflows (gh-aw) for Prowler. Trigger: When creating agentic workflows, modifying gh-aw frontmatter, configuring…
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.