PP

pmind12/panthermcp

API development
0 stars 0 forks Quality 81 Trend 20MIT

MCP server for the Panther SIEM, written in Go and packaged for Docker (stdio + Streamable HTTP)

Overview

A Model Context Protocol server for the Panther SIEM, written in Go and shipped as a ~20 MB Docker image. Give it your Panther URL and an API token, and any MCP client (Claude Desktop, Claude Code, Cursor, …) can triage alerts, hunt in the data lake, manage detections, log sources, users and more — , with raw REST/GraphQL escape hatches for anything else. Unofficial project, not affiliated with or endorsed by Panther Labs. - selected with --transport: stdio (client spawns the container) or http (Streamable HTTP, long-running service). - (PANTHER_READ_ONLY=true) registers only tools that cannot change anything. - : update_* tools fetch the current object and merge your changes, so you never wipe fields by accident. - : read-only SQL only; queries on Panther tables must have a time filter unless you opt out; results are paged and size-capped. - Bearer-token auth for the HTTP transport, non-root distroless image, no CGO.

README

panthermcp

A Model Context Protocol server for the Panther SIEM, written in Go and shipped as a ~20 MB Docker image.

Give it your Panther URL and an API token, and any MCP client (Claude Desktop, Claude Code, Cursor, …) can triage alerts, hunt in the data lake, manage detections, log sources, users and more — 85 tools covering every Panther REST endpoint plus the GraphQL API, with raw REST/GraphQL escape hatches for anything else.

Unofficial project, not affiliated with or endorsed by Panther Labs.

  • Two transports selected with --transport: stdio (client spawns the container) or http (Streamable HTTP, long-running service).
  • Read-only mode (PANTHER_READ_ONLY=true) registers only tools that cannot change anything.
  • Safe updates: update_* tools fetch the current object and merge your changes, so you never wipe fields by accident.
  • Data lake guard rails: read-only SQL only; queries on Panther tables must have a time filter unless you opt out; results are paged and size-capped.
  • Bearer-token auth for the HTTP transport, non-root distroless image, no CGO.

Quick start

1. Create a Panther API token

In Panther go to Settings → API Tokens → Create New Token and grant the permissions you want the assistant to have (e.g. Read Alerts, Manage Alerts, Query Data Lake, View Rules, View Log Sources, Read Panther Metrics, Read User Info). The server can only do what the token allows; the get_permissions tool shows what it has.

2. Build the image

docker build -t panthermcp .

Or pull the prebuilt multi-arch image: ghcr.io/pmind12/panthermcp:latest.

3a. Use it over stdio (Claude Desktop / Claude Code)

The client starts the container itself. Claude Desktop, claude_desktop_config.json:

{
  "mcpServers": {
    "panther": {
      "command": "docker",
      "args": ["run", "-i", "--rm",
               "-e", "PANTHER_URL", "-e", "PANTHER_API_KEY",
               "ghcr.io/pmind12/panthermcp:latest", "--transport", "stdio"],
      "env": {
        "PANTHER_URL": "https://your-instance.runpanther.net",
        "PANTHER_API_KEY": "your-api-token"
      }
    }
  }
}

Claude Code:

claude mcp add panther -e PANTHER_URL=https://your-instance.runpanther.net -e PANTHER_API_KEY=your-api-token -- docker run -i --rm -e PANTHER_URL -e PANTHER_API_KEY ghcr.io/pmind12/panthermcp:latest --transport stdio

3b. Run it as an HTTP service

cp .env.example .env        # fill in PANTHER_URL, PANTHER_API_KEY and MCP_AUTH_TOKEN
docker compose up -d

or without compose:

docker run -d -p 8080:8080 -e PANTHER_URL=https://your-instance.runpanther.net -e PANTHER_API_KEY=your-api-token -e MCP_AUTH_TOKEN=a-long-random-string ghcr.io/pmind12/panthermcp:latest --transport http

The MCP endpoint is http://:8080/mcp (health check: /healthz). Clients send Authorization: Bearer . For example with Claude Code:

claude mcp add --transport http panther http://localhost:8080/mcp --header "Authorization: Bearer a-long-random-string"

Put it behind TLS (a reverse proxy) if it is reachable from other machines.

Configuration

Every flag can also be set by environment variable.

Flag Env var Default Description
--transport MCP_TRANSPORT stdio stdio or http
--panther-url PANTHER_URL — Your Panther URL (see below)
— PANTHER_API_KEY — Panther API token (required)
--read-only PANTHER_READ_ONLY false Only register non-mutating tools
--addr MCP_ADDR :8080 HTTP listen address
--path MCP_PATH /mcp HTTP endpoint path
— MCP_AUTH_TOKEN — Bearer token clients must send to the HTTP transport (required for http)
--insecure-no-auth MCP_INSECURE_NO_AUTH false Allow http without MCP_AUTH_TOKEN (only behind your own auth proxy)
— PANTHER_REST_URL derived Override the REST API base URL
— PANTHER_GRAPHQL_URL derived Override the GraphQL endpoint
--log-level LOG_LEVEL info debug, info, warn, error (logs go to stderr)

Which URL? Any of these work for PANTHER_URL:

  • the console URL, https://acme.runpanther.net — the API endpoint is read from the console’s config, falling back to https://api.acme.runpanther.net;
  • the API URL shown under Settings → API Tokens, e.g. https://api.acme.runpanther.net or .../public/graphql;
  • a Cloud Connected / self-hosted API URL ending in /v1.

If your deployment is unusual, set PANTHER_REST_URL / PANTHER_GRAPHQL_URL explicitly. The resolved endpoints are logged at startup and returned by the get_panther_info tool.

Tools

Area Tools
Alerts list_alerts, get_alert, get_alert_events, update_alert, bulk_update_alerts
Alert comments list_alert_comments, get_alert_comment, add_alert_comment, update_alert_comment
Alert context tags list_alert_context_tags, create_alert_context_tag, rename_alert_context_tag, delete_alert_context_tag
Panther AI start_ai_alert_triage, get_ai_alert_triage_summary
Detections (rules, scheduled rules, simple rules, policies, correlation rules) list_detections, get_detection, create_detection, update_detection, test_detection, set_detection_enabled, delete_detection, bulk_delete_detections
Data models list_data_models, get_data_model, create_data_model, update_data_model, delete_data_model
Global helpers list_global_helpers, get_global_helper, create_global_helper, update_global_helper, delete_global_helper
Saved / scheduled queries list_saved_queries, get_saved_query, create_saved_query, update_saved_query, delete_saved_query
Data lake execute_data_lake_query, get_data_lake_query_results, cancel_data_lake_query, list_data_lake_queries, list_databases, list_tables, get_table_schema, search_indicators
Log sources list_log_sources, get_log_source, get_log_source_config, create_log_source, update_log_source, delete_log_source
Source alarms get_log_source_alarm, set_log_source_alarm, delete_log_source_alarm
Log type schemas list_log_type_schemas, get_log_type_schemas, create_or_update_log_type_schema, set_log_type_schema_archived
Cloud accounts list_cloud_accounts, get_cloud_account, create_cloud_account, update_cloud_account, delete_cloud_account
Metrics get_metrics (alerts per severity/rule, events & bytes processed/queried/ingested, latency, totals)
Users & access list_users, get_user, create_user, update_user, delete_user, list_roles, get_role, create_role, update_role, delete_role
API tokens list_api_tokens, get_api_token, create_api_token, update_api_token, delete_api_token, rotate_api_token, get_permissions
Instance get_panther_info
Escape hatches panther_rest_request, panther_graphql_query

Every tool carries MCP annotations (readOnlyHint, destructiveHint), so clients can ask for confirmation before anything that deletes data. In read-only mode the escape hatches stay available but only allow GET requests and GraphQL queries (no mutations).

Example prompts

  • “Show me all open HIGH and CRITICAL alerts from the last 24 hours and summarise them.”
  • “Investigate alert abc123: get its events, then look for other activity from the same IP in CloudTrail over the past week.”
  • “Which log sources are unhealthy, and when did each last receive data?”
  • “Disable the rule AWS.Console.RootLogin and add a comment to its open alerts explaining why.”
  • “Search all logs for 198.51.100.7 and evil.example.com in the last 30 days.”

Security notes

  • The API token is the security boundary: give it the least privilege you need, and prefer PANTHER_READ_ONLY=true for exploratory use.
  • The HTTP transport refuses to start without MCP_AUTH_TOKEN (unless you explicitly pass --insecure-no-auth); without it anyone who can reach the port could use your Panther token.
  • The API key is never sent across a redirect to another host, and an API URL discovered from the console page is only trusted if it is HTTPS on the console’s own domain.
  • execute_data_lake_query accepts only a single read statement (SELECT, WITH, SHOW, DESCRIBE, EXPLAIN).
  • Tool output contains data from your logs and alerts. Treat it as untrusted input to the model (log lines can contain prompt-injection attempts), and keep a human in the loop for destructive actions.
  • get_permissions never returns the token value.

Development

Requires Go 1.25+ (or just Docker).

go test -race ./...
go run ./cmd/panthermcp --transport stdio

Without a local Go toolchain:

docker run --rm -v "$PWD:/src" -w /src golang:1.25 go test ./...

The tests drive the tools through a real MCP client session against a fake Panther API, and the GraphQL documents the tools send have been validated against Panther’s published schema.

Layout:

cmd/panthermcp/      entrypoint, flags, stdio / HTTP transports
internal/panther/    REST + GraphQL client, endpoint discovery
internal/tools/      MCP tool definitions, one file per area

License

MIT

View this README on GitHub

Install

docker run -i --rm -e PANTHER_URL -e PANTHER_API_KEY ghcr.io/pmind12/panthermcp:latest --transport stdio

Configuration

{ "mcpServers": { "panther": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "PANTHER_URL", "-e", "PANTHER_API_KEY", "ghcr.io/pmind12/panthermcp:latest", "--transport", "stdio"], "env": { "PANTHER_URL": "https://your-instance.runpanther.net", "PANTHER_API_KEY": "your-api-token" } } } }