Tools
Every tool the MCP server exposes, the permission it needs, and whether it writes
The server exposes 30 tools across seven areas. Your client always shows the live list after it connects; if this page and your client ever disagree, believe your client.
The server also exposes the enroll_application prompt and sends enrollment instructions when a client connects.
See Enroll from your repository.
Three columns matter:
- Scope: the permission the tool needs. A call without it fails with an error naming the missing permission; retrying will not help, you have to reconnect and grant it.
- Writes: whether the tool changes anything.
- Confirm: whether the tool is marked as needing confirmation. Most clients ask you to approve these before calling. It is set for anything that spends quota, changes customer-visible security state, or produces an artifact that outlives the conversation.
Organizations
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_organizations | mcp:read | - | - | The organizations this credential can act in, and the identifier to pass as organization |
get_organization_usage | mcp:read | - | - | Plan tier, usage against every limit, and whether an operator has put the organization on hold |
create_organization | mcp:organizations:write | yes | yes | Creates an organization owned by the calling account, active from the start, and returns the URL a human opens to choose a plan |
create_organization never charges anything and never collects payment details.
It hands back a link; a person completes checkout in the browser.
An organization-scoped API key cannot call it at all; see Agents as customers.
Applications
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_applications | mcp:read | - | - | Applications with vulnerability counts by severity and scan schedule |
get_application | mcp:read | - | - | One application, its web and REST targets, and a non-secret test-user configuration summary |
register_application | mcp:applications:write | yes | - | Registers an application and its targets, starts proof of domain control for every target host, and returns the DNS record and the well-known file to publish. Uses an application slot from your plan |
configure_application | mcp:applications:write | yes | yes | Attaches an inline OpenAPI JSON/YAML specification (up to 512 KB) to an existing REST surface, or enables specification discovery during scanning |
update_application | mcp:applications:write | yes | - | Changes name, environment or criticality, and sets, replaces or removes the web and REST API targets. A new or changed host has to be proven before the application can be scanned again |
verify_application_domain | mcp:applications:write | yes | - | Checks every pending proof of the application's hosts, both methods, and reports which method proved each host. Issues the record and file for a host that has none yet |
get_domain_verification_status | mcp:read | - | - | Per host: proven or not, by which method, and the exact record and file still to publish. For one application or the whole organization |
Credentials
Write-only, under their own scope.
Nothing on the MCP ever returns a password, an authenticator seed or a code; these tools confirm the account and the kind of code source, never the value.
They exist so an agent can keep a target scannable when its test account rotates, without a person in the dashboard.
Because a secret sent as a tool argument passes through the client's context and usually its local transcript, mcp:credentials:write is separate from mcp:applications:write: grant it only to clients whose logs you trust.
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
set_test_users | mcp:credentials:write | yes | yes | Replaces the test-user strategy and the full account list (role, email or username, password). Accounts left out lose their stored credentials |
set_verification_code_source | mcp:credentials:write | yes | yes | Attaches a TOTP seed or a fixed code to one configured account, or removes them. Fixed codes on production applications require an explicit acknowledgement |
verify_application_domain is not read-only even though it looks like a lookup: it performs DNS queries and HTTPS fetches against your hosts, spends a bounded attempt budget (25 per proof), and can flip a host to proven.
It also issues the record and file for a host that has no proof started yet, which is how a surface added after registration gets one, and re-issues a proof that spent every attempt with a new token.
get_domain_verification_status answers the same question without spending an attempt and reports pending automatic starts, their last refusal and any creation failure.
When pendingFirstPentest is set, checking proof can release that scan: finish authentication and API configuration, then approve the targets and entitlement before checking.
No application can be scanned until you prove you own its domains. This applies to every application, whether it was created over the MCP server, the REST API or the dashboard, and every way of starting a scan refuses until every target host is proven. There is no operator approval that replaces the proof. Two methods, either of which proves a host:
- a DNS TXT record named
_modernpentest-verify.<host>with the valuemodernpentest-verify=<token>, one record per host, which proves that one host; - an HTTPS file at
https://<host>/.well-known/modernpentest-verify.txtcontaining the linemodernpentest-verify=<token>, served with status 200 and no redirect, which proves that one host.
A proof never expires, so a host the organization has already proven is proven for every application that targets it.
Hosts on shared hosting (run.app, vercel.app, netlify.app and the like) can only use the file, because only the provider controls that DNS zone; the tools say so per host.
Full details in Prove you own the domain.
Attack surface
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
get_asset_graph | mcp:read | - | - | The discovered attack surface: asset nodes, their relationships, per-node open-vulnerability counts and test coverage |
expand_asset_cluster | mcp:read | - | - | Lists the individual nodes folded into one cluster card returned by get_asset_graph |
get_asset_graph returns a partial view by design: low-interest nodes are folded into clusters and large graphs are truncated, and the result says so.
An assistant should check that before concluding something is absent from your attack surface, and use expand_asset_cluster rather than assuming a cluster is uninteresting.
Pentests
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_pentests | mcp:read | - | - | Scans newest first, with status, timing, severity counts and cost |
get_pentest | mcp:read | - | - | One scan in full, including failure details when it did not finish cleanly |
get_pentest_progress | mcp:read | - | - | Live phase, per-agent execution counts and cost so far. Poll this, not list_pentests |
start_pentest | mcp:pentests:write | yes | yes | Starts a real scan against your live systems and spends the plan's quota, a pentest credit or the free preview, in that order. Refused until every target host is proven |
cancel_pentest | mcp:pentests:write | yes | yes | Marks a pending or running scan cancelled. See the warning below; it is not a kill switch |
Cancelling a scan does not stop it costing you. It marks the scan cancelled, which discards whatever it had found: nothing is consolidated into vulnerabilities, triaged, or written into a report. But work already running keeps running to completion, so charges continue to accrue for some minutes after the call returns, and a scan cancelled while running keeps the credit or free preview it claimed; one cancelled while still pending releases it. It cannot be undone.
start_pentest sends genuine attack traffic (authentication attempts, injection payloads, active probing) at the surfaces you registered, and spends a scan from your quota.
It is deliberately marked as requiring confirmation.
An assistant should never call it to "check" something it could have answered from existing results.
If you created the application in the dashboard and asked for the first pentest to start as soon as ownership is verified, the platform starts it on its own; the tools report that as pendingFirstPentest so an assistant does not start a second one.
Vulnerabilities
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_vulnerabilities | mcp:read | - | - | Summary rows, filterable by application, severity and status |
get_vulnerability | mcp:read | - | - | One finding in full: impact, technical context, remediation guidance |
get_vulnerability_activity | mcp:read | - | - | Merged timeline of status, assignment, due-date and note changes |
transition_vulnerability | mcp:vulnerabilities:write | yes | yes | Moves a finding through its lifecycle |
Exploit detail
get_vulnerability accepts includeExploitDetail, which defaults to false.
While it is false, everything weaponisable is withheld: proof-of-concept and exploit code, test payloads, attack scenarios, patch-verification code, the resources a finding touched, and raw HTTP request/response logs.
The response lists which of them were withheld, so the assistant knows they exist and can ask you before requesting them.
The call is recorded in your audit log either way.
The default is false because the common question ("summarise my open criticals") has no need of exploit code, and an unattended agent should not pull a working exploit into a transcript by accident.
Status transitions
transition_vulnerability enforces the same lifecycle the dashboard does:
open → in_remediation | accepted_risk | false_positive
in_remediation → remediated | open | accepted_risk | false_positive
remediated → fixed | open
any → openMoving to accepted_risk or false_positive requires a reason, which is recorded on the finding for the humans who will later rely on it.
Moving to remediated schedules an automatic remediation-verification run against your target, which is why the tool asks for confirmation despite deleting nothing.
Reports
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_reports | mcp:read | - | - | Published and failed reports. A report still generating is deliberately not listed |
get_report | mcp:read | - | - | One report with severity totals, risk score and the written summary |
generate_report | mcp:reports:write | yes | yes | Requests the report for a completed pentest. Asynchronous; publishing emails you |
export_report | mcp:reports:write | yes | yes | Renders and returns a PDF download URL |
export_report puts a PDF of your findings behind a permanent, unauthenticated link: it does not expire and cannot be revoked.
Anyone who receives it can read the whole report.
The same is true of an attestation's PDF link, including a private one.
Attestations
| Tool | Scope | Writes | Confirm | What it does |
|---|---|---|---|---|
list_attestations | mcp:read | - | - | Letters with visibility, redaction profile, expiry and view counts. Share ids are excluded |
get_attestation | mcp:read | - | - | One letter, including its share link and content hash |
generate_attestation | mcp:attestations:write | yes | yes | Creates a signed Letter of Attestation for a completed pentest |
generate_attestation takes a redactionProfile (minimal, standard, detailed) and a visibility (private, unlisted, public).
Visibility defaults to private.
It requires a paid plan and spends one of the organization's attestations for the month; a call that exceeds either comes back as a plan-limit error.
Under the minimal profile the published severity counts are emitted as zeros by construction.
They mean "suppressed", not "no findings"; do not read a minimal letter as a clean bill of health.
unlisted and public are both world-readable to anyone holding the share link, as soon as the PDF renders; there is no separate publish step.
Only choose them when you actually want a shareable letter.
Endpoints, payloads and evidence are never published at any redaction profile.
The organization argument
Every tool accepts an optional organization, a slug or organization id that chooses which of your organizations the call acts in.
You only need it if you belong to more than one; if you do and you omit it, the call fails with an error naming them rather than guessing.
It selects among the organizations you already belong to. It cannot grant access to one you do not, and an organization-scoped API key rejects it outright if it names a different organization.
What tools never return
- Credentials of any kind. Test-user passwords, API keys, bearer tokens, OAuth client secrets, deployment-protection bypass secrets, saved browser sessions and TOTP seeds are unavailable at every permission level.
- Session material captured during a scan, by default but not unconditionally.
Raw request and response logs from a finding can contain
Authorizationheaders, cookies and session tokens our agents captured from your live system. They are withheld unless a call passesincludeExploitDetail: true. They are gated, not scrubbed: redaction removes known fields, and it cannot reliably find a token embedded in the body of a raw HTTP log. Treat exploit detail as sensitive whenever you ask for it. - Anything outside your organization. Every call is re-authorized against your actual role, by the same permission checks the dashboard uses.
Untrusted text
Vulnerability titles, descriptions, application names and endpoint paths are partly derived from content our agents read on your target, which means an attacker who can influence a page can influence that text.
Tools wrap it in an <untrusted> marker and the server tells your assistant, on connect, to treat those spans as data and never as instructions.
This makes injection visible rather than impossible. If an assistant ever reports that a finding "instructed" it to do something, that is the mechanism working; do not act on it.
Last updated: September 7, 2026