PerspectiveGraph

MCP serverDev tools

Shows which paths an attacker could take through your network to reach important systems, and which fixes block them.

Unavailable. This server has no hosted endpoint yet, so Ahel can't serve it.

Add to setup to save this item as a reference. Ahel cannot run it, and signing in will not install it.

About this server

Read-only attack-path tools: reachable routes to sensitive assets, and what a fix would cut.

Getting started

  1. Save this item in Your setup as a reference.
  2. Read the source or reference documentation for its setup requirements. Saving it here does not connect it to your AI.
  3. Check this page for availability before trying to install it through Ahel.

From the project's README

As published by luiacuaniello/perspectivegraph in README.md.

Your scanners find issues. This finds the way in.

PerspectiveGraph joins what you already run - Trivy, Semgrep, Cloud Custodian, Falco, plus your AWS and Kubernetes state - into one graph of your real environment, and asks a single question of it:

can someone get from the internet, through privilege that is too broad, to something worth stealing?

On a pull request it asks that question before the merge: the check goes red only when this change opens a route, and the fix comes back as its own pull request. Open source (Apache 2.0), runs on your infrastructure, collects no telemetry.

Twelve seconds of make demo: what is exploitable now → the ranked routes → one route's kill chain and the fix it generates → whether the scores can be trusted. Sample scanner output and seeded verdicts, not a real environment.

  • See it running - the same dashboard, published read-only. Nothing to install.
  • Check your own AWS account - one read-only command, thirty seconds, no deployment.
  • Put it on your pull requests - ten lines of YAML.

A score here is what the model concludes from the evidence it was given, not a measured frequency: nothing has been calibrated against field data yet, and the engine says so itself rather than rounding up. What is measured, and what is not.

Check your own account in 30 seconds

No deployment, no Docker, nothing ingested. One static binary asks AWS's own policy evaluator which of your roles can reach administrator - applying the service control policies, permission boundaries and condition keys that a policy reader on its own does not see:

# macOS (Apple silicon); swap darwin_arm64 for linux_amd64, linux_arm64 or darwin_amd64
curl -sSL https://github.com/luiacuaniello/perspectivegraph/releases/latest/download/perspectivegraph_darwin_arm64.tar.gz | tar xz
./perspectivegraph redteam -roles -region eu-west-1

It is read-only and free: every check is one iam:SimulatePrincipalPolicy call, a dry run that evaluates policy without performing anything, so it creates nothing and costs nothing. It needs iam:SimulatePrincipalPolicy and iam:ListRoles - both inside SecurityAudit. Binaries for linux/macOS (amd64, arm64) and Windows are on the releases page, signed with cosign and carrying SLSA build provenance - two commands verify both before you run anything.

Add -compare and it also runs the engine over the same account and exits non-zero where the two disagree - each disagreement is a false positive or a miss, in the engine or in your assumptions. That check is how the permission-boundary bug described in the manual was found, and how it stays closed.

That command is also stage 0 of a fuller trial: how to evaluate this walks from here to a verdict in stages that each end in an answer - and says what the trial will not tell you before you spend a fortnight finding out.

See the whole engine in 90 seconds

make demo

Pulls the published, cosign-signed images, feeds them sample Trivy / Semgrep / Custodian / Falco / Kubernetes / IAM / SSO output, waits for the analyzer, and prints the top attack path with its generated fix. Dashboard on http://localhost:3000. Needs Docker, jq and curl - no Go or Node toolchain, and nothing is compiled: measured at 23 seconds from an empty image cache. Tear down with make down.

Building it yourself instead is make demo-build, which is the same demo from your working tree. The images the fast path runs are the release artefacts, so you can check what you are about to run before you run it - the cosign verify command is at the top of docker-compose.demo.yml.

Prefer not to build? The release images are published to GHCR (latest also tracks the newest release; the pinned tag is the one to use if you care about reproducibility):

docker pull ghcr.io/luiacuaniello/perspectivegraph:v1.17.0 # x-release-please-version
docker pull ghcr.io/luiacuaniello/perspectivegraph-dashboard:v1.17.0 # x-release-please-version
docker pull ghcr.io/luiacuaniello/perspectivegraph-postgres:v1.17.0 # x-release-please-version

On Kubernetes, the Helm chart is published the same way - no clone needed, and a version you can pin and verify. It is listed on Artifact Hub as an official package from a verified publisher - Artifact Hub's way of saying it comes from the people who wrote the software rather than a third party repackaging it:

helm install perspectivegraph oci://ghcr.io/luiacuaniello/charts/perspectivegraph \
  --version 1.17.0 # x-release-please-version

Images and chart are signed with cosign keyless and carry an SPDX SBOM plus a SLSA build provenance attestation - verify them rather than taking the supply chain on trust.

The dashboard opens on the decision, not the inventory: what is being exploited right now, the fewest changes that remove the most risk, and how much the numbers can be trusted.

Routes are ranked by a composite triage priority - what the route reaches, whether runtime confirmed it, how exposed the entry is - not by raw exploit score, so a lower-scoring route can and does outrank a higher-scoring one.

Every hop, its probability, where that probability came from, and the ATT&CK technique.Whether the engine's own scores held up against recorded outcomes.

Every screenshot on this page is make demo, signed in with a token: sample scanner output and seeded verdicts, not a real environment. That is why the calibration panel returns a verdict of "underconfident" - across 14 seeded outcomes the engine predicted 60% where 71% held up. Those outcomes were generated to exercise the instrument, not to flatter it. On a fresh install the same page reads "insufficient data" and withholds a verdict until real outcomes exist, because a risk score you cannot check is worth less than an honest blank. The public demo at demo.a3thinker.it is this same dashboard with no seeded verdicts at all, so its Trust page reports insufficient data; it runs on a single free VM, so treat it as best-effort - the badge at the top says whether it is up.

Why?

Modern security teams don't suffer from a lack of tools - they suffer from noise, fragmentation and missing context. A scanner reports that a container carries a critical CVE. It cannot report that the container sits behind an internet-facing load balancer, runs with a role that reads the production database, and is therefore the one finding out of ten thousand worth doing something about this week. That second question needs the other tools' output in the same graph, which is what this builds.

RolePain todayWhat PerspectiveGraph gives them
DeveloperCI/CD blocked by thousands of irrelevant CVEsA PR check that goes red only when the change opens a real internet→sensitive-asset path - plus the fix as a one-click PR
SecurityTriage on flat lists of 10,000 findingsA ranked list of ~5 critical attack paths, queryable like a database
ArchitectNo live view of how IaC becomes attack surfaceAuto-generated, always-current architecture & data-flow maps + drift detection

It answers that question in the developer's workflow rather than in a console someone logs into afterwards: the reachable path is caught and closed in code review, where it is cheapest, not months later in production. This is shift-left attack-path analysis - not a scanner bolted onto CI, and not a runtime CNAPP you log into after the fact.

The gate is powered by a full attack-path correlation engine, so the same graph also gives you the rest: a queryable dashboard of your ~5 critical attack paths (not 10,000 flat findings), triage, runtime confirmation, an AI summary, and always-current architecture maps. But the wedge is the pull request.

Block the pull request that opens the path

No deployment required. The runner reads your estate read-only, ingests this pull request's scan, and answers in-process with the same engine:

- uses: luiacuaniello/perspectivegraph@v1
  with:
    mode: local
    aws-region: eu-west-1     # read-only; give the job an OIDC role with SecurityAudit
    report: trivy.json

The scan is not the only thing a pull request can send: a rendered manifest set (helm template, kustomize build) posted to /ingest/k8s?slug=&sha= attributes the objects it contains to that commit, so a change that publishes a Service fails the check the same way a vulnerable dependency does. That matters because manifests are how most routes actually open.

An estate is not optional, and that is the point: without one there are no attack paths, only a flat list of findings - the thing this replaces. If you collect your estate on its own schedule, pass estate: estate.json (what perspectivegraph awscollect -json writes) instead of aws-region.

Already running the engine? Point at it and it keeps the graph across pull requests, plus triage, history and the dashboard:

- uses: luiacuaniello/perspectivegraph@v1
  with:
    api: https://perspectivegraph.internal
    ingest: https://perspectivegraph.internal:8081
    report: trivy.json
    hmac-secret: ${{ secrets.PG_INGEST_HMAC }}

Both modes run the same normalizer, the same pathfinder and the same triage priority, and return the same verdict - a test asserts they agree path-for-path on identical input.

The check goes red when this commit puts a sensitive asset within reach. Not when it adds a critical CVE - a critical on a host nothing routes to does not fail the build, and a medium on a container that now reaches the production database does. That is a different question from the one your other scanners answer, and answering it needs a live estate, which is why the action talks to a running engine instead of scanning the runner.

It has three outcomes, and the third is the point. Every two-state gate ever written gives a pipeline whose scanner output never arrived the same green tick as one that is genuinely clean. Here that is unknown, and it fails the build by default:

VerdictExitMeaning
clean0The engine analysed this commit and found no path through it
blocked1Critical attack paths run through it - the check names them
unknown2Nobody analysed it. The scan, the ingest or the SHA is wrong

Set allow-unknown: true while you roll the gate out. Leaving it on afterwards turns a broken ingest back into a green check, which is the one thing this gate is for.

The same thing without GitHub Actions - the action is a thin wrapper over one command:

perspectivegraph gate -local -aws-region eu-west-1 \
  -report trivy.json -slug owner/name -sha "$COMMIT_SHA"

Two things to settle before wiring it up.

Fork pull requests. The gate needs secrets, and GitHub gives a fork's pull_request run none - so a fork PR cannot be analysed and fails closed as unknown. Do not reach for pull_request_target to work around it: that event runs with your secrets against the contributor's code, and in local mode your secrets are cloud credentials. Run the gate on push to your own branches instead, and let fork PRs go without it.

Public repositories. When it blocks, the check prints the route - real asset names, the CVE linking them, the sensitive asset at the end - into the job log and summary, which on a public repository are public. Use soft-fail and post the detail somewhere private, or keep the gate on a private repository.

Full input reference in action.yml; the underlying query is prVerdict in the API schema.

Let an agent query it

A language model is weak at exactly what this engine is good at: it cannot enumerate fourteen thousand edges reliably, it does not run Dijkstra, and asked for "the attack paths in my account" it will produce plausible routes that do not exist. So the engine speaks MCP - an agent calls it and reasons over answers it could not have invented.

make mcp    # or: perspectivegraph mcp --api http://localhost:8080

No engine running? Point it at the live demo instead: perspectivegraph mcp --api https://demo.a3thinker.it answers every tool - simulate_fix included - from the sample data, with no credential.

{"mcpServers": {"perspectivegraph": {
  "command": "perspectivegraph",
  "args": ["mcp", "--api", "http://localhost:8080"]}}}

Eight tools: get_posture, list_attack_paths, explain_attack_path, routes_to_target, list_fixes, simulate_fix, search_assets, get_score_trust. The one worth the integration is simulate_fix - it re-runs the whole simulation with the given edges cut and reports what actually changes, settling "would this help" with a deterministic counterfactual instead of an argument.

The surface is read-only: nothing suppresses a path, opens a PR or records a verdict, because an agent that can silently accept a risk is a liability rather than a feature. Every tool declares that on the wire (readOnlyHint), so a host can decide what to run unattended without taking this paragraph's word for it - and a test fails if a tool is ever added without that decision. The descriptions also tell the model the scores are expert estimates, and to call get_score_trust before quoting one as a probability. Every tool is also run against the real engine in the test suite, not only a stub, so a query naming a field the schema lacks fails the build instead of an agent's call. search_assets asks the engine whether full-text search is on: without OpenSearch an agent is told so, instead of receiving an empty result that reads as "no asset by that name".

The server is in the official MCP Registry as io.github.luiacuaniello/perspectivegraph, published from every stable release, and on Glama, which builds it, inspects the tools it exposes and grades their definitions - a grade of the MCP surface, not of the engine's scores:

Project status & maturity

The short version, if you read nothing else. The engine and its public API are complete, documented and tested, and the AWS connector is verified against a real account. What is not done is calibration: the path scores, and the order they produce, have not been graded against real exploited outcomes. Nobody has yet run this over a real estate, tested the paths it surfaced and fed the verdicts back. The machinery for that loop is built and tested; the loop is not closed.

So read a score as what this model believes, and how sure it says it is, not as a measured frequency. A path at 0.7 has not been shown to be exploited seven times in ten - it is what the model concludes from the evidence it was given, with an interval beside it saying how thin that evidence is. On a fresh install the calibration page reports insufficient data and withholds a verdict, because a risk score you cannot check is worth less than an honest blank. Use this to find and cut routes; don't put its percentage in front of a board. What is and isn't claimed is spelled out in positioning. It collects no telemetry: out of the box it opens no outbound connection at all - GitHub, the AI assistant and the KEV/EPSS feeds each stay dark until you set a key or a flag (THREATINTEL is off by default).

What is measured today, as of v1.17.0. Two things, both reproducible without taking anyone's word for them.

make bench-cloudgoat runs four CloudGoat-shaped scenarios in CI and grades the engine on each:

ScenarioExpectsResult
ec2_ssrfa pathfound it, invented none
iam_privesc_by_attachmenta path (leaked-credential origin)found it, invented none
ec2_private_subnet_no_pathno path (open SG, private subnet)produced none
iam_privesc_denied_by_guardrailno path (explicit Deny wins)produced none

Precision and recall are 1.00 on all four. Read that for what it is: four scenarios, two of them negative controls - a regression gate against known shapes, not a measurement of field accuracy on your estate. On real AWS, make reachability-lab-aws makes the same kind of check for free: two instances behind one wide-open security group, only one routed to an internet gateway, and only that one may be reported as exposed.

make redteam-aws grades the engine's privilege-escalation claims against AWS's own policy evaluator - read-only, free, and it applies the service control policies, permission boundaries and condition keys the engine's policy reader skips. That grading has already paid for itself: it caught the engine reporting an escalation a permissions boundary made impossible. The bug is fixed, and make boundary-lab-aws is now its regression test - engine and AWS side by side on a real account, exiting non-zero when they disagree in either direction. It deliberately does not rescale the path scores, and the manual explains why it cannot: those verdicts are one-sided, and a censored sample is not a measurement.

The long version. PerspectiveGraph is 1.x and in active development, built in the open. What is next - and read this before you rely on it:

  • Engine: feature-complete. Correlation, agentless connectors, triage, SSO, the merge gate, the AI assistant and the scale work are implemented and covered by tests. The GraphQL schema is frozen and drift-guarded, so a breaking change goes through a major version rather than arriving in a patch - API stability policy.
  • Clouds: AWS is live, Azure is fixtures only, there is no GCP connector. The connector, its read-only SecurityAudit grant, cross-account AssumeRole and the network↔identity join (instance --ASSUMES--> role) are verified against a real account - that last edge was a gap only real-account testing exposed. Closing the calibration loop on your own estate is what make validate-aws and the evaluation guide are for.
  • Deployment: demo-grade defaults, with a production switch. The backend is hardened wherever it runs (distroless, non-root, read-only rootfs, digest-pinned 0-CVE images, opt-in TLS) and under Helm every workload satisfies the restricted Pod Security Standard, asserted in CI. The compose defaults are deliberately open; PG_ENV=production makes the backend refuse to start unless API and ingest are both authenticated. A real rollout needs more - external PostgreSQL+AGE (managed only on Azure), secrets in a manager, TLS, backups, and TRUSTED_PROXY_CIDRS set, or per-IP limits key on your proxy for everybody: operations runbook, SECURITY.md, threat model.
  • Support: the newest release, and nothing behind it. No backports, no LTS branch - at six minor releases in the eight days after 1.0, a maintenance branch is a promise one maintainer breaks. Promised instead: a clock on security fixes (Critical 7 days, High 30) and an upgrade specified rather than hoped for. SUPPORT.md is the policy.
  • Scope. It answers the reachable attack-path question in the developer workflow. Not a scanner, a CNAPP or a compliance product, and it replaces none of them.
  • How it is written. Developed by a human working with Claude (Anthropic): the design decisions and what ships are the maintainer's, a large share of the implementation and its tests came out of that collaboration. Said plainly for the same reason the engine reports its own calibration - a claim you can check beats one you have to accept: make test, make bench-cloudgoat, govulncheck ./.... See CONTRIBUTING.

Issues and PRs are welcome. Nothing here is claimed beyond what the tests and the listed validation cover.

Documentation

The manual is the full reference: the scoring model, every integration, deployment, hardening and the runbook for pointing it at your own environment.

Verify the claims rather than taking them: make test, make bench-cloudgoat (precision/recall against known-vulnerable scenarios), govulncheck ./....

License

Apache License 2.0.

Signals

GitHub stars
9
Forks
2
Last commit
Oct 2026
Advanced
Delivery
perspectivegraph MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-luiacuaniello-perspectivegraph
Source
github.com/luiacuaniello/perspectivegraph