MCP reference
strauss-kb-mcp speaks stdio and takes no API key and no required environment.
{ "mcpServers": { "strauss-kb": { "command": "strauss-kb-mcp" } } }
Every tool is a projection of the same command table the
CLI projects, so the two cannot drift. Thirty-four tools;
the one CLI verb with no tool is sync-instructions. STRAUSS_KB_ACTOR names
the writer in the log, defaulting to mcp here. Diagnostics go to stderr,
because stdout is the JSON-RPC transport.
Shared parameters
bundlePath(string) — absolute path to the base. Required by every tool exceptkb_schema,kb_types,kb_pins, andkb_context.conceptId(string) —<type>.<slug>, both kebab-case, e.g.decision.cursor-v2.type(enum) — one offact,requirement,constraint,decision,assumption,open-question,risk,contract,flow,affected-system,source-note.
The tool descriptions the server registers carry the judgment a schema cannot:
an unsourced claim is an assumption, a conflict belongs in a risk or a
superseding decision, and kb_load is usually the right first call.
Write tools
Each refuses when the base is pinned --frozen in the workspace.
kb_write
As CLI write; the record object is the
input parameter rather than stdin.
Parameters: bundlePath, type (enum), input (object) — all
required. input is the write object — slug, title, why required;
sections, anchors, sources, assumption, stale_after, verify, tags,
relatedConceptIds, links (max 64), supersedes (max 32), materiality,
confidence, owner optional. Unknown keys are rejected, and sections keys
must be headings the type defines (see kb_types). links are
typed causal edges — { target, rel },
source → target, from the closed eight-rel vocabulary; a self-link is refused.
{
"bundlePath": "/repo/.strauss/kb",
"type": "fact",
"input": {
"slug": "cache-key-includes-region",
"title": "The cache key includes the region",
"why": "A region-less key serves another region's data.",
"sections": { "Claim": "Every key is prefixed with the region." }
}
}
kb_write_decision
As CLI write-decision. Parameters:
bundlePath and input, both required; input is kb_write's object minus
sections, plus optional alternative and impact.
{
"bundlePath": "/repo/.strauss/kb",
"input": {
"slug": "cas-not-lock",
"title": "Compare-and-swap rather than a lock",
"why": "A stale lock hold blocks every later writer.",
"alternative": "A lock file, which adds a stale-hold failure mode."
}
}
kb_no_decision
As CLI no-decision. Parameters:
bundlePath and reason (string), both required.
{ "bundlePath": "/repo/.strauss/kb", "reason": "The diff answers it." }
kb_status
As CLI status. Parameters: bundlePath,
conceptId, and status — one of draft, proposed, accepted, open,
resolved, rejected, superseded — all required.
{
"bundlePath": "…/kb",
"conceptId": "requirement.export-csv",
"status": "accepted"
}
kb_supersede
As CLI supersede. Parameters:
bundlePath, conceptId, replacementId — all required.
{
"bundlePath": "…/kb",
"conceptId": "decision.cursor-v1",
"replacementId": "decision.cursor-v2"
}
kb_answer
As CLI answer. Parameters: bundlePath,
conceptId, answer (string) — all required.
{
"bundlePath": "…/kb",
"conceptId": "open-question.retry-budget",
"answer": "Three retries with full jitter."
}
kb_verify
As CLI verify; --note is the note
parameter, a non-blank string that must say what the check found. Parameters:
bundlePath, conceptId, note — all required.
{
"bundlePath": "…/kb",
"conceptId": "decision.cas-not-lock",
"note": "Re-read KbStore.setStatus; the digest check is still before publish."
}
kb_anchor_resolve
As CLI anchor-resolve, with the
flags as camelCase parameters. Results carry the same per-anchor outcome;
only the exit code is CLI-only.
Parameters: bundlePath and conceptId required; repoRoot (string,
defaults to the working directory), offline, rebaseline, restamp and
check (boolean) optional.
{
"bundlePath": "…/kb",
"conceptId": "decision.cas-not-lock",
"repoRoot": "/repo"
}
kb_anchor_set
As CLI anchor-set. The object the CLI reads
from stdin is the input parameter.
Parameters: bundlePath, conceptId and input required; resolve,
offline (boolean) and repoRoot (string) optional, as the CLI flags. input is
{ reason, anchors }; anchors is the complete new set. Carry an anchor's
hash and the rest of its stamp forward to keep drift visible until the new
code is read.
{
"bundlePath": "…/kb",
"conceptId": "decision.export-retention",
"input": {
"reason": "Reviewed the refactor: isExportExpired replaces shouldDeleteExport; retentionDays owns the shared setting.",
"anchors": [
{
"file": "src/cleanup.mjs",
"symbol": "isExportExpired",
"hash": "sha256:5c7242b8…",
"hash_kind": "ast",
"resolved_at": "2026-09-17T20:06:25.829Z",
"lines": 3,
"resolver": "tree-sitter"
},
{ "file": "src/retention.mjs", "symbol": "retentionDays" }
]
}
}
Returns { conceptId, reason, changes, anchors, baseline, note }. baseline is
always "unchanged": moving a pointer is not accepting the code behind it. Call
kb_anchor_resolve next to check the new pointers, with rebaseline to accept
them, and kb_verify only for a reading someone actually did.
kb_reassess
As CLI reassess, with the flags as camelCase
parameters. Reach for it when a drift warning names a record, or when a
supersession leaves you asking whether the records around it still hold.
Parameters: bundlePath and conceptId required; repoRoot (string,
defaults to the working directory) and withDiff (boolean) optional.
{
"bundlePath": "…/kb",
"conceptId": "fact.region-key",
"withDiff": true
}
Returns { conceptId, packet, rebaselined, cosmetic }; packet is null when
the record has neither drift nor an unresolved reference. packet.references
holds outgoing — what this record's strauss_links point at that no longer
holds — and incoming, who still points at it, answered for a record that has
itself stopped holding. It rebaselines moved
anchors and does nothing else to the record — never verified[], never
standing, never a link.
Read tools
kb_load, kb_catalog, kb_query, kb_pack, and kb_trace are the only
supported ways to read a base: a raw file read bypasses supersession resolution.
kb_load
As CLI load, with the flags as camelCase
parameters. Usually the right first call.
Parameters: bundlePath required; type (enum, narrows to one record type),
budgetTokens (positive integer, default 25000), all (boolean — bypasses
the budget, mutually exclusive with budgetTokens), and repoRoot (string, default cwd, for the drift
check) optional. Superseded records arrive as name, replacement and date
stubs — pass the id to kb_trace for the history.
Refuses over budget rather than truncating, message naming the next rung.
Every result — refused or not — carries a digest, the base's content stamp
(see the load digest).
kb_load's result belongs in the stable prefix — the system prompt, or the
first turn — and should be reloaded only when the returned
digest changes. A prompt cache matches a
prefix byte-for-byte, so one volatile result placed ahead of a stable load
prices the whole base at full rate on every later call.
{ "bundlePath": "/repo/.strauss/kb", "type": "decision" }
kb_catalog
As CLI catalog: every record as one line —
concept id, type, title, standing, stale flag — at roughly thirty tokens each.
Parameters: bundlePath required, type and tags (string array) optional.
The tier-one listing, and what to reach for when kb_load refuses: alone
among the read tools it has no ceiling and never refuses. Bodies are not
here.
{ "bundlePath": "/repo/.strauss/kb", "type": "open-question" }
kb_query
As CLI query; the query text is text, and
this surface adds type and includeNonCurrent.
Parameters: bundlePath required; text (string), type (enum), tags
(string array), includeNonCurrent (boolean — the CLI always sets this), and
repoRoot (string, default cwd, for the drift check) optional.
Place them at the tail of the context, not the stable prefix kb_load's
output belongs in, or every query invalidates a prompt cache that would
otherwise hold.
{ "bundlePath": "/repo/.strauss/kb", "text": "cache key region" }
kb_pack
As CLI pack, with the flags as camelCase
parameters. Emits markdown. Parameters: bundlePath and conceptId (the root)
required; hops (default 2), maxNodes (default 20) and budgetTokens
(default 25000) optional positive integers.
{ "bundlePath": "/repo/.strauss/kb", "conceptId": "decision.cursor-v2" }
kb_trace
As CLI trace; the edge names are the edges
array, and this surface adds depth.
Parameters: bundlePath and conceptId (the seed) required; edges (array of
typed-link, supersession, anchor, source; default all four) and depth
(positive integer, default 3) optional. typed-link follows only the causal
rels — related_to is excluded because a bibliography floods a blast radius.
{
"bundlePath": "…/kb",
"conceptId": "decision.cursor-v2",
"edges": ["supersession"]
}
kb_impact
As CLI impact: the transitive set of
dependants of a record.
Parameters: bundlePath and conceptId (the root) required; depth (positive
integer, unbounded by default — a walk it cuts reports truncated: true) and
rels (array of causal rels, default every rel but related_to) optional.
The walk follows each rel in whichever direction its dependence runs. Naming
related_to — or an unknown rel — in rels is an error, not an empty
result.
{
"bundlePath": "…/kb",
"conceptId": "fact.region-key",
"rels": ["depends_on", "satisfies"]
}
via holds each edge as { source, target, rel } — the edge as written,
not as walked. For one flat hop of every rel, use kb_backlinks.
kb_match
As CLI match, except that the diff always arrives
as files — there is no --git or --stdin here. Reach for it when you have
code in hand and want what is attached to it; use kb_answer when you have a
question and want whatever addresses it.
Parameters: bundlePath and files
([{ filePath, hunks: [{ startLine, endLine, side? }] }], where a hunk's
optional side is "old" or "new" and picks which half of the change its
lines number — only an anchor on the same side lands on it) required;
symbolRanges
([{ file, symbol, startLine, endLine }], resolved from repoRoot when
omitted), repoRoot (string), offline (boolean) and includeNonCurrent
(boolean) optional.
{
"bundlePath": "…/kb",
"files": [
{
"filePath": "src/order.service.ts",
"hunks": [{ "startLine": 118, "endLine": 131 }]
}
],
"repoRoot": "/repo"
}
kb_classify
As CLI classify, except that the diff always
arrives as files — there is no --git or --stdin here. Reach for it to
decide what in a change needs reading; kb_match says what is attached to it.
Parameters: bundlePath and files required — each file
{ filePath, hunks: [{ startLine, endLine, side?, lines? }], renamedFrom?, similarity? },
where lines are the hunk's changed lines and feed the boilerplate and banner
rules; repoRoot (string) optional, and the file's first lines and any
symbol-scoped override are resolved from it; offline (boolean) optional,
which keeps that resolution off the network.
{
"bundlePath": "…/kb",
"files": [
{
"filePath": "src/protocol/generated/index.ts",
"hunks": [{ "startLine": 4, "endLine": 4, "lines": ["// @generated"] }]
}
],
"repoRoot": "/repo"
}
kb_backlinks
As CLI backlinks: every inbound typed
link, one hop, every rel including related_to, each with its rel and the
standing of the record that made it. Parameters: bundlePath, conceptId.
{ "bundlePath": "/repo/.strauss/kb", "conceptId": "fact.region-key" }
kb_list
As CLI list: every record, optionally narrowed
to one type or tag. Parameters: bundlePath required, type and tags (string
array) optional.
tags is the CLI's repeatable --tag, and is the same field on kb_query and
kb_catalog — see tags.
{ "bundlePath": "/repo/.strauss/kb", "type": "open-question" }
kb_index
As CLI index: the index, rebuilt if it
disagrees with the records — the cheap re-orientation call after compaction.
Parameter: bundlePath.
{ "bundlePath": "/repo/.strauss/kb" }
kb_log
As CLI log: what touched what, and when. Malformed
lines are reported rather than repaired, and conflicted says the log still
carries merge markers, which the read skips past. Parameter: bundlePath.
{ "bundlePath": "/repo/.strauss/kb" }
kb_stamp
As CLI stamp: the base's
digest, counts, and a digest per
record, and drifted: how many records have an anchor whose code no longer
matches its hash — no bodies. Parameters: bundlePath optional (omit it to stamp every
pinned base), since optional (a digest, or a path to a prior stamp, which
also names the changed ids). Empty means nothing moved.
Ask it when a reload hook said a base
changed, or before trusting a kb_load result from earlier in the session.
{ "bundlePath": "/repo/.strauss/kb", "since": "9f2c…" }
Promotion and export tools
kb_promote
As CLI promote, with the flags as camelCase
parameters. Reach for it at merge, to lift what a review base settled into the
base that outlives the pull request.
Parameters: bundlePath required — the base being promoted from;
conceptIds (string[]) and to (the target base) required unless list is
true; source (string, usually the pull request URL) and force
(boolean, overwrite what the target already holds) optional.
{
"bundlePath": "/repo/.strauss/review",
"conceptIds": ["decision.cursor-v2"],
"to": "/repo/.strauss/kb",
"source": "https://github.com/org/repo/pull/59"
}
Returns { mode: "promote", to, promoted } with { conceptId, droppedLinks }
per record, or { mode: "list", candidates } with
{ conceptId, type, title, why } per candidate. Copies land without the review
tags; links to records left behind are dropped and reported. The originals stay
put.
kb_export
As CLI export. Reach for it when a repository
keeps ADRs of its own and the base is where its decisions are actually written.
Parameters: bundlePath, format ("madr") and to (the directory) all
required.
{ "bundlePath": "/repo/.strauss/kb", "format": "madr", "to": "docs/adr" }
Returns { to, format, exported, foreign }, each exported entry
{ conceptId, file, status }. Numbering is keyed by slug, so a re-run rewrites
content in place.
Format tools
kb_validate
As CLI validate: cross-record checks over
supersession pointers, typed-link rels and targets, and assumptions that cite
sources. Parameter: bundlePath.
{ "bundlePath": "/repo/.strauss/kb" }
Returns an array of { check, conceptId, note, severity }. Only errors fail the
check; the non-zero exit is CLI-only.
kb_doctor
As CLI doctor, with the flags as camelCase
parameters. Read-only — every finding names a record for a person to repair.
Parameters: bundlePath required; expiringDays (default 30), unverifiedDays
(default 90), agingDays (default 90), repoRoot (default cwd, for drifted),
offline (boolean, read foreign anchors from the repo cache only), strict
(boolean — turns an expired record into a non-zero CLI exit, with no
effect on the report), drifted (boolean — report only drift, as a
kb_reassess packet per record) and withDiff (boolean, with
drifted) optional.
The checks are expired, expiring, unverified, aging, orphaned,
broken-supersession, superseded-but-cited, drifted, and unchecked — see the
CLI reference for what each detects. Every group
is reported even when empty.
{ "bundlePath": "/repo/.strauss/kb", "unverifiedDays": 30 }
Returns { bundlePath, checkedAt, recordCount, thresholds, counts, groups, findingCount, healthy }, where each group is
{ check, headline, count, findings } and each finding is
{ conceptId, title, status, note }. A superseded-but-cited finding also
carries reference: { from, target, targetStanding, rels, replacedBy }. Under drifted it also carries packets and rebaselinable.
kb_sweep
As CLI sweep: deletes records carrying tag that
are also resolved, rejected or superseded — see
the one deletion. Parameters:
bundlePath and tag required, terminal (must be true, naming the only
scope it deletes) required, dryRun (boolean) optional.
{ "bundlePath": "/repo/.strauss/kb", "tag": "review", "terminal": true }
Returns { tag, dryRun, deleted, candidates, skipped, failed }, where
skipped is { conceptId, heldBy } per record a surviving record still points
at, and failed is { conceptId, reason } per id the run could not remove.
kb_schema
As CLI schema: JSON Schema for the
frontmatter, the write input, and log entries. Takes no parameters.
{}
kb_types
As CLI types: every record type with
its purpose, body sections, and starting status. Read this before writing
rather than guessing headings. Takes no parameters.
{}
Workspace pin tools
kb_pin
As CLI pin; the base to pin is bundlePath
rather than a positional, and the layer flags become one layer parameter.
Idempotent.
Parameters: bundlePath required; optional mode (full: always emit this
base's records whole, still under the block budget — index: never upgrade —
absent: the profile's full-under threshold decides), profiles (string[], the
context profiles this pin surfaces in; absent means all), layer (project |
local | user, default project, the committed .strauss/kb-pins.json), and
frozen (boolean: true concludes the base so writes against it refuse,
false lifts the freeze).
{ "bundlePath": "/repo/docs/adr", "mode": "full", "layer": "project" }
kb_unpin
As CLI unpin: remove a base from every
manifest layer that holds it, reporting which were touched. Parameter:
bundlePath.
{ "bundlePath": "/repo/docs/adr" }
kb_pins
As CLI pins: every pinned base across the
layers, each with its layer and whether it currently resolves to readable
records. Takes no parameters.
{}
kb_context
As CLI context, with the flags as camelCase
parameters. Emits nothing when nothing is
pinned, and refuses with the list of bases and their sizes rather than
truncating past its budget. Takes no bundlePath.
All parameters are optional: budgetTokens (ceiling on the whole block, default
4000), fullUnderTokens (per-base threshold applied before the budget: a base
whose complete load fits under it arrives as full records; off by default),
profile (named budget set — built-ins session-start (full-under 1500),
compact and turn (budget 2500); an unknown name falls through to defaults),
excludeTags (string array — records carrying one stay out of the block, the
base still pinned and still readable through the tools), format (markdown |
json — the CLI envelope for hook protocols requiring strict JSON on stdout;
MCP callers omit it, the block itself is identical), and event (string, the
hookEventName stamped into that envelope, only meaningful with
format: "json").
Budgets and exclusions resolve most-specific-first: explicit parameters, then
the workspace manifests' context tables (per profile, over their default),
then the built-in profile, then package defaults. No profile excludes a tag by
default.
{ "profile": "session-start" }