Guide#
AI-generated
Drawn from the README, written with Claude Opus 5.5, under human review.
Run#
Requires uv.
A knowledgebase is wherever you point the tool: KB_ROOT, else the current
directory. It is one bundle at its root, or a set of sibling bundles. A
directory whose index.md declares okf_version is a bundle root; bundles do
not nest. An index.md at the knowledgebase root lists its bundles (index
apply=true there writes it), and tools find the root from inside a bundle
through it. bundles lists them, split makes a directory its own bundle,
rename moves or renames one, migrate moves a concept between bundles,
archive and unarchive retire and restore a concept or a whole bundle,
ingest keep_okf=true merges one in, and upgrade converts OKF v0.1.
Tools follow where you are: in a bundle, that bundle; at the knowledgebase
root, every bundle (lint, fix, baseline, status, list, search,
recheck_plan). Name files or directories to lint just those
(lint notes/a.md docs/). Files are named the OKF way: a bundle and the
path within it (bundle=eng file=runbooks/deploy.md), in every input and
output. On the command line, without bundle=, paths are relative to where
you are. Write tools act in the bundle the file belongs to. In a git work
tree, files git ignores are skipped.
wget https://raw.githubusercontent.com/dfoulks1/KnowledgeBaseMCP/main/KnowledgeBaseMCP.py
uv run KnowledgeBaseMCP.py help # every tool, one line each
uv run KnowledgeBaseMCP.py help move # one tool: what it does, its arguments
uv run KnowledgeBaseMCP.py lint format=text
uv run KnowledgeBaseMCP.py init apply=true # start a new bundle
Any tool runs as <name> name=value ... or <name> '<json object>'. Bare
arguments fill the required ones in order (move a.md b.md); on set, any
other key=value is frontmatter (set a.md status=deprecated tags=[x,y]).
Write tools that touch many files default to apply=false and show a diff.
--attest records you, as human:<KB_USER> (else your git user.email), on
create and provenance (generated), verify and recheck_apply
(verified). It exists
only on the command line: no MCP call can pass it.
Output is JSON; format=text prints one key per line and diffs as is.
Exit codes: 0 ok, 1 findings, 2 usage error.
Layout#
kb/ the knowledgebase (KB_ROOT)
index.md lists the bundles; no frontmatter
.gitattributes log.md and index.md merge=union
eng/ a bundle
index.md okf_version, then the list of its concepts
log.md its history (§9)
runbooks/
index.md optional: this directory's concepts
deploy.md a concept
archive/ the archive: a knowledgebase of its own, set apart
index.md lists the archived bundles
eng/old-runbook.md archived from eng (archive/eng is a bundle)
index.md#
OKF §8: an index lists what is in its directory, for progressive disclosure. Every index has the same body shape, sections under headings with one entry per item:
# Engineering
Hand-written text is kept.
<!-- kb-index:start (generated by KnowledgeBaseMCP index; do not edit between the markers) -->
### runbooks
* [Deploy](./runbooks/deploy.md) - How we deploy.
<!-- kb-index:end -->
- The tool owns what is between the markers and rebuilds it from frontmatter
(title, else first heading, else file name;
description, else the first sentence). Write anything else around them. - Knowledgebase root: no frontmatter (it is not a bundle). The list has one
entry per bundle, titled by that bundle's first heading. Tools find the
knowledgebase by walking up from a bundle to the nearest
index.mdthat links to it. - Bundle root: frontmatter holds only
okf_version: "0.2", which is what makes the directory a bundle (§12). The list covers every concept, a section per top-level folder. - Any other directory: no frontmatter. The list covers that directory's
concepts and subdirectories (
index dirs=truewrites one everywhere). - Being listed counts as being linked, so a listed concept is not an orphan.
getsynthesizes a missing index from frontmatter (§8 allows this).
log.md#
OKF §9: date headings, newest first, one entry per line. Each tool write adds an
entry to the nearest log.md above the change, with a hidden unix-time stamp:
## 2026-10-27
* <!-- 1761588000 --> **Update**: Moved deploy.md to [ship.md](/runbooks/ship.md).
record "note" writes one by hand (link the concept it is about). log file=x.md
reads a concept's history: the entries linking to it in every log.md above it,
and the git commits that changed it. log alone shows the bundle's recent
entries.
init adds log.md merge=union and index.md merge=union to the
knowledgebase's .gitattributes, so git keeps both sides when parallel
branches log or index. After such a merge, fix merges duplicate log date
sections, orders stamped entries by time, and rebuilds the generated index
lists. A section with a
hand-written, unstamped entry keeps its order.
The archive#
archive/ is a knowledgebase within the knowledgebase, and a special one. Its
index.md lists its bundles, like the knowledgebase root's. archive file=x.md
bundle=eng moves a concept to archive/eng/x.md (starting archive/eng as a
bundle); archive bundle=eng moves the whole bundle to archive/eng/, still a
bundle. unarchive eng/x.md or unarchive eng puts them back. Links follow
both ways.
The live knowledgebase does not see the archive: lint, list, search, status
and web leave it out, nothing in it counts as nesting, and lint warns on links
into it (archived_link). Editing tools refuse archived concepts. That is the
only enforcement: the files are plain files, so an editor or a commit can still
change them. It works whether the knowledgebase root is a set of bundles or a
bundle itself.
Register with an MCP client#
Add this to your MCP client's servers file: .mcp.json in the project for
Claude Code, claude_desktop_config.json for Claude Desktop, .cursor/mcp.json
for Cursor. VS Code's .vscode/mcp.json takes the same entry under "servers".
{
"mcpServers": {
"knowledgebase": {
"command": "uv",
"args": ["run", "/path/to/KnowledgeBaseMCP.py"],
"env": { "KB_ROOT": "/path/to/kb", "KB_USER": "your-id" }
}
}
}
KB_ROOT is the knowledgebase (else the client's working directory). KB_USER
prefills your id in confirmation prompts. Or, in Claude Code:
claude mcp add knowledgebase --env KB_ROOT=/path/to/kb -- uv run /path/to/KnowledgeBaseMCP.py
The client loads the tool list at start. After editing the script, restart
the client or reconnect the server (/mcp in Claude Code).
Before you push#
uv run KnowledgeBaseMCP.py lint format=text # what's wrong, each with a fix
uv run KnowledgeBaseMCP.py fix apply=true # mechanical repairs, then re-lint
uv run KnowledgeBaseMCP.py lint format=text # what's left needs you
scope=okf limits the check to the spec; scope=house to hygiene (links,
anchors, orphans, islands, stubs, duplicate titles, reference links,
filenames, tags, generated indexes). only= and ignore= keep or drop finding
kinds: lint only=stub,island.
CI#
Vendor KnowledgeBaseMCP.py into the bundle's repo, then:
# .github/workflows/okf.yml
name: okf
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # provenance needs full history
- uses: astral-sh/setup-uv@v6
- run: uv run KnowledgeBaseMCP.py lint scope=okf format=github
Findings show as annotations on the PR. format=sarif suits code scanning.
On PRs, since=origin/${{ github.base_ref }} limits findings to the files the PR
changed.
On a large existing collection, commit a baseline apply=true snapshot and
run lint baseline=.okf-baseline.json, so only new findings fail. The
snapshot sits at the knowledgebase root and matches from any directory. CI lints;
it does not push fixes (fork PRs get a read-only token anyway).
To skip files outright, list them in a kbmcp.nolint file. It works like
.gitignore: it may sit in any directory, patterns are relative to it, the
last match wins and ! re-includes. At the knowledgebase root it covers every
bundle. Skipped files are still read, so links to them resolve. One file can
skip itself with the line <!-- kbmcp-nolint -->.
# kbmcp.nolint: agent instructions are not concepts
AGENTS*
CLAUDE.md
.kiro/
pre-commit, lint only (a hook that edits files leaves the commit unstaged):
repos:
- repo: local
hooks:
- id: okf-lint
name: OKF lint
entry: uv run KnowledgeBaseMCP.py lint format=text
language: system
pass_filenames: false
Reading the knowledgebase#
- Find things in three steps, each small:
dirsmaps the bundle (each directory and how many concepts it holds);searchranks concepts by where every word matches (title, then id, tags, type, description, body), withtype=,tag=anddir=filters;getreads only the top hit.vocablists the tags and types in use, the words those filters take.
uv run KnowledgeBaseMCP.py dirs format=text
uv run KnowledgeBaseMCP.py search deploy rollback dir=runbooks format=text
uv run KnowledgeBaseMCP.py get runbooks/deploy.md format=text
webmaps links and citations: bundles at the knowledgebase root, concepts in a bundle.format=textfor the terminal,format=mermaidfor anything that draws Mermaid (GitHub, GitLab).heatmap days=30ranks concepts by how many commits changed them (the ones most likely to collide across branches) and by how many people did; who changes the most; and concepts only one person has ever touched. index.md and log.md are left out.statuslists what needs a person next: longest since review, oldest drafts, concepts without sources, the longest concepts, and what goes stale withindue_days; the most linked-to concepts (hubs); and the pages links want that do not exist yet (wanted), most wanted first.log file=x.mdis a concept's history.
Known limits#
- A human
verifyover MCP needs a client that supports elicitation (confirmation prompts); otherwise the human runs it from the CLI. move,migrate,split,rename,archiveandunarchiverewrite links (code blocks included; read the diff) and §6.2 path fields (resource,computation,executor/attester,sources[].resource). A value naming an existing file is a path; one naming nothing is a path only if it has a file extension or a leading/,./or../, else a scope description.- OKF makes
okf_versionoptional; here it is how a bundle is found. An undeclared bundle is any folder until you pass it asbundle=. ingestdoes not convert other formats or check licenses. Its plan says so.