Skip to main content

Pter MCP Tool Reference

This document provides a comprehensive reference for the Pter MCP tools available to agents.

CRITICAL: Tool Name Format

All engine tools must be called with the mcp__pter-api-server__ prefix. For example:

  • Correct: mcp__pter-api-server__manage_findings
  • Wrong: manage_findings

Local agent tools use mcp__pter__ prefix:

  • Correct: mcp__pter__manage_auth_session

Browser tools use mcp__playwright__ prefix:

  • Correct: mcp__playwright__browser_navigate

Web search tools use mcp__tavily__ prefix:

  • Correct: mcp__tavily__tavily-search

Quick Reference Table

ToolActionsPurpose
mcp__pter-api-server__manage_servicesupdate, get, list, add_technologyService registry management
mcp__pter-api-server__create_serviceN/A (single action)Register new service with tech fingerprinting (subagent only)
mcp__pter-api-server__update_endpoint_detailsN/A (single action)Update endpoint documentation with quality validation (subagent only)
mcp__pter-api-server__update_service_detailsN/A (single action)Update service documentation with quality validation (subagent only)
mcp__pter-api-server__update_service_journalN/A (single action)Append content to a service journal section (subagent only)
mcp__pter-api-server__manage_endpointsupdate, get, listAPI endpoint tracking (read/update only)
mcp__pter-api-server__create_endpointN/A (single action)Register new endpoint with full documentation (subagent only)
mcp__pter-api-server__manage_flowscreate_flow, update_flow, get_flow, list_flows, delete_flowUser journey flows
mcp__pter__manage_auth_sessionlist_sessions, get_current_session, replace_current_session, create_new_session, reauth, set_metadata, get_metadata, add_api_key, list_api_keys, update_capabilities, update_descriptionAuthentication session management
mcp__pter-api-server__manage_assessmentsupdate, get, list, delete, submit_resultAssessment management (read/update only — create via subagent)
mcp__pter-api-server__create_assessmentN/A (single action)Register assessment with quality validation + auto P5 task (subagent only)
mcp__pter-api-server__manage_findingscreate, update, get, list, delete, statisticsFinding submission & tracking
mcp__pter-api-server__manage_accountscreate, list, get, update, delete, statsAccount management (discovered user accounts)
mcp__pter-api-server__manage_credentialscreate, list, get, update, delete, list_by_account, update_statusCredential management (passwords, API keys, tokens)
mcp__pter-api-server__manage_attack_chainscreate, update, get, list, deleteAttack chain management (multi-step attack paths)
mcp__pter-api-server__manage_tasksget_details, update_statusTask lifecycle management
mcp__pter-api-server__create_taskN/A (single action)Create downstream task with quality validation (subagent only)
mcp__pter__manage_auth_sessionlist_sessions, get_current_session, replace_current_session, create_new_sessionAuthentication session switching
mcp__pter-api-server__lookup_referenceN/A (single action)Look up CWE/CVE by ID (free, instant)
mcp__pter-api-server__query_knowledgeN/A (single action)Search encyclopedia (CWE/CVE, playbooks)
mcp__tavily__tavily-searchsearchWeb search for exploit research, documentation
mcp__tavily__tavily-extractextractExtract content from specific URLs
mcp__pter__list_emails, mcp__pter__read_emaillist, getRead verification codes, OTPs, confirmation links
mcp__pter-api-server__save_memoryN/A (single action)Save knowledge to RAG
mcp__pter-api-server__query_memoriesN/A (single action)Query saved memories

mcp__pter-api-server__manage_services

Service registry management for tracking discovered services.

To CREATE a new service, use the register-service subagent — see "Service Registration" below.

Actions

get

service = mcp__pter-api-server__manage_services(action="get", service_id=1)

update

mcp__pter-api-server__manage_services(
action="update",

service_id=1,
name="auth-service-v2"
)

description is read-only — it is set during service creation and cannot be changed via update. To update a service's description, use the update-service subagent: Agent("update-service", "Service 5 needs update. Found..."). To record findings about a service, use save_memory(content=..., references=["service://ID"]).

list

all_services = mcp__pter-api-server__manage_services(action="list")
# Filter in memory if needed:
matching = [s for s in all_services.get("services", []) if "auth" in s.get("name", "")]

add_technology

mcp__pter-api-server__manage_services(
action="add_technology",

service_id=1,
tech_name="nginx", # Required
tech_category="web_server", # Required: web_server, framework, database, language, library, etc.
tech_version="1.24.0", # Optional
tech_confidence="high", # Optional: low, medium, high
tech_evidence="Server header: nginx/1.24.0" # Optional
)

Note: Assessments

To create assessments targeting a service, use the register-assessment subagent — see "Assessment Registration" below. Pass target EntityIDs like service://ID.


Service Registration (CRITICAL — READ THIS)

You CANNOT create services directly. Service creation is handled by the register-service subagent, which fingerprints technologies and documents the service. A P2 domain exploration task is automatically created.

Use Agent("register-service", "...") to spawn the subagent.

When you discover a new service, delegate: Agent("register-service", "Found new service at https://api.target.com. Server: nginx/1.24. Discovered by subdomain enumeration.")

The subagent will:

  1. Check for duplicates
  2. Probe the service root and common paths
  3. Fingerprint technologies from headers and error pages
  4. Write a detailed description
  5. Register and auto-create P2 task

What to include: base_url (required), name, technologies observed, auth info, discovery context


Updating Endpoints and Services

To update an endpoint's description, headers, or examples with new findings, use Agent("update-endpoint", "Endpoint 42 needs update. Found...").

To update a service's description or add technologies, use Agent("update-service", "Service 5 needs update. Found...").

To update a service's journal (structured living document), use Agent("update-service-journal", "Service ID: 5. Section: tips. Add: 'WAF bypass: curl to 203.0.113.42'. Revision summary: Added WAF bypass tip").

Journal sections: overview, architecture, access, surface_highlights, tips, known_issues. Reference entities using entity_type://id format (e.g., endpoint://42, finding://7).


mcp__pter-api-server__manage_endpoints

Read and update existing API endpoints. Supports get, list, and update only.

To CREATE a new endpoint, use the register-endpoint subagent — see "Endpoint Registration" below.

Actions

get

endpoint = mcp__pter-api-server__manage_endpoints(action="get", endpoint_id=1)

list

all_endpoints = mcp__pter-api-server__manage_endpoints(action="list")
# Filter by service:
service_endpoints = mcp__pter-api-server__manage_endpoints(action="list", service_id=5)

update

# Update non-description fields (discovered_by, openapi_schema, service_id)
mcp__pter-api-server__manage_endpoints(
action="update",
endpoint_id=1,
discovered_by="Also found via JavaScript source map analysis"
)

description is read-only — it is set during endpoint creation and cannot be changed via update. To record your findings about an endpoint, use save_memory instead:

mcp__pter-api-server__save_memory(
content="Endpoint accepts wildcard queries with no input length limit — potential DoS vector",
memory_type="discovery",
references=["endpoint://1"]
)

Endpoint Registration (CRITICAL — READ THIS)

You CANNOT create endpoints directly. Endpoint creation is handled by the register-endpoint subagent, which thoroughly investigates each endpoint before registering it with full documentation.

IMPORTANT: Use Agent("register-endpoint", "...") to spawn the subagent. Do NOT use Skill("register-endpoint") — the Skill tool only loads instructions into your context, it does not register anything. The Agent tool spawns an isolated subagent that actually does the investigation and registration.

When you discover a new endpoint, delegate to the subagent:

Agent("register-endpoint", "Found POST /api/v2/users/search on service_id=5. "
"Requires Bearer auth. Token: Bearer eyJhbGciOi... "
"Saw query and filters params in JS bundle. "
"Discovered by analyzing network traffic during browsing.")

The subagent will:

  1. Check for duplicates
  2. Probe the endpoint with curl (authenticated + unauthenticated)
  3. Map all headers, parameters, and request body
  4. Capture real curl examples with full response headers and body
  5. Write a detailed description
  6. Register via create_endpoint (which validates quality)
  7. Auto-create a P4 vulnerability recon task

What to include in your delegation message:

  • HTTP method + full URL (required)
  • Service ID (required)
  • Auth tokens/cookies with actual values (if you have them)
  • How you discovered it
  • Any parameters or headers you already observed

When to delegate:

  • Every new URL you encounter — network traffic, JS bundles, error messages, API docs, spidering
  • If in doubt, delegate. Registering a duplicate is harmless (the subagent checks)
  • An unregistered endpoint is INVISIBLE to the rest of the system

Task Registration

To create downstream tasks (P3 flow analysis, P6 validation, P7 chaining, P8 exploitation), use the register-task subagent:

Agent("register-task", "P3 flow analysis needed. Phase: 3. Service: auth-service (service_id=5). Flow: Password reset. Known steps: request → email → token → reset. Analyze for token predictability, rate limiting, enumeration.")

The subagent validates quality, checks for duplicates, and creates with proper service linkage.

P2/P4/P5 tasks are auto-created — you do NOT create them via this subagent:

  • P2 tasks: auto-created by create_service
  • P4 tasks: auto-created by create_endpoint
  • P5 tasks: auto-created by create_assessment

mcp__pter-api-server__manage_flows

Track user journeys and attack paths.

Actions

create_flow

mcp__pter-api-server__manage_flows(
action="create_flow",

name="user_registration",
start_state="anonymous", # Required: initial state
end_state="registered", # Required: final state
description="Self-service registration: user submits email+password, receives verification email with signed token, clicks link to activate account. Creates user record with 'member' role. No admin approval required. Email uniqueness enforced at DB level.",
criticality="high",
)

get_flow

flow = mcp__pter-api-server__manage_flows(action="get_flow", flow_id=123)

update_flow

# IMPORTANT: Always GET the flow first and read its current description
# before updating. Never blindly overwrite descriptions.
flow = mcp__pter-api-server__manage_flows(action="get_flow", flow_id=123)
mcp__pter-api-server__manage_flows(
action="update_flow",

flow_id=123,
description=flow["description"] + ". Verification token has 24h expiry, no rate limit on resend.",
criticality="critical"
)

list_flows

all_flows = mcp__pter-api-server__manage_flows(action="list_flows")

delete_flow

mcp__pter-api-server__manage_flows(action="delete_flow", flow_id=123)

mcp__pter-api-server__manage_assessments

Manage assessments — investigation items for attack vectors, CVEs, and attack chains.

To CREATE a new assessment, use the register-assessment subagent via the Agent tool — see "Assessment Registration" below.

Actions

get

assessment = mcp__pter-api-server__manage_assessments(action="get", assessment_id=123)

update

mcp__pter-api-server__manage_assessments(
action="update",

assessment_id=123,
status="confirmed"
)

list

all_assessments = mcp__pter-api-server__manage_assessments(action="list")
# Optional filters: type_filter, status_filter, limit

submit_result

mcp__pter-api-server__manage_assessments(
action="submit_result",

assessment_id=123, # Required
status="confirmed", # Required: "confirmed" or "refuted"
description="## Investigation Result\n\n**Verdict: Exploitable**\n\n...", # Required
report_path="work/docs/exploitation/exploitation_123_CWE-89.md" # Required
)

delete

mcp__pter-api-server__manage_assessments(action="delete", assessment_id=123)

Assessment Registration (CRITICAL — READ THIS)

You CANNOT create assessments directly. Assessment creation is handled by the register-assessment subagent, which validates quality, checks for duplicates, and auto-creates a P5 deep investigation task.

Use Agent("register-assessment", "...") to spawn the subagent.

When you identify a vulnerability to investigate, delegate:

Agent("register-assessment", "Vector: CWE-89 SQL injection on endpoint 42 (POST /api/v2/users/search). Target location: sort_by query param. Approach: time-based blind. Impact: full DB read. Targets: endpoint://42, service://5.")

The subagent will:

  1. Check for duplicate assessments on the same target
  2. Validate description quality (≥100 chars, target location + approach + impact)
  3. Create the assessment
  4. Auto-create P5 deep investigation task (with consolidation)

You do NOT need to create P5 tasks separately — the tool handles it atomically.


mcp__pter-api-server__manage_findings

Submit and track validated security findings. Findings are the primary deliverable of the engagement — they go directly into client reports. Write them like a senior pentester would: precise, evidence-backed, and actionable.

Writing High-Quality Findings

title

One line. Name the vulnerability and where it lives. A reader should know what's broken without opening the finding.

  • Good: Stored XSS via unsanitized markdown in /api/comments
  • Good: Privilege escalation from viewer to admin via IDOR on /api/users/{id}/role
  • Bad: XSS vulnerability (where? what kind?)
  • Bad: Security issue in API (meaningless)

description

The full technical write-up in Markdown. This is the primary content readers see — write it like a standalone vulnerability report, not a summary. Use ## headers, fenced code blocks, tables, and lists to make it scannable and thorough. Match the depth and quality of your exploitation report files.

Structure with these sections:

  1. Summary — 2-3 sentences: what's broken, where, and why it matters
  2. Vulnerability Details — root cause, affected component, conditions (auth, privileges, rate limits). Include request/response evidence inline with fenced code blocks
  3. Impact Analysis — concrete impact in context of this application (data exposure, account takeover, lateral movement — not generic risk language). Quantify where possible (e.g., "16 years of media uploads", "~40 documents from other orgs")
  4. Exposed Data / Affected Resources (if applicable) — tables or lists of what's exposed
## Summary

The `/wp-content/uploads/` directory has Apache `mod_autoindex` enabled,
exposing the entire media library structure from 2010-2026 and multiple
plugin-specific upload directories to unauthenticated users.

## Vulnerability Details

The Apache web server is configured with `Options +Indexes` for the uploads
directory. Any unauthenticated user can browse the full directory tree:

```bash
curl -s "https://target.com/wp-content/uploads/" | head -50
```

```html
<title>Index of /wp-content/uploads</title>
<tr><td><a href="2010/">2010/</a></td><td>2021-08-31 13:39</td></tr>
<tr><td><a href="sucuri/">sucuri/</a></td><td>2021-08-31 13:39</td></tr>
```

No authentication is required. The directory listing is accessible to anyone
with a web browser.

## Impact Analysis

- **16 years of media uploads** (2010–2026) are publicly enumerable
- **Plugin fingerprinting**: security plugins exposed (ithemes-security, sucuri)
- **Debug logs**: `pum-debug-*.log` file accessible (68 bytes)

## Exposed Directories

| Directory | Content |
|-----------|---------|
| 2010/ – 2026/ | Year-based media uploads |
| ithemes-security/ | Security plugin data |
| sucuri/ | WAF plugin data |
| wpcode/cache/ | Code snippet cache |

Don't:

  • Write one-liners or 2-paragraph summaries when you have detailed evidence — put it all in
  • Repeat the title
  • Use filler like "this could potentially allow an attacker to possibly..." — state what happens
  • Include CVSS scores, severity ratings, or remediation in the description — those have their own dedicated fields (cvss_vector, recommended_fix). The description covers what's broken and the impact, not the metadata.

cvss_vector

Required. Every finding must have a CVSS 3.1 vector. The severity field is computed from it — never set severity directly.

Build the vector using the CVSS 3.1 specification:

  • Attack Vector (AV), Attack Complexity (AC), Privileges Required (PR), User Interaction (UI)
  • Scope (S), Confidentiality (C), Integrity (I), Availability (A)

The score maps to severity automatically:

ScoreSeverity
9.0 – 10.0critical
7.0 – 8.9high
4.0 – 6.9medium
0.1 – 3.9low
0.0informational
# Unauthenticated IDOR leaking sensitive data
# AV:N (network) AC:L (no special conditions) PR:N (no auth) UI:N (no interaction)
# S:U (no scope change) C:H (full document contents) I:N (read-only) A:N
cvss_vector="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N" # → 7.5 → high

Don't: File a finding without a cvss_vector. If you can't score it, you don't understand the impact well enough to file it.

reproduction_steps

Step-by-step instructions a human pentester can follow to reproduce the finding independently. Each step must include either a full shell command (curl, sqlmap, nuclei, etc.) or manual browser/UI steps with exact clicks and inputs. Every step must show the expected output that confirms the vulnerability.

Use $VARIABLE shell syntax for placeholders that need to be filled in (e.g., $TOKEN, $SESSION_ID, $TARGET_URL). Never use <VARIABLE> — it breaks copy-paste into a shell and looks like HTML.

1. Authenticate as test@org-a.com (viewer role):
$ curl -s -X POST $TARGET_URL/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email": "test@org-a.com", "password": "TestPass123!"}'

Output: {"access_token": "eyJhbG...","user": {"org_id": "org-a", "role": "viewer"}}

2. Request a document belonging to a different organization:
$ curl -s -H "Authorization: Bearer $TOKEN" \
$TARGET_URL/api/v2/documents/847

Output: {"id": 847, "org_id": "org-b", "title": "Q3 Financial Report", "body": "..."}
Expected: 403 Forbidden. Actual: 200 with full document body from org-b.

3. Enumerate accessible documents across organizations:
$ for id in $(seq 1 100); do \
curl -s -o /dev/null -w "%{http_code} $id\n" \
-H "Authorization: Bearer $TOKEN" \
$TARGET_URL/api/v2/documents/$id; \
done | grep "^200"

Output:
200 12
200 47
200 103
200 847
... (~40 documents from other organizations returned 200)

For browser-based reproduction, specify exact navigation and interactions:

1. Log in to https://target.com/login as test@org-a.com / TestPass123!
→ Lands on /dashboard showing "Org A" in the top-right corner

2. Open browser DevTools → Network tab

3. Navigate to Documents → click any document → note the URL: /documents/847
→ URL contains a numeric document ID

4. In the browser address bar, change the ID: /documents/1
→ Page loads a document titled "Org B Internal Policy"
→ DevTools shows GET /api/v2/documents/1 returned 200 with org_id: "org-b"
Expected: error page or redirect. Actual: full document rendered.

Don't: Write "send a malicious request" without showing the request. Omit response output. Leave the reader guessing what "success" looks like — always contrast expected vs. actual behavior.

evidence

Proof that the vulnerability exists and was exploited, not just suspected. Each item should have type, the command/request, and the response.

evidence=[
{
"type": "request",
"command": "curl -H 'Authorization: Bearer eyJ...' https://target.com/api/v2/documents/847",
"response": '{"id": 847, "org_id": "org-b", "title": "Q3 Financial Report", "body": "..."}'
},
{
"type": "screenshot",
"path": "work/evidence/idor-document-847.png",
"description": "Document 847 belonging to org-b accessed by org-a user"
}
]

Include enough evidence to prove the finding without re-testing. Redact actual secrets but keep the structure.

Specific to this codebase. Reference the pattern that should change.

  • Good: Add authorization check in document_router.get_document() to verify request.user.org_id matches document.org_id before returning. Use the existing OrgAccessGuard middleware.
  • Bad: Implement proper access controls (says nothing actionable)

Common mistakes to avoid

  • Generic descriptions copied from CWE/OWASP — always ground findings in this specific application
  • Missing context — don't assume the reader tested the app; explain the feature being tested
  • Conflating findings — one vulnerability per finding; don't bundle "XSS and also CSRF" together
  • Skipping evidence — a finding without evidence is a claim, not a finding
  • No CVSS vector — every finding needs a calculated vector, not a gut-feel severity

Actions

create

mcp__pter-api-server__manage_findings(
action="create",

# --- REQUIRED fields ---
title="Time-based blind SQL injection in /api/users search",
description=(
"## Summary\n\n"
"The `/api/users/search` endpoint is vulnerable to time-based blind SQL injection "
"via the `query` parameter. The parameter value is interpolated directly into a "
"SQL WHERE clause without parameterization, allowing full database extraction.\n\n"
"## Vulnerability Details\n\n"
"The search handler constructs a SQL query using f-string interpolation:\n\n"
"```python\n"
"query = f\"SELECT * FROM users WHERE name LIKE '%{search_query}%'\"\n"
"```\n\n"
"Injecting a `SLEEP()` call confirms the vulnerability:\n\n"
"```bash\n"
"curl -s -w '\\nTime: %{time_total}s\\n' -X POST https://target.com/api/users/search \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"query\": \"admin\\' AND SLEEP(5)-- -\"}'\n"
"```\n\n"
"```\n"
"Output: {\"users\": []}\n"
"Time: 5.14s (baseline: 0.12s)\n"
"```\n\n"
"No authentication is required. The search endpoint is public-facing and has no "
"rate limiting.\n\n"
"## Impact Analysis\n\n"
"- **Full database extraction**: the `users` table contains email addresses, "
"password hashes (bcrypt), and API keys for all tenants\n"
"- **Cross-tenant data breach**: attacker can extract data belonging to any tenant\n"
"- **No rate limiting**: automated extraction via sqlmap completes in ~30 minutes\n"
),
affected_components=["endpoint://42"], # Required, non-empty. Format: entity_type://id
report_path="work/docs/exploitation/exploitation_42.md", # Required: path to report file
cvss_vector="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", # Required: severity is derived from this

# --- optional fields ---
cwe_id="CWE-89",
reproduction_steps=(
"1. Send a baseline search request:\n"
" $ curl -s -w '\\nTime: %{time_total}s\\n' -X POST https://target.com/api/users/search \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"query\": \"admin\"}'\n\n"
" Output: {\"users\": [{\"id\": 1, ...}]}\n"
" Time: 0.12s\n\n"
"2. Send a time-based injection payload:\n"
" $ curl -s -w '\\nTime: %{time_total}s\\n' -X POST https://target.com/api/users/search \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"query\": \"admin\\' AND SLEEP(5)-- -\"}'\n\n"
" Output: {\"users\": []}\n"
" Time: 5.14s\n"
" Expected: <0.5s response or input rejection. Actual: 5s delay confirms injection."
),
recommended_fix="Use parameterized queries in user_repository.search(). Replace f-string SQL with sqlalchemy text() bind parameters.",
evidence=[
{
"type": "request",
"command": "curl -s -w '\\nTime: %{time_total}s\\n' -X POST https://target.com/api/users/search -H 'Content-Type: application/json' -d '{\"query\": \"admin\\' AND SLEEP(5)-- -\"}'",
"response": "{\"users\": []}\nTime: 5.14s"
}
],
assessment_id=42, # Optional: link to assessment
)

get

finding = mcp__pter-api-server__manage_findings(action="get", finding_id=123)

update

mcp__pter-api-server__manage_findings(
action="update",

finding_id=123,
status="draft", # Valid: draft, validated, submitted, accepted, rejected, remediated, remediation_verified, cancelled, duplicate
cvss_vector="CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", # Recalculated: requires auth (PR:L), read-only (I:N)
)

list

all_findings = mcp__pter-api-server__manage_findings(action="list")
# Optional filters: severity_filter=["critical", "high"], status_filter=["draft"], limit=25

statistics

stats = mcp__pter-api-server__manage_findings(action="statistics")
# Returns: counts by severity, status, vulnerability type

delete

mcp__pter-api-server__manage_findings(action="delete", finding_id=123)

mcp__pter-api-server__manage_accounts

Manage discovered user accounts on the target application.

Actions

create

mcp__pter-api-server__manage_accounts(
action="create",

username="admin@target.com", # Required
account_role="admin", # Optional: role/privilege level
account_status="active", # Optional
notes="Default admin account found during recon" # Optional
)

list

all_accounts = mcp__pter-api-server__manage_accounts(action="list")
# Optional filters: search, account_role, account_status, limit, offset

get

account = mcp__pter-api-server__manage_accounts(action="get", account_id=1)

update

mcp__pter-api-server__manage_accounts(
action="update",

account_id=1,
account_status="compromised",
notes="Password cracked via brute force"
)

delete

mcp__pter-api-server__manage_accounts(action="delete", account_id=1)

stats

stats = mcp__pter-api-server__manage_accounts(action="stats")
# Returns: account counts by role, status, etc.

mcp__pter-api-server__manage_attack_chains

Manage attack chains — multi-step attack paths linking assessments together.

Actions

create

mcp__pter-api-server__manage_attack_chains(
action="create",

title="Privilege escalation via IDOR + password reset", # Required
description="Chain: enumerate user IDs → reset password → access admin panel",
overall_severity="critical", # Optional: critical, high, medium, low, informational
status="draft", # Optional
impact="Full admin account takeover", # Optional
details={"attack_type": "privilege_escalation"}, # Optional: arbitrary JSON
findings=[ # Optional: list of finding dicts linking findings to this chain
{"finding_id": 1, "step_order": 1, "role_description": "IDOR leaks admin email"},
{"finding_id": 3, "step_order": 2, "role_description": "Password reset via email"},
{"finding_id": 7, "step_order": 3, "role_description": "Admin panel access"}
]
)

get

chain = mcp__pter-api-server__manage_attack_chains(action="get", chain_id=1)

update

mcp__pter-api-server__manage_attack_chains(
action="update",

chain_id=1,
description="Updated chain description with new step",
overall_severity="high"
)

list

all_chains = mcp__pter-api-server__manage_attack_chains(action="list")
# Optional filters: severity_filter=["critical", "high"], status_filter=["draft"], search="IDOR", limit=25

delete

mcp__pter-api-server__manage_attack_chains(action="delete", chain_id=1)

mcp__pter-api-server__manage_credentials

Manage discovered credentials — passwords, API keys, tokens, session cookies.

Actions

create

mcp__pter-api-server__manage_credentials(
action="create",

credential_type="jwt", # Required: user_password, jwt, api_key, session_cookie, other
value="eyJhbGciOiJIUzI1NiIs...", # Required: the credential value
account_id=42, # Optional: link to account
status="valid", # Optional: unknown, valid, invalid, expired
notes="Admin JWT from /auth/login response" # Optional
)

get

credential = mcp__pter-api-server__manage_credentials(action="get", credential_id=123)

list

all_credentials = mcp__pter-api-server__manage_credentials(action="list")
# Optional filters: credential_type, status, account_id, search, limit, offset

list_by_account

account_creds = mcp__pter-api-server__manage_credentials(
action="list_by_account",

account_id=42
)

update

mcp__pter-api-server__manage_credentials(
action="update",

credential_id=123,
status="expired",
notes="Token expired after 1 hour"
)

update_status

mcp__pter-api-server__manage_credentials(
action="update_status",

credential_id=123,
status="revoked"
)

delete

mcp__pter-api-server__manage_credentials(action="delete", credential_id=123)

mcp__pter-api-server__manage_tasks

Task lifecycle management. To CREATE tasks, use the register-task subagent via the Agent tool. P2/P4/P5 tasks are auto-created by create_service/create_endpoint/create_assessment.

Actions

get_details

task = mcp__pter-api-server__manage_tasks(action="get_details", task_id="123")

update_status

mcp__pter-api-server__manage_tasks(
action="update_status",

task_id=TASK_ID,
status="done",
summary="Confirmed SQL injection with time-based technique",
key_learnings=["Parameterized queries not used", "Error messages expose DB type"]
)

mcp__pter__manage_auth_session

Manage authentication sessions, API keys, and metadata for cross-account testing.

IMPORTANT: You must close the browser before switching sessions. Switching with the browser open will cause authentication failures.

Actions

list_sessions

sessions = mcp__pter__manage_auth_session(action="list_sessions")
# Returns list of available sessions with their status

get_current_session

session = mcp__pter__manage_auth_session(
action="get_current_session",

session_id="sess-xxx" # Required
)

replace_current_session

# Step 1: Close the browser FIRST
browser_close()

# Step 2: Switch to a different session
mcp__pter__manage_auth_session(
action="replace_current_session",

session_id="sess-xxx" # Required: session ID to switch to
)

# Step 3: Open browser - you are now authenticated as the other user

create_new_session

mcp__pter__manage_auth_session(
action="create_new_session",

login_url="https://target.com/login", # Required: URL of the login page
credential_id=5, # Optional: link to a stored credential (from manage_credentials)
verification_url="https://target.com/dashboard", # Optional: URL to verify successful login
account_id=3 # Optional: link to a discovered account (from manage_accounts)
)
# Note: Auth agent will authenticate the new session via browser automation.
# Check session status after a few moments with list_sessions.

reauth

# Re-authenticate an expired or failing session
mcp__pter__manage_auth_session(action="reauth", session_id="sess-xxx")
# Spawns an auth agent to re-login using stored credentials. Check status after a few moments.

set_metadata

mcp__pter__manage_auth_session(
action="set_metadata",

session_id="sess-xxx",
metadata_key="user_id",
metadata_value="12345"
)

get_metadata

value = mcp__pter__manage_auth_session(
action="get_metadata",

session_id="sess-xxx",
metadata_key="user_id"
)

add_api_key

mcp__pter__manage_auth_session(
action="add_api_key",

session_id="sess-xxx",
key_name="Primary API Key",
api_key="sk-live-abc123..."
)

list_api_keys

keys = mcp__pter__manage_auth_session(action="list_api_keys", session_id="sess-xxx")

update_capabilities

mcp__pter__manage_auth_session(
action="update_capabilities",

session_id="sess-xxx",
capabilities={"can_create_users": True, "can_delete": False}
)

update_description

mcp__pter__manage_auth_session(
action="update_description",

session_id="sess-xxx",
description="Admin session with full access",
scope="admin"
)

When to use:

  • Cross-account testing (IDOR, privilege escalation)
  • Your current session is blocked, rate-limited, or expired
  • You need to verify behavior as a different user
  • You discover new credentials or create accounts
  • You need to re-authenticate an expired session

mcp__pter__list_emails / mcp__pter__read_email

Read emails for verification codes, OTPs, and confirmation links.

Actions

list_emails

emails = mcp__pter__list_emails(action="list_emails")
# Optional: Gmail search query
emails = mcp__pter__list_emails(
action="list_emails",

query="from:noreply@target.com newer_than:1h", # Gmail search syntax
limit=10
)

get_email

email = mcp__pter__read_email(
action="get_email",

email_id="msg-xxx" # Required
)
# Returns: id, thread_id, subject, sender, to, date, snippet, body_text, body_html, labels

Common use cases:

  • Extracting verification codes during account registration
  • Reading OTPs for 2FA testing
  • Finding confirmation links in signup flows
  • Monitoring for password reset emails

Query examples:

  • from:noreply@target.com newer_than:1h - Recent emails from target
  • subject:verification - Emails with "verification" in subject
  • is:unread - Unread emails only

Search the web for real-time information. Use for exploit research, vulnerability documentation, technology versions, API documentation, and threat intelligence.

results = mcp__tavily__tavily-search(
query="CVE-2024-1234 proof of concept exploit",
search_depth="basic", # "basic" (1 credit) or "advanced" (2 credits)
max_results=5
)

When to use:

  • Finding exploit techniques, PoCs, and blog posts
  • Looking up API documentation for target technologies
  • Checking default credentials for services
  • Researching security advisories and patches

Tips:

  • Default is search_depth="basic" (1 credit) — sufficient for most lookups
  • Only use search_depth="advanced" (2 credits) when basic results are insufficient
  • For CWE/CVE reference data, use mcp__pter-api-server__lookup_reference instead (free, instant)
  • For security knowledge queries, try mcp__pter-api-server__query_knowledge first
  • Be specific in queries: include CVE IDs, technology names, version numbers
  • Do NOT use Tavily for CWE-xxx or CVE-xxx definition lookups — use lookup_reference

mcp__tavily__tavily-extract

Extract and summarize content from a specific URL.

content = mcp__tavily__tavily-extract(
urls=["https://nvd.nist.gov/vuln/detail/CVE-2024-1234"]
)

When to use:

  • Reading full vulnerability advisories
  • Extracting API documentation from a known URL
  • Getting detailed content from search results

mcp__pter-api-server__lookup_reference

Look up a specific CWE, CVE, or other security reference by its exact ID. This is instant and free — always use this instead of web search for known identifiers.

ref = mcp__pter-api-server__lookup_reference(
external_id="CWE-89"
)
# Returns: source, external_id, title, summary, category, content, metadata

When to use:

  • You know the CWE/CVE ID and want its description
  • Looking up a specific vulnerability standard reference
  • Before submitting a finding, to verify CWE classification

IMPORTANT: Do NOT use Tavily for CWE-xxx or CVE-xxx definition lookups — use this tool instead.


mcp__pter-api-server__query_knowledge

Search the encyclopedia for security reference data using semantic search. Searches CWE definitions, CVE records, playbooks, and other reference data.

results = mcp__pter-api-server__query_knowledge(
query="SQL injection authentication bypass techniques",
limit=10,
source="cwe", # Optional: filter to "cwe", "cve", "playbook", or None for all
)

When to use:

  • Researching vulnerability classes or attack techniques
  • Finding relevant CWEs for a discovered vulnerability
  • Looking up remediation guidance

Search priority:

  1. lookup_reference for exact IDs (CWE-89, CVE-2024-1234)
  2. query_knowledge for conceptual searches
  3. query_memories for agent-specific engagement memories
  4. tavily-search only when internal knowledge is insufficient

mcp__pter-api-server__save_memory

Save knowledge to RAG for persistence and sharing with other agents.

mcp__pter-api-server__save_memory(

content="VALIDATED: CWE-89 on /api/users/search. Technique: Time-based blind SQL injection.",
title="SQL injection confirmed on user search", # Optional: short title
memory_type="discovery", # Optional: discovery, learning, warning, error_fix, decision, codebase_knowledge
references=["endpoint://42", "service://5", "finding://17"] # Optional: entity IDs in "type://id" format
# Valid reference types: endpoint, service, technology, flow, account, credential,
# finding, assessment, attack_chain, memory, agent, task
)

mcp__pter-api-server__query_memories

Query saved memories using semantic search.

memories = mcp__pter-api-server__query_memories(
query="SQL injection authentication",
memory_type="discovery", # Optional filter (singular string, not list)
limit=10
)

Common Patterns

Endpoint Discovery → Registration

When you find a new endpoint, check if it exists and delegate registration:

# Check for existing endpoint
all_endpoints = mcp__pter-api-server__manage_endpoints(action="list", service_id=service_id)
existing = [e for e in all_endpoints.get("endpoints", []) if target_url in e.get("url", "")]

if not existing:
# NEW ENDPOINT — delegate to register-endpoint subagent
# Include: method, URL, service_id, auth tokens, discovery context
Agent("register-endpoint", f"Found {method} {target_url} on service_id={service_id}. "
f"Auth: Bearer {token}. Discovered by {how_found}.")
# The subagent investigates, registers, and auto-creates a P4 task

Reflection Pattern (Discovery Audit)

After each phase, audit surfaces and flows discovered:

# For each surface touched — delegate endpoint registration
for surface in surfaces_touched:
all_endpoints = mcp__pter-api-server__manage_endpoints(action="list")
existing = [e for e in all_endpoints.get("endpoints", []) if surface["url"] in e.get("url", "")]

if not existing:
# Delegate to subagent — it handles registration + P4 task creation
Agent("register-endpoint", f"Found {surface['method']} {surface['url']} on "
f"service_id={service_id}. Discovered during reflection audit.")

# For each flow observed
for flow in flows_observed:
all_flows = mcp__pter-api-server__manage_flows(action="list_flows")
existing = [f for f in all_flows.get("flows", []) if flow["name"] in f.get("name", "")]

if not existing:
# NEW FLOW — create flow and spawn investigation task
mcp__pter-api-server__manage_flows(action="create_flow", name=flow["name"], ...)
Agent("register-task", f"P3 flow analysis needed. Phase: 3. Service: {service_name} (service_id={service_id}). Flow: {flow['name']}. Steps observed: {flow['steps']}.")

Task Completion Pattern

When finishing a task:

# 1. Save completion memory
mcp__pter-api-server__save_memory(

memory_type="discovery",
content=f"Task complete: {summary}. Key findings: {findings}"
)

# 2. Mark task done with learnings
mcp__pter-api-server__manage_tasks(
action="update_status",

task_id=TASK_ID,
status="done",
summary=f"Investigated {target}: {result}",
key_learnings=[
f"Technique: {technique_used}",
f"Result: {outcome}"
]
)

Finding Submission Pattern

When you confirm a vulnerability, submit a finding:

# 1. Create the finding with full details
mcp__pter-api-server__manage_findings(
action="create",

# --- 5 REQUIRED fields ---
title="SQL injection in /api/users endpoint",
description="## Summary\n\nTime-based blind SQL injection in...\n\n## Vulnerability Details\n\n...\n\n## Impact Analysis\n\n...",
severity="critical",
affected_components=["endpoint://42"], # Required, non-empty. Format: entity_type://id
report_path="work/docs/exploitation/exploitation_42.md", # Required: path to report file

# --- optional fields ---
cwe_id="CWE-89",
reproduction_steps="1. Send payload... 2. Observe delay...",
recommended_fix="Use parameterized queries",
evidence=[{"type": "request", "command": "curl ...", "response": "..."}],
assessment_id=assessment_id, # Link to the assessment being investigated
)

# 2. Save memory about the finding
mcp__pter-api-server__save_memory(

memory_type="discovery",
content="CONFIRMED: SQL injection in /api/users. Time-based blind technique. Severity: critical."
)

Writing Entity Descriptions

Entity descriptions are the primary way other agents and humans understand what a service, endpoint, or flow actually does. A bad description wastes everyone's time. A good description prevents duplicate work and grounds security analysis.

What belongs in a description

Describe what the entity does functionally — its purpose, inputs, outputs, and behavior. Write for someone who has never seen this application.

EntityGood descriptionBad description
Service"OAuth2/OIDC auth service. Handles login, token issuance, session management. RS256 JWT, 1h expiry. Endpoints: /authorize, /token, /userinfo.""Authentication microservice"
Endpoint"Full-text user search returning paginated results with PII (email, role, last login). Requires any authenticated session. No input length limit on query. Rate-limited to 10 req/min.""User search endpoint"
Flow"Self-service registration: submit email+password → verification email with signed token → click to activate. Creates 'member' role. No admin approval. Email uniqueness at DB level.""Complete user registration flow"

For endpoints, put request/response parameters and types in openapi_schema. Keep description focused on functional purpose, auth requirements, data sensitivity, and security-relevant behavior.

What does NOT belong in a description

Specific discoveries and commentary belong in save_memory(references=["endpoint://42"]). Descriptions are for what the entity is, not what you learned about it.

Updating descriptions

Always read before writing. Call GET first, read the current description, then either:

  • Append genuinely new functional detail you discovered (e.g., rate limits, auth requirements, undocumented parameters)
  • Leave it alone if your update is commentary — use save_memory(references=[...]) instead

Never replace a detailed description with a shorter one. Never use descriptions as a scratch pad.


Important Notes

  1. agent_id is automatic - Your identity is resolved from HTTP headers, no need to pass it
  2. Use list + filter instead of search - Most tools support list action; filter results in memory
  3. Save memories for important discoveries - Other agents can query these later
  4. Check before creating - Avoid duplicate endpoints, flows, etc.
  5. Endpoint creation goes through the register-endpoint subagent - To register a new endpoint, delegate to Agent("register-endpoint", ...) which investigates, documents, and registers with quality enforcement. A P4 task is auto-created.
  6. Submit findings for confirmed vulnerabilities - Use manage_findings with full evidence
  7. Complete tasks with learnings - Always include summary and key_learnings when marking done
  8. service_ids required for P3-P8 tasks - Every task for phases 3-8 must include service_ids. Look up services with manage_services(action="list") first. The tool rejects tasks without it.
  9. Read before updating descriptions - Always GET an entity and read its current description before updating. Never blindly overwrite. See "Writing Entity Descriptions" above.