Skip to content

Managed assessment and real acceptance ​

Pentest MCP is the assessment control plane. Codex, Claude, Daybreak Blue/Red, and API models are replaceable reasoning engines. A managed run passes only when the model uses successful Pentest MCP assessment tools and the evidence is imported into the parent engagement.

Context boundaries ​

ContextReceivesDoes not receive
Scope authorClient authorization and rules of engagementAssessor transcript or application builder context
Assessment workerOne engagement, one plan, full MCP catalog, exact allowed tool listBuilder repository, unrelated home files, other engagements, report writer history
Report writerBounded scope, plan, evidence summaries, hashes and record IDsScanner MCP, assessor transcript, builder repository, raw output by default
Analyst reviewerDraft plus cited source recordsAutomatic authority to promote model candidates

Container Codex runs in a pinned image with a temporary home/work filesystem and a one-connection bridge to the host MCP child. Shell, browser, computer, apps, plugins, image generation, multi-agent use, and web search are disabled. The bridge child has one seeded engagement and checks the authoritative parent before every call.

A dedicated-os-account profile runs local codex exec or claude -p with a fresh private home and working directory. The field is an operator attestation; use an actual separate OS account when the host filesystem boundary matters. Claude receives an explicit one-server MCP configuration and only mcp__pentest__* tools. SSH assessment profiles remain unavailable until remote scope seeding and evidence import are implemented.

An openai-compatible or openrouter assessment profile uses isolation: "guarded-mcp". The model sees only the engagement plan's exact tools and every call enters the same locked worker. It receives no shell or filesystem capability from Pentest MCP. This protects the tool boundary; it does not sandbox a locally running model-server process. See guarded local and OpenRouter models for profiles, limits, audit receipts, and operating system isolation.

Profile examples ​

Local Codex:

json
{
  "id": "local-codex-assessor",
  "kind": "codex",
  "mode": "assessment",
  "model": "gpt-6.1-sol",
  "reasoningEffort": "xhigh",
  "command": "/absolute/path/to/codex",
  "isolation": "dedicated-os-account",
  "workingDirectory": "/private/empty/work",
  "agentHome": "/private/agent/home",
  "mcpDataDir": "/private/worker/data",
  "mcpNodeCommand": "/absolute/path/to/node",
  "mcpServerEntry": "/absolute/path/to/pentest-mcp/dist/index.js"
}

Local Claude uses the same fields with "kind": "claude", its Claude model name, and an absolute claude command.

Container Codex replaces command with:

json
{
  "isolation": "container",
  "bridgeRoot": "/private/docker-shared/bridge",
  "dockerCommand": "/absolute/path/to/docker",
  "dockerContext": "colima-pentest-assessment",
  "containerImage": "pentest-mcp-assessment:codex-0.158.0"
}

A report profile uses "mode": "draft" and separate home/work paths. It receives the evidence packet over stdin and no assessment bridge.

A loopback llama.cpp-style assessment profile uses these core fields:

json
{
  "id": "local-guarded-assessor",
  "kind": "openai-compatible",
  "mode": "assessment",
  "isolation": "guarded-mcp",
  "hostileModelProtection": false,
  "model": "exact-model-id-from-v1-models",
  "endpoint": "http://127.0.0.1:8081/v1/chat/completions",
  "workingDirectory": "/private/empty/mcp-worker-cwd",
  "mcpDataDir": "/private/worker/data",
  "mcpNodeCommand": "/absolute/path/to/node",
  "mcpServerEntry": "/absolute/path/to/pentest-mcp/dist/index.js",
  "maxIterations": 16,
  "maxTotalTokens": 131072,
  "maxToolCalls": 24,
  "maxCallsPerTool": 16,
  "maxConsecutiveToolErrors": 3,
  "maxTotalToolErrors": 6,
  "toolResultMaxBytes": 65536
}

Use kind: "openrouter" and OPENROUTER_API_KEY for an approved OpenRouter route. Its endpoint defaults to the official chat-completions URL.

Real scanner readiness ​

Install the scanner binaries required by the plan. getRuntimeStatus validates ProjectDiscovery HTTPX and rejects the unrelated Python HTTPX command. On Apple Silicon Homebrew, Pentest MCP checks /opt/homebrew/opt/httpx/bin/httpx before PATH so an existing Python command does not need to be overwritten.

Nuclei remains unavailable for an engagement until MCP_NUCLEI_TEMPLATES_DIR points to a real operator-reviewed template directory. Installing the upstream template repository alone does not approve every template for client use.

Real acceptance command ​

Build first. Supply every target and authorization value explicitly:

bash
npm run build

node scripts/run-real-acceptance.mjs \
  --data-dir /absolute/private/evidence-directory \
  --engagement-id operator-chosen-id \
  --authorization-reference signed-or-recorded-approval-reference \
  --host 127.0.0.1 \
  --url http://127.0.0.1:8080/health

This direct acceptance runs real Nmap and ProjectDiscovery HTTPX through MCP and produces a deterministic draft. To prove model-selected MCP execution and separate report writing:

bash
PENTEST_AGENT_PROFILES_FILE=/absolute/private/profiles.json \
node scripts/run-real-acceptance.mjs \
  --data-dir /absolute/private/evidence-directory \
  --engagement-id operator-chosen-id \
  --authorization-reference signed-or-recorded-approval-reference \
  --host 127.0.0.1 \
  --url http://127.0.0.1:8080/health \
  --profile daybreak-blue-assessor \
  --report-profile daybreak-blue-reporter \
  --timeout-seconds 900

The verifier fails unless runtime identity checks pass, both scanner records succeed, the managed profile's toolCounts contains nmapScan and httpxProbe, and the saved report draft identifies the requested report profile. It stores evidence under --data-dir; no receipt is copied into the npm package.

Verified local vertical slice, 2026-10-25 ​

An authorized loopback run against the installed local Faxbot health route completed with Daybreak Blue at xhigh:

  • assessment run ff193ff0-58c3-4491-8990-a1f1a29f0895;
  • real Nmap record rec-06697543-c1de-45d2-acf8-dba15a1a344c;
  • real HTTPX record rec-e105d0f3-9946-47a3-836e-30d662132772;
  • 40 imported worker records and a full used/skipped tool coverage table;
  • separate report profile daybreak-report-real using gpt-daybreak-blue-latest;
  • report prompt report-evidence-v4, six evidence items, four cited observations, zero candidate findings, three recommendations, and seven limitations;
  • draft record rec-48c49b0c-c78c-43d7-b378-7b85ace55841.

The model also submitted one malformed research-note target containing a host and URL in one string. The scope gate denied it and no target interaction occurred. The agent corrected the note and continued. The receipt remains in the private data directory outside the repository.

This proves real MCP discovery, real scanner dispatch, durable logging, scope denial, evidence import, context-separated report generation, citation validation, and restart-readable storage for this exact loopback target. It does not establish a Faxbot vulnerability, coverage beyond port 8080 and the exact health URL, remote-node authorization, Internet egress confinement, or provider rights for another client's data.

Recovery and audit behavior ​

The campaign state schema uses compare-and-swap revisions, action idempotency keys, bounded retry counts, and terminal states. An action left in_progress after a restart becomes blocked: ambiguous-after-crash; it is not silently replayed. runAgentAssessment also inspects durable start and terminal receipts. An unresolved interrupted run blocks a new worker until an operator calls reconcileAgentAssessment with stopped-process confirmation, reviewer identity, and rationale; reconciliation revokes the prior worker scope when it remains available. With hostileModelProtection: true, the worker rejects an identical successful assessment tool call and records the duplicate denial; the option defaults to false. Four no-progress observations require a strategy reflection.

Every MCP call records start and completion receipts with argument digests, correlation IDs, timing and outcome. Scope gates record allowed and denied decisions. Guarded API runs additionally record run boundaries, prompt and schema digests, request and response digests, requested and reported models, finish reason, provider token counts when returned, pre-MCP denials, and all stop counters. Scanner evidence stores a retained-output hash. Imported records preserve the worker record ID in their invocation metadata. The report draft stores prompt version and digest, context packet digest, model/provider identity, response digest, cited evidence IDs, and analyst-review status.

Remaining deployment boundaries ​

The host scanner process retains the host account's filesystem and network reach. For external clients, pair the MCP scope gate with a dedicated host identity and outbound ACLs. A CLI container isolates its model process; it does not by itself constrain scanner egress from the host bridge child. A guarded API loop constrains model-requested MCP actions but cannot restrict a separately launched local model server that already has broad host permissions. Keep report homes separate from assessment homes, rotate model credentials, and keep raw evidence in an owner-scoped private directory.

Released under AGPL-3.0-or-later. Use only within explicit authorization and scope.