Verify before sign.

As a type, and as a runtime guarantee.

None of the agentic commerce protocols carries a fingerprint of the terms it is settling. The Legal Context Protocol (LCP) defines one. Agent Guard is the buyer-side check: it fetches the terms the seller advertised, recomputes that fingerprint over the bytes actually served, and refuses to reach your signing key if the two disagree.

npm install @integraledger/agent-guard
Apache-2.0 · free forever · no account, no key, no token

The failure is not the one people expect

Told that a fingerprint disagrees, agents already halt — reliably and unprompted. That is not the gap. The gap is that nothing tells them: a seller who advertised no fingerprint reads as nothing to check.

Reading is not verifying

Fetching a terms document tells you what was served to you. Only a fingerprint the seller advertised binds the seller to it.

Your own hash proves nothing

Hashing the bytes you were handed records what you saw. It is not a commitment, because the seller never made one — in a dispute it proves only that you hashed something.

Gaps resolve by policy

Paying at Level 1 can be correct. What must not happen is paying while believing the terms were verified — so a gap is your stated disposition, never a silent default.

The whole integration

Parse the challenge into a typed proposal, hand it to transact with your policy and your signer. The key is reachable on one path only.

gate.ts
import {
  makeCachingFetcher,
  nodeDnsLookup,
  parseProposalFromChallenge,
  transact,
} from "@integraledger/agent-guard";

const now = () => new Date().toISOString();
const fetcher = makeCachingFetcher({ httpFetch: fetch, now, lookup: nodeDnsLookup });

// `challenge` is the 402 body the seller returned.
const proposal = parseProposalFromChallenge(challenge, {
  level: 3,
  sellerAssurance: "domain-controlled",
});

const result = await transact(proposal, policy, { fetcher, now }, signer);
// result.kind === "signed" only on Proceed.
// On Decline or Escalate the signer is never called.

Three outcomes, and only one reaches the key

A step’s four-valued status maps totally onto a disposition. A failure always declines; gaps are resolved by the buyer’s stated policy.

Proceed

The terms served hash to the fingerprint the seller advertised, and the typed record satisfies your policy. The signer is called exactly once.

Decline

A fingerprint mismatch, an unfetchable document, a level below your floor, a forbidden clause, an offer over your cap. Carries a halt class, a code, and a detail you can report to the seller.

Escalate

Hands back the exact bytes and their hash, so what a human approves is what a key would sign. Reached only when your policy elects it.

Works on the protocol you are already on

Reading the advertised reference is universal. Parsing a full proposal is not — and the difference is a fact about the protocols, not a gap in the library.

CapabilityReachWhy
readAdvertisedTermsAll nine registered protocolsThe reference is read out of each protocol’s own placement manifest — every carrier it declares, not the first that answers.
parseProposalUniversalx402, ACPA proposal also carries an offer, and an amount with its unit is protocol-native economics no manifest declares.
Parsers reached by nameAP2, MPPEvery AP2 envelope is also an A2A message, and an MPP request body carries nothing that names MPP. Ambiguity refuses rather than guesses.
detectProtocolNever a guessCollects every discriminant that fires, not the first. A document that is legitimately two protocols’ comes back named twice.

Or hand the checks to your agent host

@integraledger/lcp-mcp-server is a read-only Model Context Protocol server. Every tool reads; nothing publishes, transmits, or holds a credential.

.mcp.json
{
  "mcpServers": {
    "lcp": { "command": "npx", "args": ["-y", "@integraledger/lcp-mcp-server"] }
  }
}

Properties worth relying on

The prompt-injection boundary is architectural

The typed proposal cannot carry natural-language prose, so the terms body can never reach policy evaluation. That is a property of the type, not a matter of discipline.

The fetcher is the SSRF guard

HTTPS-only, refuses redirects, re-checks every resolved address is public unicast on every network fetch, and caps the body while streaming. The URL came off the seller’s challenge.

Runs wherever your agent runs

Node, Bun and Deno are measured on every release — against the packed tarball, installed the way you would install it. The package’s own source imports no Node built-in.

Nothing calls home

No telemetry, no callback, no registry check — no network request other than fetching the terms the seller pointed you at.

Start here

Both packages implement the buyer side of the Legal Context Protocol, co-stewarded by Integra Ledger and AAA-ICDR. The seller-side application is separately licensed and is not part of this repository.

On npm: @integraledger/agent-guard · @integraledger/lcp-mcp-server