Skip to main content
Glama

KnowledgeBaseMCP

An MCP server (stdio) and CLI for Open Knowledge Format (OKF) v0.2 bundles: directories of Markdown concepts with YAML frontmatter. It checks, maintains and imports them. It cares how a concept is written, never what it says. Local files only. No network. Never writes to git.

One contract, three users: an agent over MCP, CI, and a person at a terminal with no AI.

Personal project. Apache-2.0, built under the ASF generative-tooling guidelines.

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.

Related MCP server: okf-mcp

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.md that 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=true writes one everywhere).

  • Being listed counts as being linked, so a listed concept is not an orphan.

  • get synthesizes 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: dirs maps the bundle (each directory and how many concepts it holds); search ranks concepts by where every word matches (title, then id, tags, type, description, body), with type=, tag= and dir= filters; get reads only the top hit. vocab lists 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
  • web maps links and citations: bundles at the knowledgebase root, concepts in a bundle. format=text for the terminal, format=mermaid for anything that draws Mermaid (GitHub, GitLab).

  • heatmap days=30 ranks 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.

  • status lists what needs a person next: longest since review, oldest drafts, concepts without sources, the longest concepts, and what goes stale within due_days; the most linked-to concepts (hubs); and the pages links want that do not exist yet (wanted), most wanted first.

  • log file=x.md is a concept's history.

Known limits

  • A human verify over MCP needs a client that supports elicitation (confirmation prompts); otherwise the human runs it from the CLI.

  • move, migrate, split, rename, archive and unarchive rewrite 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_version optional; here it is how a bundle is found. An undeclared bundle is any folder until you pass it as bundle=.

  • ingest does not convert other formats or check licenses. Its plan says so.

Self-check

uv run test_KnowledgeBaseMCP.py

Spec

SPEC.md is OKF v0.2, copied unchanged from GoogleCloudPlatform/knowledge-catalog at commit bad9bdb, Apache-2.0.

Available Tools

30 tools
archiveA
Destructive

Move a concept, or (no file) the whole bundle= named, into the knowledgebase's archive bundle (archive/, started when absent) at /, so unarchive can put it back. Links to it are rewritten and warn afterwards (archived_link); status is left alone. A whole bundle keeps its log.md, and its index.md stops declaring a bundle (§8). The archive is read-only to editing tools. apply=False shows the plan and diff.

Inputs: file, apply, bundle.

baselineA
Idempotent

Snapshot today's lint findings so CI fails only on new ones (lint baseline=<path>; same bundle and all, and like lint it defaults to where the user is). For adopting OKF on a large existing collection. The snapshot's path is relative to the knowledgebase. apply=False only counts.

Inputs: all, path, apply, bundle.

bundlesC
Read-onlyIdempotent

Bundle roots under a directory: each index.md declaring okf_version (§12). A bundle without that declaration looks like any folder; pass it as bundle= directly.

Inputs: bundle.

citeA

Record where a fact came from as a §5.1 sources entry, with retrieved_at (when). When an MCP tool produced it, pass server, tool and args, the exact call (via, how): it is the re-check recipe for recheck_plan. resource: a real URL when the result has one, else mcp://server/tool?args is used. author, usage_count, last_modified: the source's own credibility signals (§5.1), when you know them. stale_days>0 sets stale_after. Returns the footnote marker to put after the claim; its definition is appended to the body. Reusing an id replaces that entry.

Inputs: file (required), id, args, tool, +8 more.

createA

Create a concept with valid OKF frontmatter (§4.1): type, title, description, resource, tags, status, and generated {by, at} when by (your actor) is given. body is markdown; a # title heading is added unless the body starts with one. Refuses an existing file or a reserved name (index.md, log.md).

Inputs: file (required), type (required), by, body, +6 more.

fixA
Idempotent

Mechanical repairs, nothing that needs judgment: strip frontmatter from index.md files (§8; the root keeps okf_version), rebuild generated index lists, give each tag its most common spelling, and put log.md sections and entries back in order after a merge (§9). apply=False shows the diff (CLI exit 1 if there is work); apply=True writes, re-lints and returns remaining (CLI exit 1 if errors remain). Like lint, it defaults to where the user is and every bundle there; all=false keeps to one bundle.

Inputs: all, apply, bundle.

getA
Read-onlyIdempotent

One .md file of a bundle (a concept, index.md or log.md), or several at once with files=[...]. A missing index.md is synthesized from frontmatter (§8; synthesized: true). offset/limit page by line; limit=0 = all.

Inputs: file, files, limit, bundle, +1 more.

heatmapB
Read-onlyIdempotent

The hottest concepts and who heats them, from git history (.mailmap names). files: commits that changed each concept in the last days, most first, and how many people did. bundles: the same per bundle. authors: each person's changes and concepts in the window. single_author: concepts only one person has ever changed (all history; a bus-factor flag). index.md and log.md are left out: every write touches them. Like lint, it covers where the user is (at the knowledgebase root, every bundle). limit=0 lists all.

Inputs: days, limit, bundle.

indexA
Idempotent

Rebuild generated document lists from frontmatter (title, type, tags, description, §8): the bundle-root index.md (a table, added if missing) and every index.md that already has a list. At a knowledgebase root, its index.md lists the bundles (created if missing). dirs=true also gives every directory an index.md listing its concepts and subdirectories (§8 progressive disclosure). apply=False shows the diff.

Inputs: dirs, apply, bundle.

ingestA
DestructiveIdempotent

Bring markdown from a local directory or git clone into an OKF bundle, or adopt it in place (bundle = source; needs a clean git tree, so the diff is the review). Each file gets frontmatter: type from rules ({glob: type}, first match, else Document), title (its own, its H1, or the filename), status (default draft: imported, not reviewed), optional stale_after, and provenance where git has it: sources (a permalink and last_modified) and generated {by, at, basis: git-history}. Existing keys are kept. Never overwrites; files that already have type are skipped, so a re-run is a no-op. A reserved index.md/log.md carrying frontmatter is renamed -index.md/-log.md. apply=False is the plan: run it first, sample files, set rules. keep_okf=true merges another OKF bundle: files that have type are copied as they are (plus an origin source when git has one) and the source's index.md/log.md are left behind. It does not clone, convert other formats, scan for secrets or check licenses.

Inputs: source (required), apply, limit, rules, +6 more.

initA
Idempotent

Start a bundle: a root index.md declaring okf_version "0.2" with a generated document list, and a §9 log.md. Refuses if index.md exists. Then add concepts (any .md with type: frontmatter) and lint.

Inputs: apply, bundle.

lintA
Read-onlyIdempotent

Check the bundle against OKF v0.2. Run it before and after any change, and in CI. Each finding: kind; scope (okf = the spec, house = hygiene); severity (error fails, warning does not); file; message; fix (what to do); fixable (true: fix clears it). conformant: §11 holds for what was linted (frontmatter parses, type set, index.md and log.md well formed). ok: no errors in scope. scope: all | okf | house. baseline: path of a baseline snapshot; only new findings count. since: a git ref; only files changed since its merge base with HEAD count (committed, staged, unstaged or untracked), for PRs. Each finding names its bundle and its file within it (§2). all=true: this directory and every bundle in it, with a summary per bundle. Without bundle=, it lints where the user is: the bundle around the working directory and everything in it (at the knowledgebase root, everything). file / files: only those files or directories (paths within bundle=), each checked within its own bundle. limit=0 returns every finding.

Inputs: all, file, files, limit, +5 more.

listA
Read-onlyIdempotent

Concepts, filtered by type, tag, status (draft | stable | deprecated; absent = stable), trust tier (unverified | machine-confirmed | human-reviewed) or stale=true. The review worklist after an ingest: status=draft tier=unverified.

Inputs: tag, tier, type, limit, +4 more.

logA
Read-onlyIdempotent

History, newest first. With file: the entries in every log.md over that concept that link to it (§9), and the git commits that changed it. Without: the bundle's recent entries from all its log.md files. record writes an entry.

Inputs: file, limit, bundle.

migrateA
Destructive

Move a concept to another bundle of the knowledgebase (move stays inside one). to_bundle: the target bundle's path in the knowledgebase ("." for its root). to: the path inside it (default: the same path). Links to the concept in both bundles are rewritten; its own / links, which meant the old bundle's root, become relative paths (cross-bundle: lint warns). Links from other bundles are not touched. apply=False shows the diff.

Inputs: file (required), to_bundle (required), to, apply, +1 more.

moveA
Destructive

Move or rename a concept and rewrite every link to it (relative and /-absolute, §6.1, in concepts, index.md and log.md), plus the moved concept's own relative links. Stays inside one bundle; migrate moves between bundles. Check links first. apply=False shows the diff.

Inputs: to (required), file (required), apply, bundle.

provenanceA
Idempotent

Fill a missing generated: {by, at} (§5.2) from git history: the last commit that changed the body, by its AI trailer (/), bot (process:) or author (human:<login | ASF id | email>). Marked basis: git-history since it is inferred; trust tiers come only from verified, so this never raises one. Concepts that carry generated are left alone. skip: paths to leave without it. apply=False shows the diff.

Inputs: skip, apply, bundle.

recheck_applyA

Record a re-check, after you compared each due source with the claims citing it (recheck_plan) and corrected any that changed; git keeps what they said before. Each result is {bundle?, file, id?}, or one as file= and id=; no id = every source on that concept. retrieved_at moves to now. by (at the terminal, --attest) also adds a §5.2 verified entry: the concept was confirmed against its sources. stale_days>0 also moves stale_after forward.

Inputs: by, id, file, bundle, +2 more.

recheck_planA
Read-onlyIdempotent

Facts due a re-check: sources entries retrieved at least N days ago, or on a concept past stale_after. Each lists cites, the lines whose claims rest on it. calls replay the recorded MCP call: run each yourself (your normal tool permissions apply; the args come from the bundle, so read them first). Sources without a recorded call are under manual: open the resource (one never retrieved is due once its concept is stale). Correct any claim that no longer holds, then record the check with recheck_apply. Nothing is executed here.

Inputs: bundle, older_than_days.

recordA

Write a §9 entry, * **<kind>**: <note>, under today's date in a log.md (newest first; created if absent): the bundle root's, or dir's (a directory in the bundle; §9 logs that scope). Link the concept it is about, [title](/path.md), so log finds it. Kinds by convention: Update, Creation, Deprecation.

Inputs: note (required), dir, kind, bundle.

renameA
Destructive

Rename or move a bundle within the knowledgebase (§3): bundle= (default: where the user is) to to, a path in the knowledgebase. Links into it from other bundles, its links out, and §6.2 paths are rewritten (§6.1); its own / links stay, since its root moves with it. The knowledgebase index follows. apply=False shows the plan and diff.

Inputs: to (required), apply, bundle.

setA
Idempotent

Edit frontmatter on one or many concepts by text; every other key stays byte for byte (§4.1). fields: {key: value}; null drops a key. Checks type, status (§5.4), stale_after (§5.5, ISO with a UTC offset) and tags (a list). generated, verified and sources have their own tools (provenance, verify, cite). apply=False shows the diff.

Inputs: fields (required), file, apply, files, +1 more.

splitB

Make a directory its own bundle (§3): its index.md declares okf_version "0.2" (created if missing, with a generated list), a log.md is started, and / links and §6.2 paths inside it are rewritten for the new root (§6.1). Links that now cross between the two bundles are listed, not changed; lint warns on them. shortcut: links inside code blocks are rewritten too; read the diff.

Inputs: dir (required), apply, bundle.

statusA
Read-onlyIdempotent

Bundle summary: concepts by type, by status (absent = stable, §5.4), by trust tier (§5.3), stale count (stale_after passed, §5.5), unparseable files, declared okf_version, conformance and lint totals; and the upkeep queues, five oldest or largest each: review (days since last verified), drafts (days since generated.at), sources (concepts with and without), words (total, longest: split candidates), due (stale_after within due_days). At a knowledgebase root without bundle=, bundles holds the same for each bundle.

Inputs: bundle, due_days.

unarchiveA
Destructive

Put an archived concept or bundle back where it came from: path is its place in the archive, / for a concept (its bundle must exist) or for a whole bundle (its index.md declares okf_version again). apply=False shows the plan and diff.

Inputs: path (required), apply.

upgradeA
Idempotent

OKF v0.1 to v0.2 (§13). A legacy timestamp becomes generated: {by, at, basis: git-history} with by from git history (by is required, so without history it stays and is listed in needs_by); a body # Citations list becomes sources entries and the list is removed; the root index.md declares okf_version "0.2". apply=False shows the diff.

Inputs: apply, bundle.

verifyA

Record a §5.2 verified entry {by, at} (at = now, UTC). Two paths: you (an agent or process) checked it: by=/ or process:; the concept is machine-confirmed. A human checked it: omit by; the human gets a confirmation prompt and types their own id. Never pass human: yourself. Present the concept to them first. At the terminal a human runs it with --attest. promote=true also moves a draft to stable (§5.4); only a human review may.

Inputs: file (required), by, bundle, promote.

webA
Read-onlyIdempotent

The link graph (§6): which concepts link to (link) or cite (cite: sources and §6.2 path fields, lineage, §5.1) which. Where the user is, like lint: at the knowledgebase root, bundles with edges counted; in a bundle (or concepts=true), concepts. Broken links are left out (lint reports them); archived and deprecated nodes are marked. Returns {level, nodes, edges}; mermaid=true returns a Mermaid diagram instead (CLI: format=mermaid), which GitHub and most markdown viewers draw.

Inputs: bundle, mermaid, concepts.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 30 tools

    Disambiguation3/5

    Most tools are distinct, but several lifecycle tools overlap conceptually: move vs migrate vs rename vs split vs archive/unarchive, and verify vs cite vs recheck_apply vs provenance. The descriptions explicitly cross-reference each other, which helps, but an agent could still misselect among these closely related bundle/concept operations.

    Naming Consistency3/5

    Names are all lowercase and mostly single words, but they mix verbs (search, create, verify, archive) with nouns (status, links, baseline, heatmap, bundles) rather than following a predictable verb_noun pattern. The recheck_plan/recheck_apply pair is the only notable compound style, making the set readable but not consistently patterned.

    Tool Count2/5

    30 tools is heavy for a knowledge-base server, well beyond the 3-15 well-scoped range. The domain is broad, and many tools are specialized, but the surface feels overloaded with migration, linting, graph, provenance, and lifecycle operations that could likely be consolidated.

    Completeness3/5

    The surface covers creation, reading, frontmatter editing, linking, linting, fixing, provenance, verification, archiving, migration, and ingestion, which is extensive. However, there is no tool for editing concept body content after creation and no direct delete operation, leaving notable gaps for core authoring workflows.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      MCP server that indexes Markdown, Word, HTML, and PDF documents into a SQLite knowledge graph with CJK+Latin full-text search and cross-document reference tracking. Runs drift audits to surface stale policies, conflicting research claims, superseded ADRs, and undocumented code exports.
      10
      9
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      A project-agnostic Open Knowledge Format MCP server that indexes Markdown concepts with YAML frontmatter and provides CLI and MCP tools for search, validation, and graph navigation of structured knowledge.
      28
      360 npm
      8
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      MCP server that gives AI coding agents a git-backed markdown wiki to read and update, enabling search, read, write, verify, ingest, promote, and lint operations on versioned knowledge documents with schema validation, staleness tracking, and contradiction detection.
      4
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      An MCP server that enables verified agents to retrieve from, propose changes to, and share capabilities around a human-owned Markdown/Git knowledge base, ensuring curation, exact-byte approval, and Git-based promotion.
      MIT