Skip to main content
Glama
AxiomSec

hackerone-mcp

by AxiomSec
README.md
# hackerone-mcp

A local, **read-only** MCP server that connects your HackerOne researcher account
to Claude Desktop and Claude Code. It helps you find the best targets, analyze
program scopes, review your reports/earnings, and draft bug reports (which you
submit yourself — the server never writes to your account).

## How it works

Claude Desktop or Claude Code spawns this server as a local subprocess and talks
to it over stdio using the Model Context Protocol. Every tool is read-only. The
server reaches HackerOne over **two separate paths**: your private account data
through the **authenticated official API**, and public disclosed-report data
through an **unauthenticated public endpoint** (so your account is never involved
in those requests).

```
   ┌──────────────────┐        ┌──────────────────┐
   │  Claude Desktop  │        │   Claude Code    │
   └────────┬─────────┘        └────────┬─────────┘
            └───────────┬───────────────┘
                        │  MCP over stdio
                        │  (launches: python -m hackerone_mcp)
                        ▼
┌─────────────────────────────────────────────────────────────────────  ┐
│                     hackerone-mcp  (local server)                     │
│                                                                       │
│  server.py ── FastMCP tools                                           │
│     │           programs · scopes · count/filter · rank · weaknesses  │
│     │           my_reports · earnings · draft_report · raw_get        │
│     │           search_disclosed_reports · list_cwe_types · directory │
│     │                                                                 │
│     ├── config.py ◄──────── .env  (H1_ENV_FILE: H1_USERNAME / TOKEN)  │
│     │                                                                 │
│     └── tools.py ── orchestrates each request                         │
│            │                                                          │
│            ├── cache.py ......... disk cache (1h TTL) — hit? return   │
│            │                                                          │
│            ├── analysis.py ...... rank / filter / summarize ┐         │
│            ├── hacktivity.py .... build query / project     │ pure    │
│            │                       (no I/O)                 ┘ funcs   │
│            │                                                          │
│            ├── client.py ........ httpx + HTTP Basic auth ─────────┐  │
│            │                       (GET only, 429 retry)           │  │
│            └── graphql_client.py  httpx POST, NO auth ──────────┐  │  │
│                                    (read queries only)          │  │  │
└────────────────────────────────────────────────────────────-----│--│--┘
                                                                  │  │
                    authenticated, read-only GET (HTTPS)  ────────┼──┘
                    your account data                             │
                                                                  ▼
                                          ┌──────────────────────────────┐
                                          │     api.hackerone.com/v1     │
                                          │     /hackers/programs …      │
                                          └──────────────────────────────┘
                    unauthenticated, read-only POST (HTTPS)
                    public disclosed reports                     │
                                                                 ▼
                                          ┌──────────────────────────────┐
                                          │    hackerone.com/graphql     │
                                          │    (public hacktivity)       │
                                          └──────────────────────────────┘
```

**Two flows, one pattern:** Claude calls a tool → `tools.py` checks `cache.py` →
on a miss it hits the right backend → a pure function (`analysis.py` /
`hacktivity.py`) shapes the result → it goes back up to Claude.

- **Account path** (your programs, scopes, reports, earnings): `client.py` makes
  an authenticated read-only **GET** to `api.hackerone.com/v1`. Your token never
  leaves your machine except as the Basic-auth header.
- **Public path** (disclosed-report / CWE / directory search): `graphql_client.py`
  makes an **unauthenticated** read-only **POST** to `hackerone.com/graphql` — no
  token or cookie is ever attached, so these requests carry no account risk.

## Requirements
- Python 3.10+ (tested on 3.13)
- A HackerOne API token: hackerone.com → Settings → **API Token**. Use the
  narrowest read scope available.

## Install
```bash
git clone <this repo> hackerone-mcp
cd hackerone-mcp
python -m pip install -e .
```

## Configure credentials
Set two environment variables (these go in your MCP client config below):
- `H1_USERNAME` — your HackerOne username
- `H1_API_TOKEN` — the API token you generated

Optional: `H1_CACHE_DIR`, `H1_CACHE_TTL` (seconds, default 3600).

Verify your token works:
```bash
H1_USERNAME=you H1_API_TOKEN=xxxx python -m hackerone_mcp --check-auth
```
On Windows PowerShell:
```powershell
$env:H1_USERNAME="you"; $env:H1_API_TOKEN="xxxx"; python -m hackerone_mcp --check-auth
```
Expected: `OK: authenticated as you. N program(s) accessible.`

## Connect to Claude Desktop
Edit `claude_desktop_config.json`
(Windows: `%APPDATA%\Claude\claude_desktop_config.json`):
```json
{
  "mcpServers": {
    "hackerone": {
      "command": "python",
      "args": ["-m", "hackerone_mcp"],
      "env": {
        "H1_USERNAME": "your_username",
        "H1_API_TOKEN": "your_api_token"
      }
    }
  }
}
```
Restart Claude Desktop.

## Connect to Claude Code
```bash
claude mcp add hackerone --env H1_USERNAME=your_username --env H1_API_TOKEN=your_api_token -- python -m hackerone_mcp
```
Or add an `.mcp.json` entry with the same `command`/`args`/`env`.

## Tools
- `list_programs` — programs your account can access (`compact=true` for a small
  handle/name/bounty/state listing; the full list is several MB)
- `count_programs` — totals only: program count, bounty programs, VDPs, and a
  submission-state breakdown (use this for "how many ..." questions)
- `get_program` — full policy/details for a handle
- `get_program_scopes` — structured scopes, summarized in/out of scope
- `get_program_weaknesses` — the CWE/weakness types a program tracks
- `filter_programs` — compact list filtered by offers_bounties / submission_state / bookmarked (e.g. all VDPs)
- `list_my_reports` — your own submitted reports (compact; use `get_report` for full detail)
- `search_scopes` — search assets across your programs (`limit` for a quick scan)
- `rank_programs` — rank programs for hunting (bounties, scope, severity; `limit` for a quick scan)
- `get_balance`, `list_earnings` — your payments
- `get_report` — read a report by id
- `draft_report` — format a report in markdown (you submit it yourself)
- `raw_get` — authenticated read-only GET against any v1 API path
- `search_disclosed_reports` — search PUBLIC disclosed reports by keyword / severity / CWE / CVE / program; `sort` ("relevance"/"recent", default relevance for keyword searches) and `since`/`until` (YYYY-MM-DD) date bounds
- `list_cwe_types` — valid CWE names for the `cwe` filter
- `search_directory` — search the public program directory by name

> **Note:** `search_scopes` and `rank_programs` scan every program you can access
> (one API call per program on a cold cache — potentially hundreds). Results are
> cached for an hour, so the first call is slow and later calls are fast. Pass
> `limit` (e.g. 50) for a quick partial scan, or `refresh=true` to bypass the cache.

## Security
Your API token is stored in plaintext in the client config file. Protect that
file (restrict permissions), use a narrow-scope token, and revoke/rotate it from
HackerOne if it is ever exposed. This server makes **only read-only requests**
(authenticated GETs to the official API, plus unauthenticated read-only GraphQL
POSTs to the public hacktivity endpoint) — it cannot submit, edit, or delete
anything on your account.

## Public hacktivity search (Part 2)

`search_disclosed_reports`, `list_cwe_types`, and `search_directory` read
HackerOne's **public** disclosed-report data through its undocumented GraphQL
endpoint, **unauthenticated** — no token or cookie is sent, so these calls carry
no risk to your account. They are read-only (the server never sends mutations).

Because the endpoint is undocumented, the baked-in queries can break if HackerOne
changes its schema. The exact queries were captured on 2026-06-28 and saved to
`docs/superpowers/reference/hackerone-graphql-captures-2026-06-28.json`; re-capture
from a browser and update `graphql_client.py` if a hacktivity tool stops working.

## Development
```bash
python -m pip install -e ".[dev]"
python -m pytest -v
```

TDQS

A3.5/5.0

Scored across 17 tools

Disambiguation4/5

Most tools have distinct purposes, with clear boundaries between listing programs, filtering, searching, and getting details. However, `list_programs`, `filter_programs`, and `search_directory` could cause minor confusion, and `raw_get` overlaps with any read operation.

Naming Consistency5/5

All tools follow a consistent `verb_noun` snake_case pattern (e.g., `count_programs`, `get_program`, `search_disclosed_reports`). Even `raw_get` fits the pattern as an adjective-verb combination.

Tool Count5/5

17 tools cover the HackerOne domain well—program management, report viewing, earnings, and search—without being excessive. Each tool serves a clear purpose within the read-only scope.

Completeness2/5

The tool surface is read-only, lacking any write operations (e.g., submitting reports, updating programs, or managing account settings). `draft_report` prepares a report for manual submission, but there is no tool to actually submit it, creating a dead end.

Maintenance

ActivityInactive
ResponsivenessNo issues