Prowler Test Mcp logo

Prowler Test Mcp

OrganizationPopular
prowler-cloud
prowler-test-mcp

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).

Overview

Publisherprowler-cloud
Repositoryprowler
Skill nameprowler-test-mcp
Stars
14.8K
Forks
2.4K
Bundled files
3
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 3 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by prowler-cloud on GitHub. Read the source before you install it.

Installation

Install the Prowler Test Mcp AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/prowler-cloud/prowler.git /tmp/prowler
mkdir -p .claude/skills
cp -r /tmp/prowler/skills/prowler-test-mcp .claude/skills/prowler-test-mcp
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Prowler Test Mcp in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Prowler Test Mcp on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Prowler Test Mcp is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Critical Rules

  • ALWAYS drive tools through an in-memory client: async with Client(mcp_root_server). Tool 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.
  • NEVER open a fastmcp.Client inside a fixture. FastMCP warns this causes hard-to-diagnose event-loop issues; open it inline in the test.
  • ALWAYS use the mock_api_client fixture; NEVER construct a ProwlerAPIClient. Tool instances captured the singleton by reference at import time, so only an in-place patch of .client reaches them.
  • NEVER clear SingletonMeta._instances. It orphans every registered tool on an instance holding a real httpx.AsyncClient. Use isolated_api_client if 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 whole prowler_* namespace silently drops to zero tools. It is pinned in [tool.pytest_env].
  • For ProwlerAppAuth, pass mode= / base_url= explicitly. Those are resolved in default arguments, evaluated once at module import, so monkeypatch.setenv has no effect on them.
  • NEVER assert an exact tool count — every future branch would have to bump it.
  • Assert on result.data (structured output), not result.content[0].text.
  • Tests are test_*.py (prefix), like the API — not the SDK's *_test.py suffix.
  • __init__.py IS required in every tests/ subdirectory here (unlike the SDK's repo-root tests/), 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/.

text
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

FixtureAutouseWhat it gives you
_pinned_environmentyesDeterministic env; blocks a developer's .env from leaking
_no_real_networkyesAny real socket connect raises RuntimeError
_singleton_registry_guardyesSnapshots/restores SingletonMeta._instances
mock_routernoRoute registry + request recorder
api_clientnoThe live ProwlerAPIClient singleton
mock_api_clientnoThe workhorse — singleton with a mocked transport
isolated_api_clientnoEvicts the singleton, for construction/identity tests
mcp_root_servernoThe mounted root server (session-scoped)
health_clientnoStarlette TestClient for /health
http_request_headersnoInjects headers for HTTP-mode auth
hub_routernoMocks the Hub sub-server's two sync clients
docs_routernoMocks the docs search engine's two sync clients

MockRouter

python
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/:

bash
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:

bash
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

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Prowler Test Mcp AI skill do?

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).

Why use Prowler Test Mcp on TypingMind?

Because you install it once and use it with any model. Prowler Test Mcp is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Prowler Test Mcp in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/prowler-cloud/prowler/tree/master/skills/prowler-test-mcp. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Prowler Test Mcp?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Prowler Test Mcp?

As many as you like. As long as a model supports skills, you can use Prowler Test Mcp with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Prowler Test Mcp AI skill free?

Yes. It is published on GitHub by prowler-cloud under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇