Skip to content

Repository files navigation

SRepMCP

A remote MCP server for writing penetration test reports in SysReptor. It lets Claude (or any MCP client) create projects, write and score findings, fill report sections, attach screenshots, quality-check the report and render the PDF, acting as the signed-in SysReptor user.

  • Built-in OAuth 2.1 (PKCE, dynamic client registration, rotating refresh tokens with reuse detection, revocation). Users sign in with their own SysReptor API token, so every change is attributed to the right person.
  • Design-aware: reads each project's field definitions and validates data before saving. SysReptor itself silently drops unknown fields and accepts malformed CVSS vectors; SRepMCP rejects them with a clear message.
  • Report QA: check_report combines SysReptor's checks with deeper linting: invalid CVSS, missing required fields, references to images that were never uploaded, embedded base64 images, duplicate titles.
  • Safe by default: deletions are approved by the human in the client UI (not by the model), SREPMCP_READ_ONLY hides all write tools, and partial updates never wipe other fields.

How authentication works

MCP client ──(1) register + /authorize (PKCE)──▶ SRepMCP ──(2) redirect──▶ /login page
                                                                  │ user pastes SysReptor API token
                                                                  ▼
                                               (3) verified live: GET /api/v1/pentestusers/self/
                                                   stored Fernet-encrypted, single-use code issued
MCP client ◀──(4) code → /token: srm_at_… access + srm_rt_… refresh token (hashed at rest)
MCP client ──(5) Bearer srm_at_… ──▶ tools ──▶ SysReptor API (as that user)

Security model

Identity and tokens

  • SysReptor API tokens never leave the server and are encrypted at rest. MCP tokens are opaque, stored only as SHA-256 hashes, and never forwarded to SysReptor (no token passthrough).
  • Access tokens last 1 hour. Refresh tokens last 30 days and rotate on every use. If an already-rotated refresh token is presented again (a sign of theft), every token descending from that login is revoked.
  • Tokens are bound to the OAuth client that obtained them and to this server (RFC 8707 resource is validated).
  • SREPMCP_ALLOWED_USERS is checked on every request, so removing a username cuts off that user's existing sessions.
  • If SysReptor rejects a stored token (expired, revoked, user disabled), that user's MCP sessions are revoked.
  • sign_out deletes the stored token, all of the user's sessions and their pending downloads.

OAuth hardening

  • Redirect URIs are restricted to known MCP clients by default (Claude.ai/Desktop, Claude Code and other loopback clients, Cursor, VS Code). This stops attackers from registering their own client and phishing a teammate into authorizing it.
  • The login page shows the client's name and redirect host, uses a CSRF token, is single-use, cannot be framed, and sends no referrer. PKCE (S256) is mandatory, and authorization codes are single-use and expire after 5 minutes.

HTTP hardening

  • Per-IP rate limits on /login, /register, /authorize, /token, /revoke and /downloads, with bounded memory (IPv6 clients are grouped by /64).
  • X-Forwarded-For is only trusted when the connection comes from SREPMCP_TRUSTED_PROXIES.
  • Request bodies are capped based on SREPMCP_MAX_UPLOAD_MB. Access logs are off by default, because paths contain capability URLs.

Data

  • Generated PDFs are encrypted on disk, served once through an unguessable link that expires after 10 minutes, and deleted after download or expiry (a background job also cleans up leftovers).
  • The SQLite database contains no plaintext secrets; the data directory is owner-only (0700).
  • Uploads are sanitized: filenames are cleaned, image types are checked from their content (SVG is rejected), and sizes are limited.

Input handling

  • Every id placed in a SysReptor URL must be a UUID or slug. Percent-encoding alone isn't enough: an id of .. would be collapsed by URL normalization, turning e.g. a finding delete into a project delete.
  • Data written to findings and sections is validated against the project's design before saving. Scans of report markdown use linear-time patterns.

Destructive and data-exposing actions

  • delete_project, delete_finding, delete_note, sign_out and create_template_from_finding (which copies a client's finding into the template library every SysReptor user can read) ask the user for approval through MCP elicitation when the client supports it. The model's confirm flag is then ignored, so text injected into a finding cannot trick the model into deleting data. The approval is bound to the exact item being deleted. Clients without elicitation fall back to confirm=true. Those clients should be set to require manual approval for tools annotated destructiveHint.

Residual risks

  • API tokens have the user's full SysReptor rights, because SysReptor tokens can't be scoped. Create a dedicated token with an expiry date for the MCP, and avoid using a superuser account for day-to-day reporting.
  • Report content goes to the model provider. Everything the model reads (findings, notes, PDF passwords passed to render_report_pdf) is part of the LLM conversation. Follow your client-confidentiality rules.
  • Prompt injection in report content can still influence non-destructive writes (e.g. editing a finding), just like any agent with write access. Keep SREPMCP_READ_ONLY=true for review-only use.
  • Rate limits are kept in memory per process. Run a single instance, or add limits at the proxy.

Tools

Area Tools
Account whoami, sign_out
Designs list_project_types, get_report_fields
Projects list_projects, get_project, create_project, update_project, set_project_finished, delete_project
Sections get_section, update_section
Findings list_findings, get_finding, create_finding, update_finding, delete_finding, reorder_findings
Templates search_finding_templates, get_finding_template, create_template_from_finding
Reference search_cwes
Notes list_notes, get_note, create_note, update_note, delete_note
Uploads upload_image, upload_file, list_uploads
Delivery check_report, render_report_pdf

Prompts: write_finding (raw notes → complete finding), write_executive_summary, qa_report.

Tools carry MCP annotations (readOnlyHint, destructiveHint, ...) so clients can auto-approve reads and ask before writes.

Deploy (Docker + reverse proxy)

  1. DNS: point mcp.shellvoide.com at the host.
  2. Configure:
    cp .env.example .env
    docker compose run --rm --no-deps srepmcp genkey   # paste into SREPMCP_ENCRYPTION_KEY
    Set SYSREPTOR_URL, MCP_BASE_URL=https://mcp.shellvoide.com and SREPMCP_ALLOWED_USERS. Don't set SYSREPTOR_API_TOKEN on the server.
  3. Start: docker compose up -d --build, then check with curl http://127.0.0.1:8000/healthz.
  4. Reverse proxy (TLS is required): use deploy/Caddyfile or deploy/nginx.conf. The proxy must not buffer responses (SSE) and should allow uploads of about 40 MB. Turn off proxy access logs for /downloads/ and /login, or keep those logs private.
  5. Verify: curl https://mcp.shellvoide.com/.well-known/oauth-authorization-server should return JSON.

Data (OAuth clients, encrypted tokens, temporary PDFs) lives in the srepmcp-data volume. Back up SREPMCP_ENCRYPTION_KEY: without it, stored tokens can't be decrypted and every user has to sign in again. To rotate the key, set SREPMCP_ENCRYPTION_KEY=NEW,OLD.

The container port is bound to 127.0.0.1 only, so it's reachable only through the proxy. The Docker image pins every dependency to the tested versions in requirements.lock.

Connect a client

The MCP endpoint is https://mcp.shellvoide.com/mcp. Each client opens the SysReptor login page the first time. Create an API token in SysReptor under Profile → API Tokens and paste it there.

  • Claude.ai / Claude Desktop: Settings → Connectors → Add custom connector → paste the URL.
  • Claude Code:
    claude mcp add --transport http sysreptor https://mcp.shellvoide.com/mcp
    Then run /mcp inside Claude Code to authenticate.
  • Cursor / VS Code: add a remote (streamable HTTP) MCP server with the URL. OAuth is discovered automatically.
  • Other clients: add their OAuth callback URL to SREPMCP_ALLOWED_REDIRECT_URIS. If you don't, registration is rejected with invalid_redirect_uri.

Configuration

See .env.example for every option. The most important:

Variable Default Purpose
SYSREPTOR_URL (required) SysReptor base URL
MCP_BASE_URL http://localhost:8000 Public URL of this server (OAuth issuer). Must be https in production
SREPMCP_ENCRYPTION_KEY (required) Fernet key(s) for tokens and PDFs at rest
SREPMCP_ALLOWED_USERS any active user SysReptor usernames allowed to use the server (checked on every request)
SREPMCP_ALLOWED_REDIRECT_URIS known MCP clients OAuth redirect allowlist; * = any https client
SREPMCP_TRUSTED_PROXIES loopback + private ranges Networks whose X-Forwarded-For is trusted
SREPMCP_READ_ONLY false Hide all write tools
SREPMCP_DOWNLOAD_TTL / SREPMCP_DOWNLOAD_SINGLE_USE 600 / true PDF link lifetime and reuse
SREPMCP_ACCESS_TOKEN_TTL / SREPMCP_REFRESH_TOKEN_TTL 3600 / 2592000 Token lifetimes (seconds)

Local development

python -m venv .venv && .venv/Scripts/pip install -e ".[dev]"   # Linux/macOS: .venv/bin/pip
python -m srepmcp check                    # verifies SYSREPTOR_URL and SYSREPTOR_API_TOKEN from .env
pytest                                     # offline tests: tools, OAuth flow, security regressions (SysReptor mocked)

Single-user stdio mode (no OAuth, uses SYSREPTOR_API_TOKEN) for quick local testing:

claude mcp add sysreptor-local --env SREPMCP_TRANSPORT=stdio -- .venv/Scripts/python -m srepmcp

Notes and limitations

  • Community edition: SysReptor's comments and spellcheck APIs require a Professional license, so they aren't exposed.
  • CVSS 4.0 vectors are syntax-checked, but only SysReptor computes their scores. CVSS 3.0/3.1 are scored locally.
  • SysReptor documents its API as unstable. SRepMCP was built and tested against the API of sysreptor.shellvoide.com (September 2026). Re-run the tests after SysReptor upgrades.
  • New projects copy the design's default section values, which can include sample data from another client. create_project lists those fields and can clear them (clear_prefilled_sections=true).

About

A Sysreptor MCP for writing Pentests ASAP

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages