New to Prowler MCP Server? Start with the user documentation:
- Overview - Key capabilities, use cases, and deployment options
- Installation - Install locally or use the managed server
- Configuration - Configure Claude Desktop, Cursor, and other MCP hosts
- Tools Reference - Complete list of all available tools
Introduction
The Prowler MCP Server brings the entire Prowler ecosystem to AI assistants through the Model Context Protocol (MCP). It enables seamless integration with AI tools like Claude Desktop, Cursor, and other MCP clients. The server follows a modular architecture with three independent sub-servers:The core Prowler sub-server is served under the
prowler_ tool prefix, while its source lives in the prowler_app/ module for historical reasons. Tool names use the prefix; import paths use the module.For a complete list of tools and their descriptions, see the Tools Reference.
Architecture Overview
The MCP Server architecture is illustrated in the Overview documentation. AI assistants connect through the MCP protocol to access Prowler’s three main components.Server Structure
The main server orchestrates three sub-servers with prefixed namespacing:Tool Registration Patterns
The MCP Server uses two patterns for tool registration:- Direct Decorators (Prowler Hub/Docs): Tools are registered using
@mcp.tool()decorators - Auto-Discovery (
prowler_app): All public methods ofBaseToolsubclasses are auto-registered
Adding Tools to the prowler_app Sub-Server
Step 1: Create the Tool Class
Create a new file or add to an existing file inprowler_app/tools/:
try/except here on purpose. A failed request raises, and
Error Handling explains what turns that raise into a message
the agent can act on.
Step 2: Create the Models
Create corresponding models inprowler_app/models/:
Step 3: Verify Auto-Discovery
No manual registration is needed. Thetool_loader.py automatically discovers and registers all BaseTool subclasses. Verify your tool is loaded by checking the server logs:
Adding Tools to Prowler Hub/Docs
For Prowler Hub or Documentation tools, use the@mcp.tool() decorator directly:
Model Design Patterns
MinimalSerializerMixin
All models should useMinimalSerializerMixin to optimize responses for LLM consumption:
Nonevalues- Empty strings
- Empty lists
- Empty dictionaries
Two-Tier Model Pattern
Use two-tier models for efficient responses:- Simplified: Lightweight models for list operations
- Detailed: Extended models for single-item retrieval
Factory Method Pattern
Always implementfrom_api_response() for API transformation:
API Client Usage
TheProwlerAPIClient is a singleton that handles authentication and HTTP requests:
Helper Methods
The API client provides useful helper methods:Best Practices
Tool Docstrings
Tool docstrings become the description that is going to be read by the LLM. Provide clear usage instructions and common workflows:Error Handling
Raise, never return. A returned{"error": ...} dict is reported to the
client as isError: false — a successful tool call whose payload happens to
mention a failure. Clients and models read that as success. A raised exception
becomes a spec-correct tool execution error instead.
The common case therefore needs no handler at all:
prowler_mcp_server/lib/errors.py classifies the failures every tool shares —
a rejected credential, a missing permission, a rate limit, an outage, an
unreachable API, a bad argument — and gives each one a message that says what
went wrong and what to do about it. Anything it does not recognise is masked,
because mask_error_details=True is set on every sub-server and upstream
response bodies must never be replayed into a model’s context.
Three ways to raise, in the order to reach for them:
prowler_send_findings_to_jira is the worked example: work
items are created one at a time and Prowler cannot delete them, so a dispatch
that stopped halfway answers with a result object carrying
safe_to_retry: false. “This may have been applied” is a fact about the world,
not an error, and squashing it into one loses the only thing that stops a retry
from duplicating the write.
Parameter Descriptions
Use PydanticField() with clear descriptions. This also helps LLMs understand
the purpose of each parameter, so be as descriptive as possible:
Development Commands
- Installation Guide - Development setup instructions
- Configuration Guide - MCP client configuration
Testing
Tests live inmcp_server/tests/, mirroring the source tree, and use the test_*.py
prefix (the same convention as the API, not the SDK’s *_test.py suffix).
From mcp_server/:
asyncio_mode is set to auto.
Reading the Coverage Numbers
Shared Fixtures
All fixtures live inmcp_server/tests/conftest.py. Three are autouse and apply to
every test: the environment is pinned to deterministic values, real socket
connections are blocked, and the API client singleton registry is snapshotted and
restored.
Helpers live in
mcp_server/tests/helpers/: JSON:API document builders
(jsonapi.py), the MockRouter (http.py), tool-contract assertions
(assertions.py) and fake credentials (tokens.py).
Writing a Tool Test
Drive tools through an in-memory MCP client, and open the client inside the test — FastMCP warns that holding a client in a fixture causes event-loop problems.findings end to end — tests/prowler_app/models/test_findings.py
and tests/prowler_app/tools/test_findings.py. It is deliberately one feature
across both layers rather than a scattering of unrelated samples, and findings
is the feature that exercises the whole foundation: two-tier models, nested
sub-models, both relationship shapes, endpoint switching on a date range,
list-to-CSV filter encoding, and a tool that returns prose instead of a model.
Note the two files share a name. That is why __init__.py is required in every
tests/ subdirectory here — without it they would collide on import.
Why the API Key Is Pinned, Not Stripped
prowler_app/server.py builds every tool at import time. Constructing a tool
reaches ProwlerAppAuth, which raises when PROWLER_API_KEY is missing, and
load_all_tools swallows that error per tool class. The result is that the whole
prowler_* namespace registers zero tools while the server still logs
“Successfully mounted Prowler tools server”.
The suite therefore pins a fake key in [tool.pytest_env], which is applied before
any test module is imported, and tests/test_server.py asserts each namespace is
non-empty so this failure can never return silently.
ProwlerAppAuth resolves PROWLER_MCP_TRANSPORT_MODE and API_BASE_URL in its
default arguments, which Python evaluates once at module import. monkeypatch.setenv
cannot change them — pass mode= and base_url= explicitly in auth tests.prowler-test-mcp skill
and the official FastMCP testing guide.
Related Documentation
MCP Server Overview
Key capabilities, use cases, and deployment options
Tools Reference
Complete reference of all available tools
Prowler Hub
Security checks and compliance frameworks catalog
Lighthouse AI
AI-powered security analyst
Additional Resources
- MCP Protocol Specification - Model Context Protocol details
- Prowler API Documentation - API reference
- Prowler Hub API - Hub API reference
- GitHub Repository - Source code

