Metadata-Version: 2.4
Name: cyver-mcp
Version: 0.5.9
Summary: MCP server exposing the Cyver reporting platform to Claude via the cyver-reporting wrapper.
Author-email: Husain Murabbi <husain.murabbi@thoropass.com>
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cyver-reporting>=0.0.8
Requires-Dist: mcp[cli]>=1.2.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: keyring>=24.0.0
Dynamic: license-file

# cyver-mcp

A Model Context Protocol (MCP) server that lets Claude read from and
write to the [Cyver](https://cyver.io/) pentest reporting platform
through chat. Wraps the
[`cyver-reporting`](https://pypi.org/project/cyver-reporting/) Python
client and exposes 39 tools across reads, writes, and four
modifications-gated patch tools. OWASP Top 10 (Web + API) and
checklist tags apply atomically at create time and survive
subsequent updates.

## Features

- **21 read tools** (always on): projects, findings, clients,
  users (search + single-user detail), teams, client assets, plus
  reference data (vulnerability types, report templates, project
  templates, **per-project checklist + compliance catalogue with
  stable codes for tagging findings**), finding-library lookups,
  project-file listing and download (download returns inline image
  content for screenshots / PoC captures).
- **Auto-resolved default project template.** `create_project`
  discovers "Default Pentest" by title at runtime, so callers don't
  need to look up or hard-code a UUID; pass an explicit
  `project_template_id` to override.
- **14 write tools** (opt-in via `CYVER_ALLOW_WRITES=1`): create
  findings (from scratch or from a library template, with inline
  evidence, pre-emptive QA enforcement mirroring
  `/pentest-report-review` — rejects em dashes / sentinels / emojis
  / malformed CVE-CWE refs / severity-CVSS mismatches — and a
  dry-run preview that echoes the parent-project context before
  commit), append evidence, change finding status, change severity
  / CVSS, update client metadata, update client-asset fields,
  create / move projects, create clients and assets, attach users /
  assets / teams to projects, upload binary evidence files. All
  update tools are load-modify-save (preserve every field you
  don't supply).
- **4 modifications-gated patch tools** (additionally
  `CYVER_ALLOW_MODIFICATIONS=1`): `update_finding_fields` patches
  textual finding fields (name, description, recommendation,
  background, impact / likelihood prose, CVE / CWE /
  vulnerability-type / MITRE technique lists);
  `update_finding_evidence` patches a single evidence record on a
  finding (title, issue_details, results, reproduce, location, ip,
  port, protocol, version, visibility, compliance status);
  `update_finding_checklist_links` and
  `update_finding_compliance_links` tag a finding with checklist
  task codes (e.g. `OTG-AUTHN-001`) and compliance control codes
  (e.g. `A02:2025` for OWASP Top 10 Web, `API2:2023` for OWASP Top
  10 API). All load-modify-save: only supplied fields change.
  Built for post-review fixes piped through `/pentest-validator` or
  `/pentest-report-review` skills.
- **PUSH gate on `create_finding`, `update_finding_fields`, and
  `update_finding_evidence`.** They produce or rewrite
  customer-visible report content, so they default to
  `dry_run=True` and require `confirm="PUSH"` for live writes. The
  user types PUSH in chat; Claude forwards the literal string.
  `create_finding`'s dry-run also echoes the parent-project context
  (code, name, status) so users can confirm they're committing to
  the right project. `update_finding_status` and
  `update_finding_severity` are workflow-tier — no PUSH gate,
  standard `dry_run=False` default.
- **Dry-run preview** on every write tool. Returns the would-be
  request body without sending.
- **Audit log** as JSON lines on every write. Default sink is
  stderr; set `CYVER_AUDIT_LOG` to a file path to also append there.
  Sensitive values redacted automatically.
- **Severity 0..4 ↔ label** and **finding status 1..14 ↔ label**
  mappings applied automatically on reads and accepted by name on
  writes.
- **No secrets in the MCP config block.** The server loads `.env`
  itself.
- **Auto-reauth on token expiry.** The long-lived server self-heals on a
  401: it re-authenticates in place and retries the call once, so an
  expired session recovers with no reconnect — silently when the refresh
  token is still valid, or on the next call after `cyver-mcp login`
  otherwise.

## Requirements

- Python 3.10 or newer
- A Cyver portal account (username + password; 2FA supported)
- Claude Desktop or Claude Code

Pure Python, runs on macOS, Linux, and Windows.

## Installation

```bash
pipx install cyver-mcp
```

`pipx` is the recommended path for CLI tools. Plain
`pip install cyver-mcp` works too if you prefer to manage your own
venv.

## Quick Start

### One-time setup

Run the wizard. It writes `~/.config/cyver-mcp/.env` (chmod 600) and
primes the encrypted refresh-token cache.

```bash
cyver-mcp setup
```

Prompts:

```
Portal hostname (e.g. app.cyver.io): app.cyver.io
Username or email: you@example.com
Password: ********
Enable write tools (create findings, projects, etc.)? [y/N]: y
Enable modifications tools (patch existing finding text and evidence: update_finding_fields, update_finding_evidence)? [y/N]: y
2FA required. A code has been sent to your email.
Enter 2FA code: 123456
OK. Token cached for pentester session on app.cyver.io.
```

### Re-authenticate later

When the cached token expires, refresh without re-running setup:

```bash
cyver-mcp login
```

Prompts for a 2FA code only when the wrapper raises
`Cyver2FARequired`. Otherwise silent. A running MCP server picks up the
refreshed token on its next call automatically — no reconnect or restart.

To wipe the cache:

```bash
cyver-mcp logout
```

### Connect Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json`
(macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "cyver": {
      "command": "cyver-mcp"
    }
  }
}
```

Restart Claude Desktop. The cyver tools appear in the tool list.

### Connect Claude Code

Add the same block to `.mcp.json` in any project where you want the
tools available, then restart Claude Code in that directory.

### Try it

In a new chat:

> List three projects from Cyver.

Claude calls `search_projects` and returns three real project
records. To verify writes (if enabled):

> Use create_finding with dry_run=true to preview a Critical finding
> for project P-YYYY-NNNN about MFA missing.

You see a `would_send` payload back, with no actual change made on
the portal.

## Tools

### Reads (21, always registered)

| Tool                              | Purpose                                                                  |
| --------------------------------- | ------------------------------------------------------------------------ |
| `search_projects`                 | Filter projects by client, status, or name.                              |
| `get_project`                     | Full detail for one project.                                             |
| `list_project_report_versions`    | Versions of the project's report.                                        |
| `list_project_checklists`         | Checklists associated with a project.                                    |
| `list_project_compliance_norms`   | Compliance norms attached to a project.                                  |
| `search_findings`                 | Filter by project, client, reporter, reviewer, status, severity, type, or free-text. Includes `severityLabel` / `statusLabel` and the reporter / reviewer display name. |
| `get_finding`                     | Full finding detail (evidence included by default).                      |
| `search_clients`                  | Filter clients by name/status.                                           |
| `get_client`                      | Full client detail.                                                      |
| `list_client_assets`              | Assets owned by a client.                                                |
| `search_users`                    | Filter users by name, client, or role.                                   |
| `get_user`                        | Full detail for one user by UUID.                                        |
| `search_teams`                    | Filter teams by name, client, or project.                                |
| `list_vulnerability_types`        | Vulnerability type catalog.                                              |
| `list_report_templates`           | Available report templates.                                              |
| `list_project_templates`          | Available project templates (used by `create_project` auto-resolve).     |
| `list_finding_libraries`          | Finding-library catalog.                                                 |
| `list_finding_templates`          | Templates inside a library.                                              |
| `list_project_files`              | List files attached to a project (id + name + type metadata). Pairs with `download_project_file`. |
| `list_project_template_catalogue` | Returns the project's checklist + compliance catalogue (e.g. OWASP OTG v4 tasks, OWASP Top 10 Web / API controls). Stable codes for cross-project tagging. |
| `download_project_file`           | Fetch a project file's bytes; returns an inline image content block for `image/*` MIMEs. |

### Writes (14, opt-in via `CYVER_ALLOW_WRITES=1`)

With the flag unset, the tools are not registered at all (Claude
can't see or call them). `update_finding_status` and
`update_finding_severity` are workflow-tier updates with the
standard `dry_run=False` default. The PUSH gate applies only to
`update_finding_fields` in the modifications tier below.

| Tool                          | Purpose                                                          |
| ----------------------------- | ---------------------------------------------------------------- |
| `create_finding`              | Create a finding on a project (with optional inline evidence; supports `template_id` for from-template creation). |
| `update_finding_status`       | Change a finding's status. Load-modify-save; preserves every other field. |
| `update_finding_severity`     | Change severity (0..4 / Informational..Critical) and / or CVSS 3.1 vector + score. Load-modify-save. |
| `add_evidence_to_finding`     | Append evidence records to an existing finding (true add).       |
| `create_project`              | Create a project for a client.                                   |
| `update_project_status`       | Move a project (ONBOARDING / TESTING / REPORTING / REMEDIATION / COMPLETED). |
| `create_client`               | Create a new client.                                             |
| `create_client_asset`         | Create an asset under a client.                                  |
| `add_users_to_project`        | Append users (true add, no replace).                             |
| `add_assets_to_project`       | Append assets (true add, no replace).                            |
| `add_teams_to_project`        | Append teams (true add, no replace).                             |
| `upload_evidence_to_project`  | Upload a binary file (screenshot, PoC, etc.) as project evidence.|
| `update_client`               | Load-modify-save patch on a client (name, status, account_manager_id, client_information, label_id_list, team_id_list). |
| `update_client_asset`         | Load-modify-save patch on a client asset by (client_id, asset_id). |

Every write tool accepts `dry_run=True` and emits one audit-log
line per call. Update tools additionally capture per-field from/to
diffs in the audit log.

### Modifications (4, additionally `CYVER_ALLOW_MODIFICATIONS=1`)

| Tool                          | Purpose                                                                                                       |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `update_finding_fields`       | Patch any subset of textual finding fields (name, description, recommendation, background, impact / likelihood prose, CVE / CWE / vulnerability-type / MITRE technique lists). Load-modify-save; only supplied fields change. PUSH-gated. |
| `update_finding_evidence`     | Patch a single evidence record on a finding by `evidence_id` (title, issue_details, results, reproduce, location, ip, port, protocol, version, is_visible_in_report, evidence_compliance_status). Load-modify-save; preserves other fields on that evidence and other evidence records on the finding. PUSH-gated. |
| `update_finding_checklist_links`  | Tag a finding with checklist task codes (e.g. `["OTG-AUTHN-001"]`). PUSH-gated. Pass an empty list to clear. |
| `update_finding_compliance_links` | Tag a finding with compliance controls under one norm (e.g. `norm_code="OWASPTOP102025", control_codes=["A02:2025"]`). PUSH-gated. Other norms preserved; call twice to tag both Web and API Top 10. |

Use these for post-review fixes from `/pentest-validator` or
`/pentest-report-review`. Severity / CVSS go through
`update_finding_severity` instead. Status goes through
`update_finding_status`. To append NEW evidence (rather than patch
an existing record), use `add_evidence_to_finding`. To discover
which checklist task and compliance control codes a project
supports, use `list_project_template_catalogue`.

## Configuration

### Environment variables

The server reads `.env` from `$CYVER_CONFIG`, then `./.env`, then
`~/.config/cyver-mcp/.env`. Process env always wins over file values.

| Variable                  | Required  | Purpose                                                                       |
| ------------------------- | --------- | ----------------------------------------------------------------------------- |
| `CYVER_PORTAL`            | yes       | Portal hostname, e.g. `app.cyver.io`.                                         |
| `CYVER_USERNAME`          | preferred | Cyver username or email. Primary auth path.                                   |
| `CYVER_PASSWORD`          | preferred | Used with `CYVER_USERNAME`.                                                   |
| `CYVER_API_KEY`           | optional  | Static API key. Leave blank to use username/password.                         |
| `CYVER_SESSION_TYPE`      | optional  | `pentester` (default) or `client`.                                            |
| `CYVER_ALLOW_WRITES`        | optional  | `1` to register the 12 write tools. Default: off.                             |
| `CYVER_ALLOW_MODIFICATIONS` | optional  | `1` to also register `update_finding_fields`. Has no effect without `CYVER_ALLOW_WRITES=1`. |
| `CYVER_AUDIT_LOG`         | optional  | Path to append audit-log JSON lines. Default: stderr.                         |
| `CYVER_CONFIG`            | optional  | Override the `.env` search path.                                              |

Username/password is the preferred path. Static API keys are
supported as a fallback when one is available.

### Audit log

Every write tool (live or dry-run) emits one JSON object per line:

```json
{"ts":"2026-04-27T10:46:21Z","tool":"create_finding","target":"<project_id>","result":"ok","params":{...}}
```

`result` is one of `ok`, `dry_run`, or `error:<ExceptionName>`.
Sensitive values (passwords, tokens, 2FA codes) are redacted before
the line is written.

## License

MIT.

Developed and maintained by penetration testers at
[Thoropass](https://thoropass.com/).
