Skip to main content
Glama
tarhou
by tarhou

Chokepoint Finder MCP Server

The engine behind the Chokepoint Finder method: a FastMCP server with ten tools that turn thousands of findings into the smallest set of remediation actions that eliminates the largest share of risk, with fail-closed pre-flight safety gates, typed execution plans, and verification that refuses to report clean when it could not actually look.

Part of the Chokepoint Finder family: chokepoint-finder (the agent) · chokepoint-finder-skill (the portable method) · chokepoint-finder-playbook (the cross-vendor chain).

Quickstart (zero credentials)

uv sync
uv run python -m chokepoint_finder.demo     # the full loop in the terminal
uv run python -m chokepoint_finder.report   # a board-ready HTML report
uv sync --extra dev && uv run pytest        # 167 tests

The demo estate is deterministic (seeded): every run shows the same collapse — 3,734 findings across 783 assets reduced to 7 actions covering 74% of weighted finding risk (1,700 findings) and severing all 22 known attack paths — plus a pre-flight refusal and a wave-by-wave simulated delta. Synthetic data, reproducible numbers, labelled SIMULATED in every report it produces. The demo can prove the mechanics but can never authorize a real change-record close.

Related MCP server: runtime-guard

The ten tools

chokepoint_setup (guided plumbing, never asks for a secret in conversation, defaults to the offline source so it cannot hang on a captive portal), chokepoint_ingest (demo | tenable | aws | all), chokepoint_rank (greedy weighted max-coverage, marginal ranking), chokepoint_supply_evidence (relay EDR or change-freeze evidence in from another connected MCP server; coverage that could clear a gate requires explicit human confirmation), chokepoint_preflight (EDR silence, change freezes, blast radius → PROCEED / REQUIRES_STAGING / HOLD_PARTIAL / HOLD), chokepoint_payload (CAB-ready change request), chokepoint_plan (typed execution workflow routed to your MCP fleet, per-step approvals, canary-first waves, CTI out-of-band handoff; pass json_output=true for the complete canonical v3 manifest), chokepoint_mark_executed (requires a matching plan hash, explicit confirmation, the exact wave, a targeted-rescan digest, and a recent one-time approval receipt), chokepoint_verify (fail-closed, plan/wave-bound delta; pass json_output=true for the structured proof receipt), chokepoint_demo (the whole wave-aware loop at once).

Five tools update only this process's session state: ingest, rank, evidence relay, execution accounting, and verification. None holds credentials for, or calls, an external write API. Planning, payload generation, setup, and the complete demo remain read-only at the MCP annotation boundary.

{
  "mcpServers": {
    "chokepoint-finder": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/chokepoint-finder-mcp", "chokepoint-mcp"]
    }
  }
}

The safety model

Three properties, each enforced in code and asserted by tests in tests/test_failclosed.py rather than promised in prose.

Evidence that is missing is not evidence of safety. Every gate reads typed Evidence carrying its source, collection time and completeness. Evidence that is unconfigured, failed, partial, truncated or stale reads as UNKNOWN, and UNKNOWN produces HOLD. Forgetting to wire up a feed can only ever make the agent less willing to act. An empty result from a healthy feed is a different thing — ABSENT — and does clear the gate.

Evidence composes, the same way execution does. Chokepoint Finder holds no EDR, SIEM or ITSM credentials and never will. It sees that telemetry anyway: your agent queries whatever server provides it — CrowdStrike, SentinelOne, Splunk, Sentinel, ServiceNow, a CTI platform — and relays the answer through chokepoint_supply_evidence with its provenance intact. The trust rules do not soften for relayed data: stale, partial or truncated input still reads UNKNOWN and still holds, a future collection timestamp is rejected outright, and every verdict built on relayed evidence prints [RELAYED by the operator's agent] next to its source. Evidence that travelled through an agent is one link weaker than a direct pull, and a human reading a HOLD deserves to know which one they are looking at. The failure mode that matters — an agent queries an EDR, gets an error, and relays nothing — leaves the channel UNKNOWN, so giving up produces a refusal rather than a silent proceed. Relayed evidence also carries an exact covered_asset_ids set. Free-text scope is audit context only; if any proposed target is absent from that set, the gate holds. The confirmation rule is asymmetric: adding detections or freezes inside coverage the gate already has needs no confirmation because it can only make the decision more cautious. Establishing, refreshing, replacing, or extending coverage can turn a HOLD into PROCEED. The first call therefore refuses with a SHA-256 preview digest. The agent must present the exact evidence and digest to a human, then retry with confirm=true, that digest, a recent confirmer identity/time, and a one-time receipt ID; it may not self-attest. Changing the payload changes the digest, and replaying the receipt is refused.

Honest relay limitation. The same agent is still the transport. A human who confirms a relay is attesting to the evidence the agent handed them, not to a direct read of the EDR, SIEM, or ITSM console. confirm=true does not make the relay an authoritative pull. What it buys is a deliberate control boundary: fabrication that could widen action now requires active human affirmation instead of happening silently inside one tool call.

Verification re-queries the authoritative source and fails closed. chokepoint_verify runs a fresh collection and grades it: VERIFIED, PARTIAL, NOT_FIXED, UNKNOWN or ERROR. Auth failure, timeout, malformed payload, truncation, partial collection and stale evidence all resolve to UNKNOWN/ERROR, and only a non-simulated, final-wave VERIFIED result permits closing a change record. Verification also requires the re-query to match the baseline tenant/account, credential principal, query and region scope. An empty result set against a non-empty baseline is treated as a broken query unless the approved action was expected to retire the entire baseline and the collector independently proves complete, exact asset coverage. This prevents an outage from masquerading as a fix without making a genuine all-clear impossible. In demo mode the re-query is a simulation, says so in its status line, and can never authorize closure.

Every external mutation requires approval bound to the exact plan. Steps carry a structural mutates_external_state flag, and the Step constructor refuses to build one that mutates without requiring approval. "External" is drawn deliberately wide: opening a change ticket, notifying a SOC, and launching a scan all count, not just touching infrastructure. Approval binds to ExecutionPlan.plan_hash; change the source scope, complete finding IDs, complete targets, exact mutation args, ordered waves, or held assets and the full SHA-256 hash changes, voiding the earlier approval. Each wave must be recorded and verified before the next becomes eligible. The server holds no write credentials and calls no write API — execution happens through the operator's own MCP servers, as a separate human-invoked phase. The human Markdown is intentionally bounded and omits bulk identifier arrays; the full v3 JSON manifest carries exact identifiers, mutation arguments, and ordered steps. For a three-wave plan, target mutation, targeted rescan, execution receipt, and proof are physically interleaved for wave 1 before any wave-2 target is touched. Proof binds to the approved plan hash, exact wave, authoritative scope, and rescan receipt.

Attack-path credit is also evidence-bound. A fix severs a path only when the source explicitly maps that remediation target to the path; mere overlap between a target asset and a path node is not counted as disruption.

How this is tested

167 tests, of which the load-bearing ones are in tests/test_failclosed.py: they are written to fail if a safety claim stops being true, which is the only reason a safety claim is worth making.

Each control was verified by reverting it and confirming the test catches it — a regression test that passes against the broken code proves nothing. That exercise found real gaps twice: an injection test that only checked the sanitiser rather than the call sites, and an AWS fixture whose STS branch was never reached.

What the suite covers: every way a re-query can fail (auth, timeout, malformed payload, truncation, staleness, wrong scope) resolving to UNKNOWN or ERROR and never to clean; unconfigured, stale or under-scoped evidence holding a gate; approval bound to a plan hash and refused when the plan changes; wave ordering that cannot skip ahead; coverage arithmetic that cannot exceed 100%; finding identity stable under reordering; and prompt-injection payloads driven through the real MCP tools — not the sanitiser in isolation — asserting no rendered line can become a model instruction.

The attack-path collector was additionally verified against a live Tenable One tenant: 2,285 vectors, 24 distinct remediations, with partial coverage and unbound vectors counted and surfaced rather than silently dropped.

The ranking, and what is actually guaranteed

The library default (ranking_mode="risk", with both early stops disabled) is greedy on marginal objective gain under a cardinality constraint. The objective — covered finding risk plus a bonus per newly severed attack path — is monotone and submodular, so the classical (1 − 1/e) ≈ 63% approximation bound applies (Nemhauser, Wolsey & Fisher 1978), and no polynomial algorithm does better unless P = NP (Feige 1998).

The MCP shortlist intentionally uses an early target-share stop and prints that the classical guarantee does not apply to that run. More generally, the early stops (target_share, min_marginal_share) trade that k-cardinality bound for a shorter list, and the bound is over the objective, not over any single reported percentage.

ranking_mode="effort_weighted" divides marginal gain by effort^exponent. It is useful and it carries no approximation guarantee — ratio-greedy under a cardinality constraint can be arbitrarily bad. It is opt-in and named honestly rather than sold under a theorem it does not satisfy.

Coverage is reported as two numbers, never blended. Finding-risk coverage has total finding risk as its denominator; attack-path disruption has the path count. Mixing them is how a tool reports more than 100% of something, so the code keeps the path bonus out of every human-facing percentage.

Live sources

Tenable Vulnerability Management via the export APIs (uv pip install ".[tenable]", TENABLE_ACCESS_KEY / TENABLE_SECRET_KEY), and AWS via Security Hub + IAM + EC2 security groups (uv pip install ".[aws]", standard credentials, read-only, SecurityAudit suffices). Copy .env.example to .env, or use the environment. Run chokepoint_setup first: it reports READY / TO DO / FAILED per source with the exact next step.

Finding identity is derived from source-natural keys — (asset uuid, plugin id, port/protocol) for Tenable, and the Security Hub finding ID plus every sorted resource and vulnerability identity for AWS — never from enumeration order, so a re-query that returns rows in a different order still compares correctly against the baseline.

Limitations

These are the things this tool does not do. They are listed because a security tool that hides its edges is worse than one that has them.

  • The demo estate is synthetic. Its numbers are illustrative, not benchmarks.

  • No bundled EDR/SIEM/ITSM collector, by design. Evidence comes from the MCP servers you already have connected, relayed in through chokepoint_supply_evidence (see above). This tool holds no credentials for CrowdStrike, Splunk or ServiceNow and is not trying to. The consequence to be aware of: if nobody relays a channel, it stays UNKNOWN and the gates hold. Refusing is the safe direction, but an unattended run against live data will refuse a lot until the evidence path is wired up.

  • Attack paths need Tenable One APA. The Tenable collector reads APA vectors -- what Tenable calls attack paths -- paginating to the tenant's reported total, and binds each to the plugin APA says severs it. Deliberately not the findings endpoint: a finding is a technique that can occur in many paths (one observed finding sat on 39 vectors), so counting findings as paths yields a denominator that is neither the path count nor a stable quantity. A path is credited only when APA names the remediation that breaks it; node overlap earns nothing. Without an APA licence -- and on the AWS collector, which has no equivalent surface -- zero paths are collected and the reason is recorded, rather than reported as "this estate has no attack paths". Unbound vectors, vectors referencing assets outside the ingest, and any short read are counted and surfaced, so partial coverage is never presented as complete.

  • Collectors are read-only and not load-tested against very large tenants; exports are capped, and hitting the cap marks the collection truncated, which blocks verification rather than degrading it silently.

  • Ranking quality tracks metadata quality. Findings with no fix-sharing key cannot converge into a chokepoint. Garbage in, defensible out — every ranking cites the evidence behind it, so a wrong answer is visible rather than opaque.

  • HTTP is loopback-only development transport. It has no authentication or session isolation and uses one process-global state, so the CLI refuses non-loopback binds. Use stdio for real operator/tenant boundaries and a separate process per session.

  • Finding text is untrusted input. Titles and remediation strings are normalized into bounded labels or escaped <untrusted-scanner-data> JSON, never interpreted as instructions; prompt-injection tests exercise the model-facing rendering boundary.

Authors: Zane K (@zkilling), Tarek H (@tarhou). MIT license.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    Enables security auditing, penetration testing, and compliance validation with tools like Semgrep, Trivy, Gitleaks, and OWASP ZAP. Features strict project boundary enforcement and supports OWASP, CIS, and NIST compliance frameworks.
    Last updated
    7
  • A
    license
    A
    quality
    C
    maintenance
    Blocks dangerous operations: rm -rf, sensitive file access, privilege escalation, and more are denied before execution. Gates risky commands behind human approval (optional): configurable commands require explicit operator sign-off via a web GUI before the agent can proceed. Simulates blast radius: wildcard operations like rm \*.tmp are evaluated against real files before running, and blocked if th
    Last updated
    8
    15
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Blocks dangerous operations — rm -rf, sensitive file access, privilege escalation, and more are denied before execution. Gates risky commands behind human approval Simulates blast radius — wildcard operations like rm \*.tmp are evaluated before running Automatic backup of files Full audit of commands
    Last updated
    8
    15
    MIT

View all related MCP servers

Related MCP Connectors

  • Threat modeling, code/cloud/pipeline scanning, shadow-AI discovery, compliance checks and fixes.

  • Deterministic trust gate for AI output: leaked-secret, prompt-injection & PII in one call.

  • Runtime AI governance: decision gates, human approval, hash-chained audit, compliance mapping.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tarhou/chokepoint-finder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server