intigriti-mcp
# intigriti-mcp
MCP server for the [Intigriti](https://www.intigriti.com/) bug bounty platform.
Covers the full [Intigriti Researcher API v1](https://api.intigriti.com/external/researcher/swagger/v1.0/swagger.json):
lists/filter programs, pulls scope details and historical versions, reads
rules of engagement (the program's rules text), tracks what programs are
doing via the activity feed, lists payouts, diffs scope changes over time,
and recommends the best-fit programs based on your skills profile.
Endpoint paths, response shapes (pagination envelope, enum/money view-models),
and field mappings are verified against Intigriti's published OpenAPI spec —
all six endpoints are implemented:
- `GET /v1/programs` (filters: `statusId`, `typeId`, `following`; full pagination)
- `GET /v1/programs/{id}` (scope + rules + program metadata)
- `GET /v1/programs/{id}/domains/{versionId}` (historical scope versions)
- `GET /v1/programs/{id}/rules-of-engagements/{versionId}` (historical rules versions)
- `GET /v1/programs/activities` (program update feed)
- `GET /v1/payouts` (payout overview, BETA)
## Tools
All tools output tight markdown by default (easy for agents to read, no API
plumbing or empty fields); pass `format: "json"` for structured output.
| Tool | Description |
| --- | --- |
| `list_programs(statusId?, typeId?, following?, offset?)` | All programs visible to your token, with status, bounty range, and type. Filterable + paginated. |
| `get_program_scope(programId, scopeVersionId?)` | Full asset list: endpoint, type, tier, description, **Intigriti-declared required skills**, in/out-of-scope flag, scope version timestamp. `scopeVersionId` fetches a historical version. |
| `get_program_rules(programId, rulesVersionId?)` | Rules of engagement: description text, testing requirements (Intigriti-me, automated-tooling policy, required UA/header), safe-harbour status, attachments. |
| `get_program_updates(createdSinceDays?, following?, limit?)` | Program-wide activity feed: new scope versions, new rules-of-engagement versions, status changes — with old→new version ids so you can fetch the exact new content. |
| `diff_scope(programId)` | Fetch current scope, diff against the last stored snapshot (added/removed/modified assets **plus rules-version changes**), update snapshot. First run records the baseline. |
| `list_payouts(statusId?, createdSinceDays?, paidSinceDays?)` | Your payouts: amounts, statuses, created/paid dates. |
| `recommend_program(topN?, recencyWindowDays?, maxRecencyBoost?, includePaused?)` | Score all active programs against your skills manifest, ranked best-fit first. |
Programs can be addressed by **handle or GUID** everywhere (`coca-cola` works,
no need to copy guids); handles are resolved through a cached program index.
## Setup
```bash
npm install
npm run build
```
Set environment variables:
```bash
export INTIGRITI_API_TOKEN="your-researcher-api-token" # required
export INTIGRITI_SKILLS_PATH="./skills.json" # optional, default shown
export INTIGRITI_SNAPSHOT_PATH="./data/scope-snapshots.json" # optional, default shown
```
Generate a Personal Access Token (PAT) from your Intigriti profile:
**Personal access tokens**. Auth is `Authorization: Bearer <PAT>`.
Copy `skills.example.json` to `skills.json` and edit it to reflect your
strengths — each entry is a skill/vuln-class, a confidence weight (0–1), and
the asset types it applies to:
```json
{ "skill": "ssrf", "weight": 0.8, "appliesTo": ["api", "url"] }
```
**Tip:** use Intigriti's official skill names (e.g. `"Web Hacking"`,
`"API"`, `"Mobile Hacking"`, `"Cloud Hacking"`, `"Reverse Engineering"`) —
so the strongest signal (programs explicitly requiring that skill on an
asset) matches directly. `skills.json` is git-ignored since it's personal.
## Running
Standalone:
```bash
npm start
```
As an MCP server in your client config (point at the built entry point with
the env vars above):
```json
{
"mcp": {
"intigriti": {
"type": "local",
"command": ["node", "/path/to/intigriti-mcp/dist/index.js"],
"enabled": true,
"environment": {
"INTIGRITI_API_TOKEN": "{env:INTIGRITI_API_TOKEN}",
"INTIGRITI_SKILLS_PATH": "/path/to/skills.json",
"INTIGRITI_SNAPSHOT_PATH": "/path/to/scope-snapshots.json"
}
}
}
}
```
## How scoring works
`recommend_program` fetches every active program's scope and uses two
matching signals:
1. **Official (strongest, 3× weight):** Intigriti itself declares which
skills an asset requires (`requiredSkills` on each domain). If the
required skill matches an entry in your manifest, that's a direct hit.
2. **Heuristic:** asset-type overlap with your manifest's `appliesTo` list.
```
score = Σ(weighted matches: official ×3, type-overlap ×1) / total in-scope assets
```
Normalizing by asset count means a program isn't favored just for being
bigger. See `src/scoring.ts`; the matching logic is intentionally simple to
start — extend it there as your manifest gets richer.
### Recency boost
Intigriti publishes a scope version timestamp per program (`domains.createdAt`)
and a rules-of-engagement version timestamp — whichever was published most
recently drives the boost. If it's within the recency window (default 14 days),
the score is multiplied by a factor that decays linearly from a max boost
(default **1.25×** for a just-updated program) down to 1.0 at the window edge.
This surfaces freshly-changed programs before they cool off. Boosted results
include a note like:
```
Recency boost 1.214x — version published 2.0d ago (window 14d, max 1.25x)
```
Tune it per call: `recommend_program(topN=10, recencyWindowDays=7, maxRecencyBoost=1.5)`.
### Scope fidelity
- Asset types are normalized to a stable vocabulary
(`website`/`wildcard` → `url`, `web_service` → `api`).
- Out-of-scope assets (tier `"Out Of Scope"`) are excluded from scoring.
- Epoch timestamps are auto-detected as seconds or milliseconds.
## Persistence
Scope snapshots are stored as flat JSON at `INTIGRITI_SNAPSHOT_PATH`
(default `./data/scope-snapshots.json`), keyed by program GUID. Snapshots
also record scope/rules version ids so `diff_scope` can flag
rules-of-engagement updates even when the asset list didn't change. Swap
`ScopeStore` for a SQLite-backed version if this grows large or you want
historical diff queries instead of just "since last check".
## Project layout
- `src/intigritiClient.ts` — API client + response mapping (all 6 endpoints)
- `src/format.ts` — markdown renderers for clean agent-facing output
- `src/scoring.ts` — program scoring, official-skill matching, recency boost
- `src/scopeStore.ts` — scope snapshot persistence and diffing
- `src/skillsManifest.ts` — loads your `skills.json`
- `src/index.ts` — MCP tool wiring + handle→GUID resolutionTDQS
Scored across 4 tools
The four tools have mostly distinct purposes, though list_programs and recommend_program both return program lists, and get_program_scope and diff_scope both fetch scope. Clear descriptions make the intended difference—overview vs. ranking and display vs. change tracking—usable, but mild confusion risk remains.
All tool names follow a consistent lowercase snake_case verb-first convention: list_programs, get_program_scope, diff_scope, recommend_program. The object follows the verb in each case, making the naming pattern predictable and easy to extend.
Four tools is a well-scoped size for a focused Intigriti program-monitoring server. Each tool addresses a necessary step in the workflow without redundancy or bloat.
For the inferred use case—program discovery, scope inspection, scope change tracking, and skill-based recommendations—the surface is largely complete. Submission/reporting workflows are absent, but they appear outside this server's narrow, deliberate scope.