Get started

Scan your first agent in two minutes.

Install ABOM, then produce a signed bill of materials from local code, a GitHub repo, or a live MCP endpoint — run the MCP gateway for signed, verifiable evidence on every tool call, and turn on blocking when you opt in.

$pip install abom-cli
1

Install

ABOM is an open-source CLI on PyPI. Requires Python 3.10+.

shell
pip install -U abom-cli
...
Successfully installed abom-cli-0.2.0

Confirm it's working — every command opens with the ABOM banner:

shell
abom version

   ╭───────╮    ▄▀█ █▄▄ █▀█ █▀▄▀█
   │   ✔   │    █▀█ █▄█ █▄█ █░▀░█
   ╰──╮ ╭──╯    Agent Bill of Materials  v0.2.0
      ╰─╯
  a signed record of what your AI agent is, what it can do, and what it did

abom 0.2.0 · ABOM spec v0.1 (manifest) + v0.2 (gateway, draft)
Tip: if you installed with pipx, add the MCP support with pipx inject abom-cli httpx. See troubleshooting if a command can't find a module.

In a hurry? Scan an agent and run the /authorize API in two commands — your agent asks before it acts and gets back a verdict plus a signed receipt:

shell · the /authorize API
abom scan . -o abom.json          # signed manifest
abom serve abom.json              # POST /authorize on :8088

curl localhost:8088/authorize -d '{"tool":"wire_transfer"}'
  { "verdict": "deny", "rule": "tool_not_in_manifest",
    "receipt": { "inclusion_proof": {…}, "head_signature": {…} } }

# run it metered (Bearer keys + per-key /usage):
abom keys issue --label acme
abom serve abom.json --keys .abom/keys.json
2

Scan a local agent what it's made of

Point abom scan at any agent project. It detects the models, tools, prompts, data sources, frameworks and MCP servers it's built from — each with the exact DETECTED FROM reference — and emits a signed manifest.

Here's a minimal example agent — a few dependencies, some source, and a system prompt:

shell
tree abom-demo
abom-demo
├── agent.py
├── prompts
│   └── system.txt
└── requirements.txt

2 directories, 3 files
requirements.txt
langchain==0.2.16
langgraph>=0.2.0
openai==1.51.0
anthropic
chromadb~=0.4.22
guardrails-ai
agent.py
"""A tiny customer-support agent."""
from langchain.chat_models import init_chat_model

SMART_MODEL = "gpt-4o"
FAST_MODEL = "claude-3-5-sonnet-20241022"


def lookup_order(order_id: str) -> dict:
    """Look up an order by id."""
    return {"id": order_id, "status": "shipped"}


def issue_refund(order_id: str, amount: float) -> dict:
    """Refund an order."""
    return {"id": order_id, "refunded": amount}
prompts/system.txt
You are a helpful customer-support agent. Only answer order questions.

Scan it:

shell
abom scan .

   ╭───────╮    ▄▀█ █▄▄ █▀█ █▀▄▀█
   │   ✔   │    █▀█ █▄█ █▄█ █░▀░█
   ╰──╮ ╭──╯    Agent Bill of Materials  v0.2.0
      ╰─╯
  a signed record of what your AI agent is, what it can do, and what it did


  ABOM · abom-demo @ 0.0.0  —  9 components
  ┌───┬─────────────┬────────────────────────────┬──────────┬──────────────────────────┐
  │ # │ TYPE        │ NAME                       │ VERSION  │ DETECTED FROM            │
  ├───┼─────────────┼────────────────────────────┼──────────┼──────────────────────────┤
  │ 1 │ model       │ Anthropic (SDK)            │ —        │ dependency:anthropic     │
  │ 2 │ model       │ OpenAI (SDK)               │ 1.51.0   │ dependency:openai        │
  │ 3 │ model       │ claude-3-5-sonnet-20241022 │ —        │ file:agent.py:5          │
  │ 4 │ model       │ gpt-4o                     │ —        │ file:agent.py:4          │
  │ 5 │ framework   │ LangChain                  │ 0.2.16   │ dependency:langchain     │
  │ 6 │ framework   │ LangGraph                  │ >=0.2.0  │ dependency:langgraph     │
  │ 7 │ data source │ Chroma                     │ ~=0.4.22 │ dependency:chromadb      │
  │ 8 │ policy      │ Guardrails AI              │ —        │ dependency:guardrails-ai │
  │ 9 │ prompt      │ prompts/system.txt         │ —        │ file:prompts/system.txt  │
  └───┴─────────────┴────────────────────────────┴──────────┴──────────────────────────┘

  signed: ed25519 · key 7f7aa8d6efabb18f
  composition_sha256: c780ddca2eb619e0…
  → wrote abom.json

Verify the signature any time — it's self-contained, so anyone you hand the file to can check it:

shell
abom verify abom.json

   ╭───────╮    ▄▀█ █▄▄ █▀█ █▀▄▀█
   │   ✔   │    █▀█ █▄█ █▄█ █░▀░█
   ╰──╮ ╭──╯    Agent Bill of Materials  v0.2.0
      ╰─╯
  a signed record of what your AI agent is, what it can do, and what it did


  ✓ VALID — signature OK, 9 components
3

Block an unauthorized action what it's allowed to do

The gate enforces the signed manifest at the tool boundary, deny-by-default. A tool that isn't declared is blocked before it runs — not logged after.

Start from a manifest that declares which tools the agent may use:

gate.json
{
  "abom": "0.1",
  "type": "CompositionManifest",
  "agent": { "name": "support-agent", "version": "1.0.0" },
  "components": [
    { "type": "tool", "name": "lookup_order" },
    { "type": "tool", "name": "http_fetch", "scope": "egress",
      "allowed_endpoints": ["api.internal.shop"] }
  ]
}

A declared tool is allowed (exit 0):

shell
abom gate gate.json --tool lookup_order

   ╭───────╮    ▄▀█ █▄▄ █▀█ █▀▄▀█
   │   ✔   │    █▀█ █▄█ █▄█ █░▀░█
   ╰──╮ ╭──╯    Agent Bill of Materials  v0.2.0
      ╰─╯
  a signed record of what your AI agent is, what it can do, and what it did

  ALLOW  lookup_order
  action is within the signed envelope
  notarized · log root d4806211478cc5fcb081fe9c… · signed key 7f7aa8d6efabb18f

An undeclared tool — say a prompt injection trying to call wire_transfer — is denied (exit 1, so it fails a CI build):

shell
abom gate gate.json --tool wire_transfer

   ╭───────╮    ▄▀█ █▄▄ █▀█ █▀▄▀█
   │   ✔   │    █▀█ █▄█ █▄█ █░▀░█
   ╰──╮ ╭──╯    Agent Bill of Materials  v0.2.0
      ╰─╯
  a signed record of what your AI agent is, what it can do, and what it did

  DENY   wire_transfer  (tool_not_in_manifest)
  tool 'wire_transfer' is not in the signed Composition Manifest (declared: ['http_fetch', 'lookup_order'])
  notarized · log root d749e5cc0ad8cff29b9917ae… · signed key 7f7aa8d6efabb18f

Or wrap a tool in your agent code — a prompt injection that calls an undeclared tool raises ActionDenied before the body executes:

agent.py
from abom import Gate, ActionDenied

gate = Gate(signed_manifest)

@gate.gated()
def wire_transfer(amount, to):
    ...                       # never runs if undeclared

try:
    wire_transfer(1_000_000, to="attacker")
except ActionDenied as e:
    print(e.decision.rule)    # → "tool_not_in_manifest"
    # the money never moved — and the denial is notarized

Or move the gate out of the agent's process entirely with the MCP broker. The agent connects to the broker instead of its MCP server; every tools/call is checked against the signed manifest before it's forwarded, so a call the agent was never granted never reaches the server:

shell · the MCP broker
abom broker abom.json --upstream https://mcp.internal/rpc \
             --trusted-key 9a72f7ef… --log gate.mlog

  ABOM broker → https://mcp.internal/rpc
  mediating tools/call against the signed manifest (deny-by-default)

  tools/call  lookup_customer  # → forwarded
  tools/call  wire_transfer    # → DENIED (-32001)  # upstream never saw it
Honest scope: enforcement is structural for brokered tools. It doesn't stop an agent from making raw out-of-band calls (HTTP, subprocess) that never transit the broker — pair it with network egress containment for that.
4

Authorize against the /authorize API zero agent code

The same broker can authorize every tool call against a hosted /authorize endpoint instead of deciding locally — so a brokered MCP agent gets a signed receipt per call and the endpoint meters usage. The agent changes nothing: it just points its MCP client at the broker.

First, run the API yourself (self-hosted, free). Issue a key to run it metered:

shell · the /authorize API
abom serve abom.json                       # POST /authorize on :8088
abom keys issue --label acme              # a Bearer key (shown once)
abom serve abom.json --keys .abom/keys.json # metered

curl localhost:8088/authorize -d '{"tool":"wire_transfer"}'
  { "verdict": "deny", "rule": "tool_not_in_manifest",
    "receipt": { "inclusion_proof": {…}, "head_signature": {…} } }

Then point the broker at that endpoint. Now every brokered tools/call is authorized by the API and comes back with a signed receipt — the agent never touches the API:

shell · broker → /authorize
abom broker abom.json --upstream https://mcp.internal/rpc \
    --authorize-url https://api.abom.ai/authorize \
    --api-key abom_…

  tools/call  lookup_customer  # → authorized, forwarded
  tools/call  wire_transfer    # → DENIED  # signed receipt, upstream never saw it
The hosted, neutral endpoint is the paid tier — an independent attestor a third party can rely on. Self-hosting abom serve is free forever. See pricing.
5

Run the MCP gateway signed evidence per tool call · v0.2

The gateway sits between your agent and its MCP server and turns every tools/call into a signed, hash-chained Action Provenance Record — written and fsync'd to disk before the call is forwarded, so a forwarded-but-unrecorded call cannot exist. The default is inspect-only: policy violations are recorded as would_deny and forwarded — the evidence is the product, blocking is one flag away.

Point it at your MCP server with a project policy and an org-wide floor no template can weaken:

shell · inspect-only by default
abom gateway run gateway-baseline.json --floor org-floor.json \
    --upstream https://mcp.internal/rpc --jurisdiction EU

  ABOM gateway → https://mcp.internal/rpc  (policy 'gateway-baseline' · 26618a1394…)
  inspect mode (default) — violations recorded as would_deny and forwarded;
  pass --enforce to block
  signed provenance per call → gateway.apr.jsonl

  # … the agent works normally; the log fills with evidence …
  session ended · 4 forwarded · 0 denied · 1 would-deny · 0 degraded · 5 record(s)

Every record is ed25519-signed and hash-chained; the file opens with a signed epoch (its creation is itself attested), and arguments enter the log as salted-HMAC commitments plus a de-identified shape — never content. Verify the whole chain, or a set of rotated files, in one command:

shell · the auditor's check
abom gateway verify gateway.apr.jsonl
  ✓ VALID — 5 record(s), chain intact, every record signed

# tamper with any record — the chain breaks at the exact seq:
abom gateway verify gateway.apr.jsonl
  ✗ INVALID at seq 3 — chain: hash mismatch

Because arguments are commitments, you can later prove which call was made without payloads ever having entered the log — the per-file salt stays in your boundary, its hash sealed in the epoch:

python · settle a dispute
from abom.gateway import verify_argument_claim
verify_argument_claim("gateway.apr.jsonl", seq=3, salt_hex=salt,
                      args={"amount": 1_000_000, "to": "attacker-iban"})
  {"valid": True}   # it WAS the $1,000,000 call…
verify_argument_claim(…, args={"amount": 20, "to": "attacker-iban"})
  {"valid": False}  # …and provably not the $20 one

Already running Google Model Armor? Keep it — and seal its verdicts into the same signed records. And when the evidence has earned trust, turn on blocking:

shell · notarize the guardrail · then enforce
# they decide, you prove: sanitize-API verdicts land in detectors[]
abom gateway run gateway-baseline.json -u https://mcp.internal/rpc \
    --model-armor-template projects/acme/locations/eu/templates/strict

# opt into blocking — would_deny becomes a denial the upstream never sees:
abom gateway run gateway-baseline.json --floor org-floor.json \
    -u https://mcp.internal/rpc --enforce
  tools/call  wire_transfer   # → DENIED (-32001)  # floor policy: *_transfer
The floor is CISO-grade: a project template that contradicts it (say, allowing wire_transfer against a floor that denies *_transfer) is refused at startup with a signed PolicyConformanceViolation record — the refusal is evidence too. Upstream failures likewise become signed GatewayDegraded records: "we degraded at 14:03, here's the proof", never a silent gap. Spec: the v0.2 draft (full text ships in the sdist).
6

Prove the record can't be rewritten verify without trusting the operator

Every gate decision lands in an append-only Merkle transparency log — the same RFC 6962 construction behind Certificate Transparency and Sigstore. Two independent steps make that record something a third party can rely on, even against the operator who runs it.

First, run the gate or broker with --log so decisions land in a durable transparency log:

shell
abom gate abom.json --tool lookup_customer --log gate.mlog
abom broker abom.json --upstream https://mcp.internal/rpc --log gate.mlog

A witness cosigns the log with its own key — separate from the operator's. It refuses to cosign unless the log only grew since it last saw it, so a rewrite or truncation is rejected:

shell · the witness
abom witness gate.mlog
   cosigned  size=2  root 1ffb0c515b588b7d…
  witness key 9987a9c4c7d5f2c8 · → gate.mlog.cosig.json

# operator rewrites the log and tries again:
abom witness gate.mlog
  ✗ WITNESS REFUSED — a log may only grow (append-only)

Anchor the root to an RFC 3161 timestamp authority — an independent third party the operator doesn't control. Under eIDAS Art. 41 a qualified timestamp carries a legal presumption of time and integrity, proving the root existed, unaltered, by time T:

shell · the anchor
abom anchor gate.mlog --tsa https://freetsa.org/tsr
   anchored  root 1ffb0c515b588b7d…
  RFC 3161 token · → gate.mlog.anchor.json  # re-verifiable offline, later

# the auditor's check — no trust in the operator:
abom witness-verify gate.mlog.cosig.json
  ✓ VALID — witness attests size=2, root 1ffb0c51…
Honest scope: a witness you host yourself is defense-in-depth — the operator holds both keys. Genuine non-equivocation comes from an independent witness (the neutral network is the next step). The RFC 3161 timestamp is the third-party proof that stands today.
7

Scan a remote agent three ways

You don't need the code locally. ABOM scans a GitHub repo by URL, or a live deployed agent over the Model Context Protocol.

scan <path>

Local code

A directory or repo on your machine.

scan --url

A GitHub repo

Clones, scans, and cleans up. Never runs the code.

scan --mcp

A live endpoint

Enumerates a running agent's declared capabilities.

Scan a public GitHub repo directly — here, the real GPT Researcher agent. ABOM clones it, scans it, and shows the exact file:line each model was found at:

shell · a public repo
abom scan --url https://github.com/assafelovic/gpt-researcher

  ABOM · gpt-researcher @ 0.14.7  —  25 components
  ┌────┬────────────┬────────────────────────┬──────────┬─────────────────────────────────────────────────────────────────┐
  │ #  │ TYPE       │ NAME                   │ VERSION  │ DETECTED FROM                                                   │
  ├────┼────────────┼────────────────────────┼──────────┼─────────────────────────────────────────────────────────────────┤
  │ 1  │ model      │ ChatGPT                │ —        │ file:evals/hallucination_eval/results/aggregate_results.json:17 │
  │ 3  │ model      │ Hugging Face (SDK)     │ >=0.32.0 │ dependency:huggingface-hub                                      │
  │ 6  │ model      │ Ollama (SDK)           │ >=0.4.8  │ dependency:ollama                                               │
  │ 7  │ model      │ OpenAI (SDK)           │ >=1.3.3  │ dependency:openai                                               │
  │ 13 │ model      │ gpt-4o                 │ —        │ file:multi_agents_ag2/task.json:12                              │
  │ 14 │ model      │ gpt-4o-mini            │ —        │ file:gpt_researcher/config/config.py:220                        │
  │ 19 │ framework  │ Google GenAI           │ >=1.0.0  │ dependency:google-genai                                         │
  │ 20 │ framework  │ LangChain              │ >=1.0.0  │ dependency:langchain                                            │
  │ 21 │ framework  │ LangGraph              │ >=0.2.76 │ dependency:langgraph                                            │
  │ 22 │ framework  │ Model Context Protocol │ >=1.9.1  │ dependency:mcp                                                  │
  │ 23 │ tool       │ custom_tool            │ —        │ file:gpt_researcher/utils/tools.py                              │
  │ 24 │ tool       │ search_tool            │ —        │ file:gpt_researcher/utils/tools.py                              │
  │ 25 │ MCP server │ gpt-researcher         │ —        │ file:.mcp.json                                                  │
  └────┴────────────┴────────────────────────┴──────────┴─────────────────────────────────────────────────────────────────┘
  # 25 components total — trimmed to 13 rows here

  composition_sha256: 255bc40431675931…
  → wrote ./gptr.json

Or scan a live deployed agent over MCP — ABOM connects, runs the handshake, and reads the capabilities the server itself declares (it never invokes a tool):

shell · a live MCP server
abom scan --mcp https://mcp.deepwiki.com/mcp

  ABOM · DeepWiki @ 2.14.3  —  4 components
  ┌───┬────────────┬─────────────────────┬─────────┬──────────────────────────────────┐
  │ # │ TYPE       │ NAME                │ VERSION │ DETECTED FROM                    │
  ├───┼────────────┼─────────────────────┼─────────┼──────────────────────────────────┤
  │ 1 │ tool       │ ask_question        │ —       │ mcp:https://mcp.deepwiki.com/mcp │
  │ 2 │ tool       │ read_wiki_contents  │ —       │ mcp:https://mcp.deepwiki.com/mcp │
  │ 3 │ tool       │ read_wiki_structure │ —       │ mcp:https://mcp.deepwiki.com/mcp │
  │ 4 │ MCP server │ DeepWiki            │ 2.14.3  │ mcp:https://mcp.deepwiki.com/mcp │
  └───┴────────────┴─────────────────────┴─────────┴──────────────────────────────────┘

  signed: ed25519 · key 7f7aa8d6efabb18f
  composition_sha256: bb85b78a15d2eb91…

Auth-gated servers work too. Point it at GitHub's production MCP server with a token (--token <PAT>, or the ABOM_MCP_TOKEN env var) and you get a signed inventory of its entire capability surface — including the consequential ones a security team cares about, like delete_file and merge_pull_request:

shell · GitHub's MCP server (auth-gated)
export ABOM_MCP_TOKEN=github_pat_…
abom scan --mcp https://api.githubcopilot.com/mcp/

  ABOM · github-mcp-server @ github-mcp-server/remote-3249da0b62a1a670d81b660ad408f1d4810eacdc  —  51 components
  ┌────┬─────────────┬───────────────────────────────────┬───────────────────────────────────────────────────────────────────┬────────────────────────────────────────┐
  │ #  │ TYPE        │ NAME                              │ VERSION                                                           │ DETECTED FROM                          │
  ├────┼─────────────┼───────────────────────────────────┼───────────────────────────────────────────────────────────────────┼────────────────────────────────────────┤
  │ 1  │ tool        │ add_comment_to_pending_review     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 2  │ tool        │ add_issue_comment                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 3  │ tool        │ add_reply_to_pull_request_comment │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 4  │ tool        │ create_branch                     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 5  │ tool        │ create_or_update_file             │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 6  │ tool        │ create_pull_request               │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 7  │ tool        │ create_repository                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 8  │ tool        │ delete_file                       │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 9  │ tool        │ fork_repository                   │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 10 │ tool        │ get_commit                        │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 11 │ tool        │ get_file_contents                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 12 │ tool        │ get_label                         │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 13 │ tool        │ get_latest_release                │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 14 │ tool        │ get_me                            │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 15 │ tool        │ get_release_by_tag                │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 16 │ tool        │ get_tag                           │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 17 │ tool        │ get_team_members                  │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 18 │ tool        │ get_teams                         │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 19 │ tool        │ issue_read                        │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 20 │ tool        │ issue_write                       │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 21 │ tool        │ list_branches                     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 22 │ tool        │ list_commits                      │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 23 │ tool        │ list_issue_fields                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 24 │ tool        │ list_issue_types                  │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 25 │ tool        │ list_issues                       │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 26 │ tool        │ list_pull_requests                │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 27 │ tool        │ list_releases                     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 28 │ tool        │ list_repository_collaborators     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 29 │ tool        │ list_tags                         │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 30 │ tool        │ merge_pull_request                │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 31 │ tool        │ pull_request_read                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 32 │ tool        │ pull_request_review_write         │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 33 │ tool        │ push_files                        │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 34 │ tool        │ request_copilot_review            │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 35 │ tool        │ run_secret_scanning               │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 36 │ tool        │ search_code                       │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 37 │ tool        │ search_commits                    │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 38 │ tool        │ search_issues                     │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 39 │ tool        │ search_pull_requests              │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 40 │ tool        │ search_repositories               │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 41 │ tool        │ search_users                      │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 42 │ tool        │ sub_issue_write                   │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 43 │ tool        │ update_pull_request               │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 44 │ tool        │ update_pull_request_branch        │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 45 │ MCP server  │ github-mcp-server                 │ github-mcp-server/remote-3249da0b62a1a670d81b660ad408f1d4810eacdc │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 46 │ data source │ get_me_ui                         │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 47 │ data source │ issue_write_ui                    │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 48 │ data source │ pr_edit_ui                        │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 49 │ data source │ pr_write_ui                       │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 50 │ prompt      │ AssignCodingAgent                 │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  │ 51 │ prompt      │ issue_to_fix_workflow             │ —                                                                 │ mcp:https://api.githubcopilot.com/mcp/ │
  └────┴─────────────┴───────────────────────────────────┴───────────────────────────────────────────────────────────────────┴────────────────────────────────────────┘

  signed: ed25519 · key 7f7aa8d6efabb18f
  composition_sha256: 770a91fc2bc1cd11…
  → wrote github-mcp.json
ABOM only reads the declared capabilities over the protocol — it never invokes a tool.
8

Make it shareable

Add --card to any scan or gate to also write a self-contained SVG "report card" you can drop into a PR, a doc, or a slide.

shell
abom scan . --card abom-card.svg
abom gate abom.json --tool wire_transfer --card decision.svg

And it drops into CI — abom gate exits non-zero on DENY, so an unauthorized action fails the build:

.github/workflows/ci.yml
- uses: josephassiga/abom-dev/.github/actions/abom-scan@main
  with:
    path: .
?

Troubleshooting

A couple of real-world Python gotchas, and how to fix them.

SymptomFix
The abom command says No module named httpx even though you installed it Your abom binary (often a pipx install) uses a different environment than your pip. Either pipx inject abom-cli httpx, or run it via the right interpreter: python -m abom scan --mcp …
import abom reports an old version A stale copy is shadowing the install. Reinstall cleanly: pip install -U abom-cli (or pip install -e . from a source checkout for the latest).
--no-banner "no such option" It's a top-level flag — put it before the command: abom --no-banner scan …
Piping scan -o - into jq Works cleanly — the banner and summary go to stderr, so stdout is pure JSON.

Next steps

How it works

The three questions, the gate, and the Merkle Notary explained.

Build status

What's real vs. roadmap — honest maturity.

Source & spec

The spec · Architecture · PyPI (the sdist ships spec + ADRs)

ABOM is draft v0.1, Apache-2.0, and inspired by CycloneDX ML-BOM. Feedback and spec proposals welcome.