{
  "markdown": "# kcp-agent\n\n### 🧾 See it run — [interactive KCP playground](https://cantara.github.io/pi-kcp/playground/) · [read the reveal](https://wiki.totto.org/blog/2026/07/22/the-ai-agent-that-keeps-the-receipts/)\n\n\n**The reference agent for the [Knowledge Context Protocol](https://github.com/Cantara/knowledge-context-protocol).**\n\n> **[The Arena →](https://cantara.github.io/kcp-agent/)** — the real planner, bundled unmodified,\n> running live in your browser, head-to-head against the usual suspects. *The most deterministic\n> agents in the world. Every decision defensible.*\n\nKCP defines how knowledge declares itself so agents can navigate it. `kcp-agent` is the other\nhalf: the agent that *consumes* KCP end to end. Given a task and a `knowledge.yaml`, it produces an\ninspectable **load plan** — which units to load and in what order, which to skip and exactly why,\nhow it selects sub-manifests across a federation, and what the whole thing costs — and then,\noptionally, answers the task from only those units.\n\nThe valuable, novel core is **LLM-free and deterministic**. The plan is an *audit-before-action*\nartifact — the trusted-render principle (\"a manifest may influence what an agent knows, never what\nit does\") extended to the whole agent loop. Only the final synthesis step calls a model.\n\n```\ndiscover → verify signature → trust-gate → score units by task → federate (context + agent_identity)\n        → temporal filter → budget (payment + rate_limits) → emit load plan → [follow] → [answer]\n```\n\n## Install\n\n```bash\nnpm install\nnpm run build\n```\n\n### Native executables\n\nCI cross-compiles self-contained binaries (no Node/Deno required on the target) for\nLinux x64/arm64, macOS x64/arm64, and Windows x64 — grab them from a release or from the\n`kcp-agent-natives` artifact on any CI run. To build one yourself:\n\n```bash\nnpm ci && npm run build\ndeno compile --allow-read --allow-env --allow-net --node-modules-dir=auto \\\n  --output kcp-agent dist/cli.js\n```\n\n## Use\n\n### `plan` — the inspectable load plan (no API key)\n\n```bash\nnode dist/cli.js plan \"how do I deploy to production?\" --manifest ./path/to/knowledge.yaml --env prod\n```\n\n```\nPlan for: \"how do I deploy to production?\"\n  companyx-knowledge-hub v1.0.0 · kcp 0.24 · env prod\n\nTrust: · no manifest attestation requirement\n\nLoad plan (2 units):\n  ● 1. front-door (score 4)  overview.md  free\n  ● 2. deploy-guide (score 3) docs/deploy.md  free\n\nBudget: tier default\nFederation:\n  → platform-engineering  needs github_pat before fetch  [acquire github_pat]\n  · platform-engineering-dev  context [\"dev\",\"test\"] excludes env 'prod'\nSkipped (2):\n  · auth-guide: no task-relevance match\n```\n\nEach stage maps onto a layer of the spec: query scoring (§15), temporal validity (§4.22), agent\nattestation (§3.2), federation `context` + `agent_identity` (§3.6), and payment / rate-limits\n(§4.14/§4.15). A restricted unit the agent can't attest for is listed but marked **not\nload-eligible** — fail-closed, with the reason attached.\n\n**Budgeting the context window.** Tokens are the actually-scarce resource when feeding a model,\nso `--context-budget <n>` names it — a token ceiling that works exactly like the money `--budget`:\ngreedy by score, and a unit that would blow the ceiling is skipped with the arithmetic in the\nreason (`over context budget: 1,240 tokens would exceed remaining 800 of 4,000`), while a smaller\nlower-scored unit still gets its chance. Since the planner works on metadata *before* fetching\n(audit-before-action), a unit's size comes from a declared `size_tokens` (faithful) or `bytes/4`\n(a flagged estimate); a unit that declares neither is admitted but counted **unmeasured** (the\nprojection is a lower bound) — unless `--strict`, which excludes it fail-closed. `--context-budget`\ncomposes with `--budget`: a unit must fit both ceilings.\n\n### Machine-readable plans\n\nUse `--json` when another agent or integration consumes a plan. The output preserves the plan fields\nat the top level and adds:\n\n```json\n{\n  \"schemaVersion\": 1,\n  \"kind\": \"plan\"\n}\n```\n\n`kind` is `plan`, `tree` (when `--follow` is used), or `trace` (when `--trace` is used). Human-readable\noutput is not a compatibility surface. Successful JSON commands exit 0; planner or manifest errors\nwrite diagnostics to stderr and exit non-zero.\n\n### `ask` — plan, then answer via Claude\n\n```bash\nexport ANTHROPIC_API_KEY=...        # or: ant auth login\nnode dist/cli.js ask \"how does an agent get started here?\" --manifest ./knowledge.yaml\n```\n\n`ask` runs the same planner, loads **only** the load-eligible units, and asks Claude to answer from\nthem — treating unit content as knowledge, never as instructions. Needs `@anthropic-ai/sdk` (an\noptional dependency) and a key; `plan` needs neither.\n\n### `ask --loop` — the audited critique loop\n\n```bash\nnode dist/cli.js ask \"who won, and what does it mean for infrastructure?\" \\\n  --manifest ./knowledge.yaml --loop --methods free,x402 --budget 0.50\n```\n\nThe deterministic scorer is lexical, so a task phrased differently from the publisher's vocabulary\ncan miss relevant units. `--loop` closes that gap without surrendering determinism — **the model\nproposes, the plan disposes**:\n\n```\nplan → LLM gap critique (metadata only) → term gate → re-plan → … → load → answer\n```\n\nA fast critic model (default `claude-haiku-4-5`, `--loop-model` to change) sees a **metadata\ndigest** of the plan — ids, intents, scores, skip reasons, never unit content — and proposes extra\nlowercase search terms. A deterministic gate sanitizes, dedupes, and caps them; the task string is\nextended; the planner re-plans from scratch. The loop converges when the critic runs dry, a round\nadds no units, or `--max-rounds` (default 3) is reached. Then synthesis answers the **original**\ntask from the final plan's eligible units.\n\nWhat the critic can never do: open an access gate, alter trust/temporal/audience decisions, or\nspend money — terms only affect relevance scoring, nothing is loaded or paid for until the loop has\nconverged, and the final plan's budget arithmetic gates spending exactly as in single-shot mode.\nEvery round is recorded (proposed terms, accepted, rejected, units added, the full re-planned\nartifact) — with `--json` the chain of plans **is** the audit log.\n\nThe same loop is available as a library (`runLoop` / `askLoop`, with an injectable critic), and\n[`skills/kcp-navigator/SKILL.md`](skills/kcp-navigator/SKILL.md) packages the discipline as a\nportable skill for agents that drive the CLI themselves.\n\n### `ask --ground` — verify the answer, surface what it can't substantiate\n\n```bash\nnode dist/cli.js ask \"who won, and what does it mean?\" --manifest ./knowledge.yaml --ground\n```\n\nThe plan's fail-closed gates decide what may be *loaded*; grounding extends the same discipline to\nwhat may be *asserted*. After synthesis, each claim in the answer is checked by a **separate\nverifier** — a distinct model call from the generator — that must attribute the claim to one of the\nloaded units or return nothing. The result is a two-part artifact:\n\n```\nGrounded (2/3 claims):\n  ● The award went to the Nordic bid.\n     ↳ chipfab-exclusive · sha 9f2c1a0b7e34\nUnsubstantiated (1): — could not be grounded in a loaded unit\n  ○ The datacenter runs on hydro power.\n     no loaded unit supports this claim\n⚠ partial-unsupported — 1 claim(s) could not be substantiated\n```\n\nA claim grounds **only** if the cited unit was actually loaded and its content hash matches — so a\nverifier that mis-attributes (or is prompt-injected into) citing a unit that was never loaded can\nnever ground a claim: attribution is a proposal, grounding is adjudicated. Unsupported claims are\n**surfaced, never silently dropped** — the honest half of \"every decision defensible\". Each surfaced\ngap is also a signal to the *publisher*: the task needed evidence the manifest didn't provide. The\nsurfaced list is capped to guard against a compromised generator flooding it with spurious gaps.\n\n**`--ground-rounds <n>`** closes the loop: a surfaced gap seeds reformulation terms, the agent\nre-navigates to try to find the missing evidence, and re-grounds — up to `n` rounds. Termination is\nguaranteed by three independent bounds, any one of which halts: the term gate is **absorbing** (a\nterm accepted once is known forever, so re-navigation can only add units from the finite eligible\nset), the **round cap**, and a **progress guard** (a round that adds no new unit halts). Oscillation\nis impossible — the loaded set grows monotonically or the loop stops. Every terminal state that\nisn't `grounded` (`partial-unsupported`, `partial-budget`, `partial-rounds`) still **surfaces** the\nremaining gaps. A compromised verifier can, at worst, widen navigation within the eligible set — it\ncan never cross a gate, name a URL, or spend past the budget.\n\n### `assess` — gate what may be *acted on*\n\nThe plan's gates decide what may be *loaded*; grounding decides what may be *asserted*;\n`assess()` decides whether a conclusion clears a confidence threshold before it is **acted on**.\nIt runs downstream of synthesis — confidence is a property of the output, which is exactly why it\nstructurally cannot be gate #14 in the pre-selection cascade.\n\n```ts\nimport { assess } from \"kcp-agent\";\n\nconst verdict = await assess(task, answer, loadedUnits, {\n  threshold: 0.7,            // org policy — caller-supplied, never manifest data\n  severity: \"critical\",\n  evaluator,                 // optional: makeProviderEvaluator(provider) — a separate judge\n});\n// { gate: \"confidence\", passed, score, threshold, signals[], detail, asOf }\n```\n\nSame trust posture as grounding: confidence is a **proposal** (the answer's own\n`Confidence: 0.82`-style self-report via `extractSelfReport`, and/or an injected evaluator's\njudgment); the gate **adjudicates** deterministically — `min`-aggregated by default, fail-closed\non anything unmeasurable. Raw `signals[]` are preserved verbatim on every verdict so thresholds\ncan be calibrated against real outcomes over time. The verdict reuses the gates'\nbinary-plus-written-reason contract but is a separate downstream artifact — `DecisionTrace` and\nthe conformance vectors are untouched.\n\nThe enforcement side lives in [kcp-harness](https://github.com/Cantara/kcp-harness): its\n`harness_assess` MCP tool runs this gate and routes failed verdicts on critical tasks to a\n**named human** via its approval tickets, with the verdict embedded as evidence.\n\n### `kind: skill` — governed procedures, gated separately from knowledge\n\nSpec §4.3a (v0.26.1) adds a second class of unit alongside plain knowledge: `kind: skill` marks\nsomething an agent could *do* — a runbook, a script, a procedure — not just read. Skills fail\nclosed by default: a `kind: skill` unit is **not invoke-eligible** unless the manifest carries an\nexplicit `load_eligible: true` grant, bounded by an `action_scope` (`tools`, `paths`,\n`capabilities` it may touch). This runs as its own gate, `skill_eligibility`, inserted into the\npre-selection cascade right after relevance — the cascade is **fourteen** gates, not thirteen.\n\n```yaml\nunits:\n  - id: restart-web-service\n    kind: skill\n    load_eligible: true\n    action_scope:\n      tools: [\"shell.exec\"]\n      paths: [\"/opt/web-service\"]\n      capabilities: [\"service.restart\"]\n```\n\nAn ungranted skill is still **planned** — the agent can see it exists and why it matched a task,\nsame audit-before-action posture as every other gate — it is simply withheld (`loadEligible:\nfalse`), or dropped entirely under `--strict`, attributed to `skill_eligibility` specifically\nrather than the generic strict cutoff. Planning a skill is always safe; invoking one is a\nseparate, explicitly-granted decision. See [The Governed Skill](#demos--twenty-scenarios-no-mocks)\ndemo below, and the [conformant-implementation guide](guides/build-a-conformant-implementation.md)\nfor the exact gate contract a port must reproduce.\n\n### Demos — twenty scenarios, no mocks\n\n```bash\nnode examples/demos.js            # all twenty, narrated\nnode examples/demos.js --list     # newsstand · transition · vault · org · audit · trace · loop · grounding · seal · incident · leash · summer · milky-way · moved-world · deja-vu · borrowed-memory · context-window · dogfood · second-opinion · governed-skill\nnode examples/demos.js vault      # one at a time\n```\n\n| Demo | What it shows | Spec |\n|------|---------------|------|\n| **The Newsstand** | a 0.40 USDC ceiling: buy by score, skip with the arithmetic in the reason | §4.14 |\n| **The Transition** | one question, three `--as-of` dates; supersession decides the overlap day | §4.22 |\n| **The Vault** | payment never opens an auth gate — x402 in hand, still fail-closed | §4.11/§4.14 |\n| **The Org** | federation `context` slices by env; `agent_identity` plans credentials pre-fetch | §3.6 |\n| **The Audit** | two `--json` plans diffed: exactly which gate a capability flip moves, and its price | — |\n| **The Loop** | the audited critique loop with a scripted critic: injection bounces, terms re-plan, budget holds | — |\n| **The Grounding** | `ask --ground`: a claim citing an unloaded unit fails closed; the closed loop re-navigates and grounds it against real bytes | — |\n| **The 03:00 Page** | a zero-day across four federated parties — attestation, a signed CERT, supersession, TLP:AMBER as an enforced gate, an intel budget ([`examples/incident/`](examples/incident/)) | all of it |\n| **The Borrowed Leash** | a scripted foreign MCP client replans the incident over stdio — same gates, same ledger — then `kcp_replay` catches its falsified artifact | — |\n| **The Seal** | a signed manifest verifies; one unit appended after signing → fail-closed before planning | §3.2 |\n| **The Summer Plan** | a family vacation across four federated parties — a signed hub, timetable supersession, an identity-gated accessibility registry, x402 tour detail, and the `not_for` footgun caught by the validate lint ([`examples/summer/`](examples/summer/)) | §3.6/§4.11/§4.22 |\n| **The Milky Way** | a whole enterprise documentation estate — a signed hub over eight domains: env-sliced dev mirror, a future regulation dated out, human-only HR docs, HSM-attested formulations, an identity-gated ERP vendor with subscription rate tiers, and a CSRD annual handover ([`examples/milky-way/`](examples/milky-way/)) | §3.6/§4.14/§4.22 |\n| **The Moved World** | episodic memory: an answer recorded byte-free, recalled by task overlap, then replayed — still-grounded while the pins hold, drifted the moment the source moves | — |\n| **The Déjà Vu** | memory-validated reuse: identical inputs against an unchanged manifest are provably the same plan; new options miss; a drifted manifest is refused | — |\n| **The Borrowed Memory** | MCP session dedup: `kcp_load` withholds the bytes a caller already holds (sha-confirmed stubs), and re-serves any unit that drifted | — |\n| **The Context Window** | `--context-budget`: a token ceiling, greedy by score, over-budget units skipped with the arithmetic; size from declared `size_tokens` or `bytes/4` | — |\n| **The Dogfood** | the agent validates and navigates its own repository | §2 |\n| **The Second Opinion** | assess() gates a low-confidence conclusion before it is acted on | — |\n| **The Governed Skill** | a `kind: skill` procedure fails closed until an explicit `action_scope` grant makes it invoke-eligible | §4.3a |\n\nEvery fact each demo narrates is parsed or computed from the shipping CLI's and library's real\noutput — nothing is hardcoded — and `test/demos.test.ts` runs all twenty in CI, so the narration is\nitself a regression suite. Everything is offline; no API key needed.\n\n### This repo describes itself\n\nThe repository dogfoods KCP: [`knowledge.yaml`](knowledge.yaml) at the root declares the README,\nthe source modules, the demo manifest, and the CI workflow as knowledge units, and federates to\nthe [KCP spec](https://github.com/Cantara/knowledge-context-protocol)'s own manifest. So the agent\ncan navigate its own repo:\n\n```bash\nnode dist/cli.js plan \"how does the planner score units?\" --manifest .\n```\n\n`test/manifest.test.ts` keeps the manifest honest — parseable, pointing at files that exist, and\nplanning sensibly.\n\n## Options\n\n| Flag | Meaning |\n|------|---------|\n| `--manifest <loc>` | path, directory, or HTTPS URL of a `knowledge.yaml` (required) |\n| `--env <name>` | runtime environment for federation `context` selection (`dev`/`test`/`staging`/`prod`). Fail-closed: without it, context-tagged refs are never followed |\n| `--as-of <date>` | ISO date for temporal evaluation (default: today, UTC) |\n| `--max-units <n>` | cap on selected units (default 5) |\n| `--strict` | fail-closed: drop non-eligible units instead of listing them |\n| `--role <role>` | audience role the agent presents (default: `agent`) |\n| `--methods <list>` | payment methods the agent can settle, e.g. `free,x402` |\n| `--credentials <list>` | credential kinds the agent holds, e.g. `api_key,oauth2` |\n| `--attest <provider>` | attestation provider the agent can present |\n| `--budget <amount>` | spend ceiling for pay-per-request units — greedy by score, skips (with arithmetic) what would blow it. One ceiling for the whole federated walk, not per manifest |\n| `--currency <code>` | budget currency (default `USDC`) |\n| `--context-budget <n>` | token ceiling for what the plan loads into the model's context window — greedy by score, skips (with arithmetic) what would blow it. Composes with `--budget`: a unit must fit both |\n| `--follow` | fetch and plan eligible federation refs too (fail-closed: gated/excluded refs are never fetched) |\n| `--max-depth <n>` | federation hops to follow (default 1; implies `--follow`) |\n| `--max-nodes <n>` | cap on total manifests fetched across the whole walk (default 64; fail-closed fan-out ceiling) |\n| `--allow-private-hosts` | permit fetches to loopback/private/link-local hosts and `http://` — off by default (blocks SSRF into internal/metadata addresses) |\n| `--no-verify` | skip manifest signature verification |\n| `--require-signature` | fail unless every manifest has a *verified* signature |\n| `--trust-key <loc>` | pinned ed25519 public key (path, URL, or inline) for verification |\n| `--trace` | (`plan`) show the decision trace: per-unit gate cascade — every gate each unit was evaluated against, in order |\n| `--json` | emit the plan (and, for `ask`, the answer) as JSON |\n| `--correlation-id <id>` | opaque caller-supplied id echoed into the `--json` envelope, so a plan artifact can be joined to the caller's decision chain or audit log |\n| `--help` | print usage and the option reference (also `-h`; works after any subcommand) |\n| `--once` | (`watch`) run a single validate/plan cycle and exit instead of watching |\n| `--diff` | (`watch`) report what changed against the previous cycle |\n| `--task <task>` | (`watch`) the task to re-plan on each cycle |\n| `--publisher <name>` | (`init`) publisher name written into the scaffolded manifest |\n| `--from-llms-txt <loc>` | (`init`) draft a `knowledge.yaml` from an existing `llms.txt` — URL or path |\n| `--dry-run` | (`init`) print the generated `knowledge.yaml` instead of writing it |\n| `--force` | (`init`) overwrite an existing `knowledge.yaml` |\n| `--model <id>` | (`ask`) model id: `provider/model` (e.g. `openai/gpt-4o`, `anthropic/claude-opus-4-8`) — default `claude-opus-4-8` |\n| `--base-url <url>` | (`ask`) base URL for OpenAI-compatible endpoints (overrides provider default) |\n| `--api-key <key>` | (`ask`) API key — alternative to env vars `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` |\n| `--loop` | (`ask`) audited critique loop: plan → LLM gap critique → term gate → re-plan |\n| `--max-rounds <n>` | (`ask --loop`) max critique rounds (default 3) |\n| `--loop-model <id>` | (`ask --loop`) critic model — default `claude-haiku-4-5` |\n| `--ground` | (`ask`) verify each answer claim against a loaded unit; surface unsubstantiated ones |\n| `--ground-model <id>` | (`ask --ground`) verifier model — default `claude-haiku-4-5` |\n| `--ground-rounds <n>` | (`ask`) closed-loop grounding: a surfaced gap re-navigates for evidence (default 0) |\n| `--check-gaps` | (`replay`) re-navigate today's manifest to see if a grounded answer's surfaced gap now closes |\n| `--memory <dir>` | (`remember`/`recall`) episodic-memory directory — one hash-addressed entry per artifact |\n| `--replay` | (`recall`) re-verify each recalled episode against today's manifests (a drifted hit exits 1) |\n| `--limit <n>` | (`recall`) cap the number of episodes returned |\n| `--public-url <url>` | (`serve`) public URL this server is reachable at — self-checked against the manifest's declared `serving.mcp` (§3.12); a mismatch warns at startup |\n\n`test/docs.test.ts` keeps this table honest: every flag `parseArgs` accepts must appear here\nand in the `cli.ts` header, and vice versa.\n\n### `validate` — lint a knowledge.yaml\n\n```bash\nnode dist/cli.js validate .            # or a path, directory, or URL\n```\n\nErrors are structural problems that mislead or fail an agent (duplicate ids, unsafe or missing\npaths, `superseded_by` pointing nowhere, attestation requirements no agent can ever satisfy);\nwarnings are declarations that weaken navigation (no triggers, expired units with no successor).\nExit code 1 on errors — run it in the CI of any repo that publishes a manifest.\n\n### `replay` — re-verify a saved plan or grounded answer\n\n```bash\nnode dist/cli.js plan \"task\" --manifest . --json > plan.json\nnode dist/cli.js replay plan.json       # exit 0 identical · exit 1 drifted\n```\n\nA `plan --json` artifact pins the manifest's sha256 and echoes every planner input. `replay`\nre-fetches each manifest (every node of a `--follow` tree), compares the bytes, re-runs the\npure planner with the saved inputs, and reports **identical** or **drifted** — per manifest,\nwith the fields that moved. A plan is evidence; replay is the cross-examination. Editing the\nartifact by hand is also drift: the recomputed plan won't match it.\n\n`replay` auto-detects a **grounded-answer** artifact (from `ask --ground --json`) and\ncross-examines it claim-by-claim instead: each grounded claim's cited unit is re-read and its\n`sha256` re-compared to the pinned one — **still-grounded**, **drifted** (bytes changed), or\n**gone** (unit removed) — exit 1 if any citation no longer holds, because a stale answer must\nnot read as verified. With `--check-gaps` it re-navigates today's manifest to see whether a\npreviously-surfaced gap now **closes** (the manifest grew the missing evidence since the answer)\n— gaps have a lifecycle, and a memory is a plan you can re-verify against a moved world.\n\n### `remember` / `recall` — episodic memory as replayable plans\n\n```bash\nnode dist/cli.js ask \"who won the exclusive story\" --manifest examples/fjordwire --ground --json > ans.json\nnode dist/cli.js remember ans.json --memory .kcp-memory        # log the episode (unit bytes stripped)\nnode dist/cli.js recall \"the exclusive story winner\" --memory .kcp-memory --replay\n```\n\nA memory here is not a summary or an embedding — it is the plan/grounded-answer artifact itself,\n**stripped of the one thing that would make it dangerous to keep: the unit bytes.** Caching\nrestricted or paid content in the memory log would let a later recall read it without re-passing\nthe access gate, so `remember` keeps only what replay needs — each unit's `id`, `path`, `sha256`,\nand the citation table — and drops every `content` field. Entries are hash-addressed by their\ncontent-stripped artifact, so recording the same answer twice is idempotent.\n\n`recall` matches past episodes by lexical task-term overlap (the same tokenizer the planner\nscores with), ranked by overlap. Because the bytes are gone, a recalled episode carries **no\nfreshness claim on its own**: with `--replay` each hit is re-verified against today's manifests —\n**valid** (every cited unit holds its pinned bytes), **drifted** (a citation moved — exit 1), or\n**unverifiable** (the replay could not run). Without `--replay`, every hit is reported\n`unverifiable` — memory never falsely claims a stale answer is still true. A memory is a plan you\ncan re-verify against a moved world.\n\n### memory-validated reuse — a determinism-preserving cache\n\nPassing `--memory <dir>` to `plan` or `ask` turns the episode log into a cache whose\ncorrectness rests on the same property everything else does: a plan is a pure function of\n`(manifest bytes, task, options)`. The rule is **recall (exact match) + replay (freshness) =\nreuse**, and everything short of that is fail-closed.\n\n```bash\nnode dist/cli.js plan \"how do I deploy\" --manifest . --memory .kcp-memory   # records + reports determinism\nnode dist/cli.js plan \"how do I deploy\" --manifest . --memory .kcp-memory   # ♻ provably identical to episode …\nnode dist/cli.js ask  \"how do I deploy\" --manifest . --ground --memory .kcp-memory   # reuses a clean grounded answer, skips the model\n```\n\n`plan --memory` records each plan and, if a prior episode ran with the *same* task, manifest,\nand options, reports whether today's manifest is byte-identical (**♻ provably identical**) or\nhas **drifted** since — a determinism/audit signal, never a silent reuse across a sha change.\nThe cache key includes the effective `--as-of` date, so an unpinned plan is only reuse-eligible\nwithin the same day; a run under different capabilities (`--role`, `--budget`, …) is a different\nplan, not a hit.\n\n`ask --ground --memory` is where reuse pays off: before calling the model it looks for a cached\n**grounded answer** for the identical request and replays it — re-reading every cited unit and\nre-checking its pinned `sha256`. Only if *every* citation still holds is the stored answer\nreturned (**♻ reused**, no model call); if any cited unit drifted or is gone, the answer is\n**re-computed**, never served stale. Because ingest already stripped the unit bytes, a recalled\nanswer can never smuggle restricted content past the next access gate — reuse re-reads the units\nthrough the guard, live. A memory is a plan you can re-verify against a moved world.\n\n### `mcp` — serve the planner to any MCP client\n\n```bash\nnode dist/cli.js mcp                   # stdio transport\n```\n\nExposes four tools: `kcp_plan` (the inspectable load plan), `kcp_load` (the plan **plus the\ncontent** of load-eligible units, so the calling agent's own model synthesizes — kcp-agent never\nneeds an API key here), `kcp_validate`, and `kcp_replay` (cross-examine a saved plan artifact\nover the wire). `kcp_plan`/`kcp_load` take the CLI's full capability surface — `role`,\n`methods`, `credentials`, `attest`, `budget` — so attestation and credential gates answer for\nany MCP client exactly as they do on the command line. The borrowing agent doesn't have to be\ndeterministic; it just has to ask someone who is. Register it in e.g. Claude Code:\n\n```bash\nclaude mcp add kcp -- node /path/to/kcp-agent/dist/cli.js mcp\n```\n\n**Session dedup.** `kcp_load` accepts a `known` argument — the units the caller already holds,\nas `[{id, sha256}]`. A unit whose sha still matches comes back as an `unchanged` stub (bytes\nwithheld, sha confirmed) instead of re-serving its content, saving the caller's context window\nacross a multi-turn session; the response reports `deduped` and `bytesSaved`. This is the\ncaller-side of episodic memory, kept in character: the server stays stateless (the caller's\nwindow *is* the session), a stub is emitted **only** on an exact sha match — any drift re-serves\nthe fresh bytes — and because `kcp_load` re-plans and so re-gates every call, a unit the caller\nhas since lost access to is simply absent, never smuggled back as a stub.\n\n## knowledge.yaml is llms.txt grown up\n\n[llms.txt](https://llmstxt.org) solved the easy half of the same problem — telling an agent\nwhat a site contains — and publishers adopted it. It is a flat link list, and that is all it\ncan ever be.\n\nSame site, both ways:\n\n```\n# Acme Docs                                    kcp_version: \"0.30\"\n                                               project: \"Acme Docs\"\n> Everything you need to integrate Acme.       intent: \"Everything you need to integrate Acme.\"\n\n## Guides                                      units:\n                                                 - id: \"quickstart\"\n- [Quickstart](/quickstart):                       path: \"quickstart\"\n    Get running in five minutes                    intent: \"Get running in five minutes\"\n                                                   audience: [agent, human]\n                                                   triggers: [\"guides\", \"quickstart\"]\n```\n\nConvert an existing one — deterministic, no model call, and it never overwrites a manifest\nyou already have:\n\n```bash\nkcp-agent init --from-llms-txt https://acme.dev/llms.txt --dry-run\n```\n\n`kcp-agent discover <url>` also checks `/llms.txt` before falling back to crawling, and\npoints you at the upgrade.\n\nWhat the upgrade buys, none of which llms.txt can express:\n\n| | llms.txt | knowledge.yaml |\n|---|---|---|\n| What exists | ✅ a link list | ✅ units with intent and triggers |\n| Who it is for | ❌ | `audience`, `not_for`, role gating |\n| When it is true | ❌ | `valid_from` / `valid_until`, supersession |\n| Whether it is authentic | ❌ | detached ed25519 signatures |\n| What it costs | ❌ | `payment`, budgets, rate limits |\n| What it points to | ❌ | federation to other publishers' manifests |\n| What an agent may *do* with it | ❌ | `kind: skill` / `playbook`, `action_scope`, `load_eligible` |\n\nThe conversion marks every one of those as a TODO rather than inventing it. A converted\nmanifest that silently claimed an audience or a validity window would be asserting something\nyou never said.\n\n## Signatures\n\nA manifest may declare a `signing` block (scheme `ed25519`, key + detached signature URLs — see\nthe [spec repo's own manifest](https://github.com/Cantara/knowledge-context-protocol)). When\npresent, kcp-agent verifies the signature over the exact manifest bytes before planning:\nan **invalid** signature always fails closed; an **unverifiable** one (key unreachable) is a\nwarning unless `--require-signature`. Supported: JSON signature envelopes\n(`{algorithm, public_key, signature}`), raw base64/hex signatures, and PEM / SPKI-DER / raw-32-byte\nkeys. Pin a publisher key with `--trust-key` so the manifest can't attest for itself.\n\n## The network boundary\n\nA manifest is untrusted input that *chooses* URLs the agent then fetches — federation refs,\nsignature and key locations, remote unit content. Every remote read funnels through one guarded\nfetch (`src/fetch.ts`), fail-closed by default:\n\n- **SSRF / confused deputy** — `https://` only for remote; loopback, private, link-local, and\n  cloud-metadata addresses (e.g. `169.254.169.254`) are refused. Hostnames are DNS-resolved and\n  every address checked; redirects are followed manually so a public host can't bounce the agent\n  into a private one. `--allow-private-hosts` opts in for local/internal manifests.\n- **Fan-out** — depth and cycles were already bounded; `--max-nodes` (default 64) now caps the\n  *total* manifests a single `--follow` will fetch, so one hostile hub can't fan out to millions.\n- **Response size** — every read is streamed against an 8 MiB ceiling and aborted past it, with a\n  whole-exchange timeout, so a hostile endpoint can't exhaust memory.\n\nOver MCP the guard is on by default — a foreign client is exactly the untrusted-caller case.\n\n## Writing triggers agents can find\n\nThe scorer is **lexical and deterministic** — intent, triggers, and id/path are matched against the\ntask's terms; there is no model and no embedding. That's the feature (reproducible, auditable,\nfree), and it has an honest consequence: **a unit is only findable through the words its manifest\ndeclares.** A real miss from the field:\n\n```\ntask: \"sovereign compute award and infrastructure implications\"\n  · datacenter-power: no task-relevance match      ← the story's best infrastructure angle\n```\n\nThe unit's triggers were `[datacenter, power grid, capacity, Nordics]` and its intent never said\n\"infrastructure\" — zero lexical overlap, score 0, skipped. The fix belongs in the manifest, not\nthe planner:\n\n- **Write triggers for the questions agents ask, not the nouns in the content.** \"infrastructure\",\n  \"energy costs\", \"where does the compute run\" — the phrasings of tasks — beat article vocabulary.\n- **Spend intent words on question terms too**: intent is scored, so \"How the power grid limits\n  sovereign compute infrastructure\" is findable where \"Live Nordic datacenter power-grid feed\" is not.\n- Run `kcp-agent plan` with your expected tasks against your own manifest before publishing —\n  the skip reasons show exactly what a real agent would miss and why.\n\n## Library\n\n```ts\nimport { loadManifest, plan, synthesize } from \"kcp-agent\";\n\nconst manifest = await loadManifest(\"./knowledge.yaml\");\nconst p = plan(manifest, \"how do I deploy?\", { env: \"prod\", capabilities: { paymentMethods: [\"free\", \"x402\"] } });\n// p.selected / p.skipped / p.federation / p.budget / p.trust — a pure, inspectable artifact\nconst { answer } = await synthesize(p);   // optional LLM step\n\nimport { assess } from \"kcp-agent\";       // post-synthesis confidence gate\nconst verdict = await assess(\"how do I deploy?\", answer, [], { threshold: 0.7 });\n```\n\n## Design\n\n- **Deterministic planner** (`src/planner.ts`) — pure functions, fully unit-tested (`npm test`), no\n  I/O and no model. The plan is reproducible and auditable.\n- **Self-contained KCP client** (`src/client.ts`) — parses `knowledge.yaml` from a path, directory,\n  or HTTPS URL. No dependency on the spec repo's internals.\n- **Federation follower** (`src/follow.ts`) — the async shell around the pure planner: fetches\n  eligible refs recursively, fail-closed, with cycle detection and per-hop signature verification.\n- **Signature verification** (`src/verify.ts`) — ed25519 over exact manifest bytes via WebCrypto.\n- **Synthesis layer** (`src/synthesize.ts`) — the only part that calls a model; loads only the\n  planned units and answers the task.\n- **Confidence gate** (`src/assess.ts`) — post-synthesis: adjudicates self-reported and/or\n  evaluator confidence against a caller-supplied threshold, deterministically, fail-closed.\n- **MCP server** (`src/mcp.ts`) — dependency-free JSON-RPC over stdio.\n\n## Spec conformance\n\nThe agent targets **KCP 0.25** and consumes the subset below end to end. (The spec repo's own\nmanifest currently declares `kcp_version: 0.21` — the manifests are compatible for these layers.)\n\n| Spec layer | Section | Where |\n|------------|---------|-------|\n| Query scoring (intent / triggers / id+path) | §15 | `planner.ts` `scoreUnit` |\n| Audience & `not_for` targeting | §4 | `planner.ts` audience/negative gates |\n| Access is the auth axis — payment never substitutes | §4.11 | `planner.ts` access gate |\n| Temporal validity & supersession | §4.22 | `planner.ts` `temporalStatus` |\n| Agent attestation requirements | §3.2 | `planner.ts` trust gate |\n| Federation `context` + `agent_identity` | §3.6 | `planner.ts` + `follow.ts` |\n| Payment methods & tiers | §4.14 | `planner.ts` `planPayment` |\n| Rate-limit tiers | §4.15 | `planner.ts` `planBudget` |\n| Manifest signing (ed25519) | signing block | `verify.ts` |\n| Discovery (`knowledge.yaml`, `.well-known/`) | §2 | `client.ts` |\n\nEvery row is pinned to the CI tests that enforce it in\n[`docs/conformance.json`](docs/conformance.json) — rendered as\n[the Receipts](https://cantara.github.io/kcp-agent/#receipts) on the site — and\n`test/docs.test.ts` fails the build if a referenced test disappears or is renamed.\n\n### Conformance vectors\n\n[`vectors/`](vectors/) freezes the planner's decisions as portable\n`(manifest, task, options) → expected outcome` fixtures — the deterministic core's behavior as\n**data**, not code. `test/vectors.test.ts` proves the reference planner reproduces every one; any\nsecond implementation (a Go/Rust port for a 2–5 MB static binary, or a third party's) is\nconformant iff it does the same. Two independent implementations that pass the same vectors\nvalidate the *spec*, not just the code — the strongest proof a protocol is unambiguous. The corpus\nis generated from the reference planner (`npm run gen:vectors`) so the expected outcomes are never\nhand-written, and is [proposed upstream](vectors/README.md) as the normative KCP conformance suite.\n\nNot yet consumed: dependency chains between units, `hints.load_strategy`, compliance/audit blocks.\n\n## Guides\n\n- [Quickstart — your first ten minutes](guides/quickstart.md) — install → plan → ask → validate\n  → serve → replay, end to end.\n- [Make your repo navigable in 10 minutes](guides/make-your-repo-navigable.md) — from nothing to a\n  manifest real plans run against, kept honest in CI.\n- [Sign your manifest](guides/sign-your-manifest.md) — ed25519 over exact bytes, envelopes, key\n  pinning, and the fail-closed lifecycle.\n- [Wire the planner into Claude Code](guides/wire-mcp-into-claude-code.md) — `kcp_plan` /\n  `kcp_load` / `kcp_validate` over MCP, no API key needed.\n- [Give your agent a memory](guides/give-your-agent-memory.md) — record answers as replayable,\n  byte-free episodes; recall by task, verify by replay, reuse only while they hold.\n- [Cut context cost with session dedup](guides/cut-context-cost-with-dedup.md) — how an MCP\n  caller passes `known` units to `kcp_load` and stops re-spending its context window.\n- [Build a conformant implementation](guides/build-a-conformant-implementation.md) — the planner\n  pipeline as a spec, and how to validate a second (e.g. Go) implementation against the vectors.\n\n## License\n\nApache-2.0 · Proposed by [eXOReaction AS](https://www.exoreaction.com), hosted under\n[Cantara](https://github.com/Cantara).\n",
  "bytes": 37656,
  "sha": "92c6d49b18e2ff240dc3fbcf1adf19648a4d335e626694c296307f241681195f",
  "repo_slug": "cantara/kcp-agent",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_no_cantara_kcp_agent_cf62a65b/readme"
}