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.