Open Knowledge Format (OKF) with markdown-vault-mcp¶
Open Knowledge Format (OKF) is a vendor-neutral convention for a folder of markdown files that serves as curated context for AI agents. A bundle is a directory of notes with YAML frontmatter: one concept per file, the file path as the concept identity, and a small set of frontmatter fields that describe each note's type, lifecycle, provenance, and trust. This guide shows how markdown-vault-mcp recognises OKF bundles, what it does with the metadata, and how to move an existing vault into the format.
Note
OKF support is read-only by default. Recognising a bundle only ever adds annotations and advice; it never changes your files. The migration tools that do change files are explicit, and this guide covers them near the end.
Example pack
The examples/okf/ directory ships a declaration index, typed note templates (Concept, Capture), and a prompt pack (author a concept, verify a note, triage stale content, migrate a vault) that you can copy into your vault and prompt folders.
How the server recognises a bundle¶
The server treats a vault as an OKF bundle when the root index.md declares a version in its frontmatter:
---
okf_version: "0.2"
---
# My bundle
This declaration travels with the vault, so the same bundle behaves the same way here and in other OKF tools. The MARKDOWN_VAULT_MCP_OKF_MODE environment variable controls how the server responds to it:
| Value | Behaviour |
|---|---|
auto (default) |
Apply OKF read semantics when the root index.md declares okf_version. |
off |
Never apply OKF semantics, and hide the OKF tools. Use this if your frontmatter happens to use keys such as status for an unrelated purpose. |
on |
Force OKF semantics even without a declaration. Use this for a bundle you consume but do not own. |
Check the current state at any time through the okf section of the stats tool, or the config://vault resource.
What the metadata means¶
On a recognised bundle, the server reads a few OKF frontmatter families and surfaces them in search, read, and get_context results under an okf key:
- Type: the free-text
typefield, such asPlaybook,Metric, orReference. - Lifecycle:
statusis one ofdraft,stable, ordeprecated. A note with nostatusis treated asstable.stale_aftermarks a note stale from a date on (2026-12-31) or from an instant on (2026-12-31T00:00:00Z; the offset is required). - Trust tier: derived from the
verifiedlist (a single entry may be written as a bare mapping, per the spec). A note verified by a person (by: human:...) ishuman-reviewed; a note verified only by a process ismachine-confirmed; an unverified note isunverified. - Sources: the
sourcesprovenance list, surfaced in full onreadand as a count on search hits.
You can filter on these dimensions. search and list_documents accept status, stale, trust_tier, and type filters, so {"stale": "true"} or {"status": "deprecated"} builds a triage listing. See the tools reference for the full filter set.
Frontmatter fields¶
A conformant note needs only a non-empty type. Everything else is optional but recommended:
---
type: Playbook
title: Onboarding a new teammate
description: Step-by-step setup for the first week.
tags: [process, people]
status: stable
stale_after: 2027-01-01
sources:
- resource: https://example.com/handbook
id: handbook
verified:
- by: human:alex
at: "2026-08-01T09:14:00Z"
---
Migrating an existing vault¶
Adoption is incremental. A note is either conformant or not, and the server tolerates a mix, so you can convert a vault gradually rather than in one step. The recommended order is a ratchet: declare early so new notes are written correctly, then converge the backlog.
1. Audit¶
Run the okf_validate tool to see where the vault stands. It reports conformance as a degree rather than a pass or fail. The report gives the conformant count out of the total, alongside a per-rule breakdown with example paths (notes missing a type, unknown status values, and the like). The audit reads from disk, so it works before you declare anything. Use it to decide whether to start.
The report also lists index.md files that carry frontmatter the format does not allow. That includes index files the server generated: with MARKDOWN_VAULT_MCP_REQUIRED_FIELDS set, the server writes the required fields into them so they stay indexed. Leave those fields in place, because an index without them drops out of search and document listings.
2. Declare¶
Add okf_version: "0.2" to the root index.md frontmatter. From this point the server annotates results, the filters work, and the agent receives guidance to keep OKF conventions when it edits. Declaring early means every new note is authored conformantly while you work through the backlog.
3. Enrich¶
Backfill the missing metadata. This is where most of the work is, and an agent can do it note by note. For each note it proposes a type from the content and fills in the title and description. Approve the changes in batches, then re-run okf_validate to watch the conformant count climb.
When using write to update an existing note or root index, read its current
content first and pass that read's etag as if_match. New files omit
if_match. A failed replacement leaves the existing file intact; report the
conflict before continuing with any dependent migration steps.
4. Mechanical transforms¶
Three tools handle the changes you should not make by hand. They are write tools, so they are hidden in read-only mode and when OKF_MODE is off, and on a git-backed vault each change is committed.
okf_convert_linksrewrites[[wikilinks]]as the root-absolute markdown links OKF recommends, such as[text](/guides/note.md). Only links whose target exists are converted, so your link graph is preserved exactly. Unresolvable wikilinks are left alone and reported. You can write in either link style day to day; run this before sharing the bundle.okf_generate_indexwrites a folder'sindex.mdas a listing of the notes directly in that folder, plus a pointer into each subfolder's ownindex.md. It draws the description from each note's frontmatter and lists one level at a time, so a nested bundle stays navigable rather than flattening into one long page. It preserves existing frontmatter, so regenerating the rootindex.mdkeeps yourokf_versiondeclaration.okf_seed_logcreates alog.mdchange history from the vault's git commits, newest first. Thefolderargument both places the log and scopes its content: seeding a folder includes only the commits that touched that subtree, while seeding the bundle root includes the whole vault's history. It refuses to overwrite an existinglog.md, so a hand-maintained history is safe.
Reserved files on a vault with required fields¶
If you set MARKDOWN_VAULT_MCP_REQUIRED_FIELDS, documents missing any listed field are excluded from the index. The generated index.md and log.md are ordinary documents to the indexer, so the generators give them the fields your vault requires. Your title field is filled in from the file's heading; any other required field is written as null, for you to fill in. Without this the bundle's own listing and change history would be absent from search and list_documents while still opening through read.
Anything already in the file wins, so a title you wrote yourself and the root index.md's okf_version declaration are left alone. On a vault that sets no required fields nothing changes: the reserved files carry no frontmatter, exactly as before.
The enforced write layer¶
The steps above keep a bundle conformant by convention. The enforced write layer makes the server maintain two of those fields on the writes that go through it. Turn it on by setting MARKDOWN_VAULT_MCP_OKF_WRITE=true. It requires OKF_MODE to be auto or on; pairing it with OKF_MODE=off is a configuration error, since there is nothing to enforce. When the flag is off the write path is untouched, so an ordinary vault behaves exactly as before. On a read-only deployment the flag is accepted and does nothing: the write tools are hidden, so there is no write for the layer to act on.
With the layer on, and only while the vault is an active OKF bundle, every write and edit does two things to the note that lands:
- Stamps provenance. The server writes
generated: {by, at}describing the bytes it just saved.atis the instant of the write, in UTC (2026-09-07T05:32:29Z), as the OKF spec requires of every timestamp.byishuman:<subject>when the token carries a usablesubclaim, and a tool actor such asmarkdown-vault-mcp/1.4.0otherwise. Static bearer credentials, including custom and mapped subjects, use the tool actor. OAuth service tokens carryingsubstill receive human attribution; issuer-aware classification is tracked in #1480. It records who called the write, which is not the same as who wrote the words, so an agent writing under your token stamps your identity. Any existinggeneratedvalue is replaced, including one another tool wrote. The field tracks the last write this server saw rather than the note's history. - Invalidates prior review. A content change means an earlier human review no longer describes the current note, so the server clears
verified. This fires on any content-changing write, including an edit that touches only a frontmatter line. Arenamemoves the file without changing its content, so it leaves both fields alone. Clearing is one-way. The entries are removed rather than archived, and a write that changes nothing of substance clears them just the same.
Both fields describe what this server observed on its own write path. A note changed by anything else (a teammate's editor, a git pull, a script) carries whatever its author left there, and the layer neither notices nor corrects that.
The one-shot migration transforms above (okf_convert_links, okf_generate_index, okf_seed_log) are exempt: a mechanical rewrite does not re-stamp provenance or discard a human attestation.
The stamp goes into the note's own frontmatter, so the layer refuses a write whose frontmatter cannot be parsed: there is nowhere to record the provenance it promises. The error names the note and the reason, and nothing is written. Fix the block and write again. With the layer off the write goes through, and the note is left out of the index until its frontmatter parses.
Recording a human review¶
Enabling the layer also exposes the okf_verify tool. Call it on a note you have reviewed and it appends a {by: human:<subject>, at} entry (at a UTC instant) to that note's verified list, which promotes the note's trust tier to human-reviewed. The verification write is exempt from the invalidation above, so attesting a note does not immediately clear the attestation you just added.
One subtlety is worth understanding before you rely on the human-reviewed tier. The authenticated subject is whose token made the call, not proof that a person read the note. When an agent holds your token and attribution rests on the token alone, the model could promote a note to human-reviewed on its own, and the tier would mean nothing. MARKDOWN_VAULT_MCP_OKF_VERIFY controls how the tool guards against that. It applies only when OKF_WRITE is on. Setting it to a non-default value with the layer off is a configuration error, because the tool is hidden and the setting would have no effect.
elicit(the default) makesokf_verifyask you to confirm the review through an MCP elicitation, a prompt your client shows you, and it writes theverifiedentry only when you answer yes. It fails closed. On modern MCP connections the tool returns an input request and your client repeats the call with your answer; on older handshake connections it uses server-initiated elicitation. When your client cannot show the prompt or you decline it, the call fails and writes nothing. Use a client whose elicitation handler actually presents the question to a person: an automatically affirmative handler would defeat the human-review gate. The recorded subject is your token'ssubwhen available, orlocalwhen you use a service credential or no auth.offhides the tool entirely. Use this when reviews are recorded only by tooling outside the server (a CLI step, a git hook, or a CI job that writesverifieddirectly) and you want no in-session path to the tier at all.trust-authattributes to a token'ssubwith no confirmation. It refuses static bearer credentials, other client-ID-only identities, and callers without auth. Choose it only when the sole caller ofokf_verifyis a human-driven interface rather than an agent.
Upgrading bearer-based review clients: deployments that used trust-auth with a static or mapped bearer token must switch MARKDOWN_VAULT_MCP_OKF_VERIFY to elicit and use a client that presents the confirmation to a person. Existing provenance and verification entries are not rewritten.
Whichever mode you pick, human-reviewed means a person deliberately confirmed the review, not that the note is provably correct. Someone can still rubber-stamp a note. The tier promises a deliberate human act rather than diligence, so treat it as one signal instead of a guarantee.
A verified entry is also a claim about the note as it stood when the entry was written. This server clears it when its own write path changes the note, but a change that arrives another way (a teammate's editor, a git pull, a script) leaves the entry in place. On a shared vault, read the tier as a record that someone confirmed a review at that time, and read generated.at beside it.
Keeping log.md and index.md current¶
With the layer on, the server also maintains a folder's reserved files as a side effect of writing into it. After a successful write or edit on a note, it appends a dated **Update** bullet to that folder's log.md (creating the log, and the day's ## YYYY-MM-DD section, when needed) and regenerates the folder's index.md listing so a new note shows up without a manual step. That covers the upkeep the advisory layer otherwise asks the agent to do by hand, on the write paths below and no others.
A few boundaries keep this predictable, and they are also its limits. Maintenance runs only for content writes on an active bundle. A rename or delete does not trigger it, so a listing can name a note that has moved or gone until the next write into that folder. Neither does a write whose target is itself a reserved file. The okf_verify tool and the one-shot migration transforms are exempt, so an attestation or a mechanical rewrite does not churn the reserved files. Only the folder directly containing the written note is refreshed: if a write creates a brand-new subfolder, its pointer in the parent index.md appears on the next write into that parent. A failure to update a reserved file is logged and skipped; it never fails or rolls back the note write that triggered it, so a listing can silently fall behind.
Do not hand-edit a maintained index.md. The write itself is left alone, because the maintainer skips a write whose target is a reserved file. But the next content write into that folder regenerates the listing from the note list, and an edit you made is gone with nothing reporting it. The same applies to anyone else editing the vault. A teammate's change to a maintained index.md in Obsidian, or one arriving through a git pull, survives only until the next write into that folder. Put anything you want to keep in a note, not in the generated navigation. log.md is appended to rather than regenerated, so hand-written entries there are not overwritten, but a conflicting edit from another editor can still collide with the server's append.
One cost comes with this upkeep. Each write or edit waits for the search index to catch up before it regenerates index.md, so that a just-created note is listed; on a busy vault or with a slow embedding backend this adds latency to the write, bounded at 60 seconds. A refresh failure keeps the existing listing and logs a warning; the note write is not rolled back. Embedding jobs already in the queue can delay the refresh, but it does not wait for embeddings queued after it. On a git-backed vault the reserved-file updates are not extra commits: every write a tool call makes is committed together, so saving a note produces one commit naming the note, its log.md and its index.md.
To stop the upkeep, set MARKDOWN_VAULT_MCP_OKF_WRITE=false and maintain the reserved files yourself. Two things to know before you do. If you had set MARKDOWN_VAULT_MCP_OKF_VERIFY to anything but its default elicit, reset it as well. Startup fails otherwise, because that setting only applies while the layer is on. And disabling the layer stops further upkeep without repairing anything already written. Stamps the layer added stay in their notes, and a log.md that collected repeated frontmatter blocks from a version before 4.2 keeps them. Removing those blocks is a manual edit: the server cannot tell a block it stacked from one the log quotes on purpose, so it leaves all of them alone.
Using OKF with PARA or Zettelkasten¶
OKF composes with the PARA and Zettelkasten methods. They organise where notes live and how work flows; OKF describes what each note is. A PARA or Zettelkasten vault can also be an OKF bundle. Three points of overlap matter (each method's guide has a matching section from its own angle: Using PARA with OKF, Using Zettelkasten with OKF):
- Status vocabulary: PARA uses
statusfor workflow state (active,archived), while OKF uses it for lifecycle (draft,stable,deprecated). These mean different things, so keep PARA's workflow state in its own frontmatter key and reservestatusfor OKF lifecycle. OKF preserves any extra keys you add. - Untyped inbox notes: PARA's inbox holds quick captures that are typed later. Give them a placeholder
type: Captureso they are conformant from the start, and let triage set the real type. - Wikilinks: write in whichever link style you prefer. The server resolves both, and
okf_convert_linksproduces the OKF link style when you are ready to share the bundle.
Exporting a bundle¶
When you want a copy to share or hand to other OKF tooling, download one through the standard create_download_link tool with a bundle reference rather than a file path:
create_download_linkwithref="okf-bundle"returns a one-time URL for a zip of the whole vault.create_download_linkwithref="okf-bundle:guides"scopes the zip to a folder subtree.
The export reads from the live vault and never changes it. Wikilinks become the root-absolute markdown links OKF recommends. Convention files (_conventions.md) and the template folder are left out, while the reserved index.md and log.md stay in. Non-conformant notes appear as they are.
It is best-effort packaging rather than a certified publication, in three specific ways. It packages what the index currently holds. A note the index left out, whether excluded by pattern, missing a configured required field, or carrying frontmatter that does not parse, is absent from the archive without being reported. It reads the vault as it goes rather than from a point-in-time snapshot, so a write that lands mid-export can leave the archive internally inconsistent; quiesce writes if that matters. And nothing validates the result: the archive carries no conformance report, and building one does not run okf_validate. Run okf_validate yourself before you publish, and read get_index_status.skipped_files to see what the index is leaving out.
Bundle export needs an HTTP or SSE transport with MARKDOWN_VAULT_MCP_BASE_URL set, the same requirement as any transfer link, and it is unavailable when OKF_MODE is off. A git-backed vault also remains a shareable bundle on its own: the repository is an interchange format.