Picus Security MCP Server
Enables interaction with Picus Security's Breach & Attack Simulation API, allowing natural language queries for simulations, threat library searches, and agent management through MCP-compatible clients.
README
<div align="center">
<img src="assets/picus-logo.png" alt="Picus" width="260" />
Picus Security MCP Server
Drive the Picus Breach & Attack Simulation platform from Claude — and any MCP client.
29 tools · 8 Picus API groups · 23 read-only + 6 write (opt-in)
</div>
A Model Context Protocol server that exposes the Picus Security Breach & Attack Simulation (BAS) REST API as tools any MCP-compatible client — Claude Code, Claude Desktop, and others — can call.
Ask your assistant to "list failed Picus simulations", "show the latest run results for the DMZ agent", or "search the threat library for ransomware threats affecting Windows" — and it drives the Picus API for you.
How it works
<div align="center"> <img src="assets/architecture.svg" width="820" alt="An MCP client calls picus-mcp over stdio; picus-mcp calls the Picus REST API over HTTPS with a Bearer token, backed by an auto-refreshing token cache." /> </div>
You provide a long-lived refresh token; the server exchanges it for a short-lived access token, caches it, and refreshes it automatically — so tool calls never deal with auth.
Features
- All eight Picus API groups — simulations, run results & analytics, threat library, threat templates, agents, integrations, and vendor mitigations.
- 23 read-only tools always available; 6 write tools (create/update/ delete/cancel/simulate-now, create template) gated behind an explicit opt-in so an assistant can't launch or destroy a simulation by accident.
- Automatic auth — you provide a long-lived refresh token; the server handles the access-token exchange, caching, and refresh (including retry on a 401).
- Portable — speaks MCP over stdio and reads all config from the environment, so it works with any MCP host and isn't tied to one machine.
- Handles the API's quirks — mixed
/v1and/v2paths, inconsistent pagination envelopes, and epoch-millisecond timestamps. Responses are returned raw so nothing is lost in translation.
Prerequisites
- Python 3.10 or newer
- A Picus Security account with REST API access
Getting a Picus API token
- Sign in to the Picus dashboard and go to Settings → REST API Token.
- Click Generate Token. Choose a name, description, expiration (up to 6 months), and the scopes you need.
- Copy the token immediately — Picus shows it only once.
This is your refresh token (PICUS_REFRESH_TOKEN). The server exchanges it for
short-lived access tokens automatically.
Install
git clone https://github.com/BeardedInfoSec/picus-security-mcp.git
cd picus-security-mcp
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
Configure
Copy the example env file and paste in your token:
cp .env.example .env
# .env
PICUS_API_BASE="https://api.picussecurity.com/v1"
PICUS_REFRESH_TOKEN="paste-your-refresh-token-here"
.env is gitignored — your token never gets committed. The server auto-loads
.env from the working directory (real environment variables take precedence, so
a host that injects config still wins).
| Variable | Required | Default | Purpose |
|---|---|---|---|
PICUS_REFRESH_TOKEN |
yes | — | Picus REST API refresh token |
PICUS_API_BASE |
no | https://api.picussecurity.com/v1 |
API base; the host origin is derived from it (both /v1 and /v2 endpoints are used) |
PICUS_ENABLE_WRITE_TOOLS |
no | false |
Register the mutating tools when true |
PICUS_REQUEST_TIMEOUT |
no | 30 |
Per-request timeout (seconds) |
PICUS_TOKEN_LEEWAY_SECONDS |
no | 120 |
Refresh the access token this many seconds before it expires |
Verify it works
A smoke test builds the server, authenticates, and makes a couple of live calls:
python scripts/smoke_test.py
Expected output ends with your real agent and simulation counts, e.g.:
auth OK — access token acquired (len=185)
GET /v1/agents ...
agents returned: 42 -> ['Browser Agent', 'DMZ-client', ...]
Use with Claude Code
Copy the example config and set the command path to your clone location, then
restart Claude Code (or run /mcp):
cp .mcp.json.example .mcp.json
# prints the exact absolute path to paste as "command" — no guessing:
echo "$(pwd)/.venv/bin/picus-mcp"
.mcp.json is gitignored (it holds a machine-specific absolute path); the tracked
.mcp.json.example is the template:
{
"mcpServers": {
"picus": {
"command": "/absolute/path/to/picus-security-mcp/.venv/bin/picus-mcp",
"env": { "PICUS_ENABLE_WRITE_TOOLS": "false" }
}
}
}
No token in this file — the server reads it from .env. Once connected, ask
Claude to "list my Picus simulations" to confirm the end-to-end path.
Use with Claude Desktop
Add to claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"picus": {
"command": "/absolute/path/to/picus-security-mcp/.venv/bin/picus-mcp",
"env": {
"PICUS_REFRESH_TOKEN": "paste-your-refresh-token-here"
}
}
}
}
Claude Desktop doesn't run from your project directory, so pass the token in the
env block (or export it in the launching environment).
Use with any other MCP client
Run the server directly; it speaks MCP over stdio:
export PICUS_REFRESH_TOKEN="…" # or rely on .env
picus-mcp
Response format
Every tool returns its result twice:
- a text block — markdown tables for list endpoints, an indented tree for everything else, with epoch-millisecond timestamps decoded inline;
structuredContent— the untouched API payload, exactly as Picus returned it.
The text is what a model or a human reads, and it is a summary: long prose is
truncated and rows are reordered (dead agents last, and so on). Anything it
leaves out is still in structuredContent, so nothing is lost. On a typical
call the text runs ~65% smaller than the raw JSON dump it replaces.
Formatting lives in src/picus_mcp/format.py. The
high-volume list endpoints have hand-written formatters; everything else uses a
generic renderer that walks arbitrary JSON without dropping fields, so an
undocumented or newly-added API shape still renders readably. A formatter that
raises falls back to that generic renderer rather than failing the call.
Enabling write tools
Write tools are off by default because they can start or delete real
simulations against your infrastructure. To enable them, set
PICUS_ENABLE_WRITE_TOOLS=true (in .mcp.json, the client env block, or your
shell). This adds:
| Tool | Endpoint |
|---|---|
picus_create_simulation |
POST /v1/simulations |
picus_update_simulation |
PUT /v1/simulations/{id} |
picus_delete_simulation |
DELETE /v1/simulations/{id} |
picus_cancel_simulation |
PUT /v1/simulations/{id}/cancel |
picus_simulate_now |
POST /v1/simulations/{id}/simulate-now |
picus_create_template |
POST /v1/templates |
Tools reference (read-only)
| Tool | Endpoint |
|---|---|
picus_list_simulations |
GET /v1/simulations |
picus_get_simulation |
GET /v1/simulations/{id} |
picus_get_run_result |
GET /v1/simulations/{id}/run/{runId} |
picus_get_latest_run_result |
GET /v1/simulations/{id}/run/latest |
picus_get_run_threats |
GET /v1/simulations/{id}/run/{runId}/threats |
picus_get_action_alerts |
GET …/run/latest/threats/{t}/actions/{a}/integrations/{i}/alerts |
picus_get_run_frameworks |
GET /v1/simulations/{id}/run/{runId}/frameworks |
picus_get_latest_run_frameworks |
GET /v1/simulations/{id}/run/latest/frameworks |
picus_search_threats |
GET /v1/threat-library/threats |
picus_get_threat |
GET /v1/threat-library/threats/{id} |
picus_get_threat_action |
GET /v1/threat-library/actions/{id} |
picus_list_threat_actions |
GET /v2/threat-library/actions |
picus_list_threat_actors |
GET /v1/threat-library/threat-actors |
picus_list_templates |
GET /v1/templates |
picus_get_template |
GET /v1/templates/{id} |
picus_list_agents |
GET /v1/agents |
picus_get_agent |
GET /v1/agents/{id} |
picus_list_integrations |
GET /v1/integrations |
picus_list_integration_agents |
GET /v1/integrations/agents |
picus_list_mitigation_devices |
GET /v2/mitigation/devices |
picus_get_mitigation_device_stats |
GET /v2/mitigation/devices/{id} |
picus_get_device_signatures |
GET /v1/mitigation/devices/{id}/signatures |
picus_list_not_blocked_actions |
GET /v1/mitigation/generic/not-blocked-actions |
Project layout
src/picus_mcp/
config.py # env-based configuration + .env auto-loading
client.py # async HTTP client: token exchange, caching, retry, v1/v2 routing
server.py # builds the FastMCP server and registers tool groups
tools/ # one module per Picus API group
scripts/
smoke_test.py # live end-to-end check
PICUS_API.md # auth flow + manual curl reference
Troubleshooting
configuration error: PICUS_REFRESH_TOKEN is not set— create.envfrom.env.exampleand paste your token, or export the variable.Failed to obtain access token … refresh token may be expired— the refresh token expired or was revoked; generate a new one in the dashboard.- Tools don't appear in Claude Code — MCP servers load at startup; restart
Claude Code or run
/mcp. Check thecommandpath in.mcp.jsonis absolute and points at your.venv. - Timestamps look wrong — most Picus fields are epoch milliseconds (a few threat-library fields are seconds). Values are returned raw; convert as needed.
Notes on the Picus API
- List endpoints paginate with
limit/offset. Most wrap results in apagesobject withtotal_count;picus_list_not_blocked_actionsusespaginationinstead, and a few list endpoints return a bare array. - The API mixes
/v1and/v2paths, which is why the client is anchored at the host origin and each tool carries its full versioned path.
See PICUS_API.md for the auth flow and a manual curl example.
License
MIT — see LICENSE.
<sub>An independent, community-built integration. Not affiliated with, endorsed by, or sponsored by Picus Security. "Picus" and the Picus logo are trademarks of Picus Security, used here only to identify the API this project targets.</sub>
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.