● meerkat · knows
meerkat — CLI & integrations
The full mk command surface, plus how to expose the knowledge base to agents over MCP and to web tools over HTTP.
The CLI
Every subcommand works under both meerkat and mk. Page IDs are slash-paths from the wiki root without the .md extension (e.g. concepts/Rate-Limiting), optionally prefixed with <collection>: when several collections are mounted.
| Command | Description | Example |
|---|---|---|
search | Full-text BM25 search over the served wiki. | mk search "rate limiting" --limit 20 |
show | Print a single wiki page (raw markdown). | mk show concepts/Rate-Limiting |
list | List pages, filtered by prefix/category/status/owner/type. | mk list --prefix systems/backend/ |
mcp serve | Run the MCP server over stdio. | mk mcp serve |
mcp serve-http | Hosted MCP over Streamable HTTP with OIDC auth. | mk mcp serve-http --port 4005 |
http serve | Run the HTTP/OpenAPI server with bearer auth. | mk http serve --port 4004 |
ingest | Plan (and with --execute, run) ingestion. | mk ingest --source policies --execute |
ingest sources | Print the ingestion source registry. | mk ingest sources --json |
update | Check for / install meerkat updates. | mk update --check |
lint | Check for dangling related: and pointer targets; exit 1 if any. | mk lint |
collections status | Report how current each refreshing type: local collection is; --check exits 1 when one is behind its remote. | mk collections status --check |
version | Print version info and content provenance. | mk version --json |
Global flags
These apply to every subcommand and select which knowledge base is served. They are resolved once, before the subcommand runs.
| Flag | Effect |
|---|---|
--kb-dir string | Serve content from a directory in the content-repo layout. Wins over everything else. |
--content-source string | Path to a content-source.yaml. A nonexistent path is a hard error. |
Search flags
| Flag | Effect |
|---|---|
--limit int | Max hits (default 10, server-clamped to 100). |
--body | Print the full body of each hit. |
--collection string | Search only this named collection (default: all, merged by score). |
--json | Machine-readable output. |
mk search "exact phrase" # phrase match
mk search +cache -queue # must / must-not
mk search title:eviction # field-targetedList filters
Filters compose with AND, and are applied to frontmatter after loading — they are not query terms.
| Flag | Effect |
|---|---|
--prefix string | Page IDs starting with this prefix. |
--category string | Frontmatter category. |
--status string | Frontmatter status. Free string — meerkat and OKF vocabularies both pass through untranslated. |
--owner string | Frontmatter owner. |
--type string | Frontmatter type — OKF's concept-kind field, e.g. "BigQuery Table". |
--collection string | List only this named collection. |
--collections | Enumerate the mounted collections instead of pages. |
show --json carries trust metadata
mk show <id> --json returns trust_tier and stale as top-level siblings of front — not fields inside it. Both are computed on read, never stored. See OKF bundles.
OpenCode (MCP over stdio)
Register meerkat as a local MCP server and your agent gains mk_search, mk_show, mk_list, mk_list_collections, and mk_report_outcome on every prompt — plus mk_save_memory when a collection has a memory: block.
{
"mcp": {
"meerkat": {
"type": "local",
"command": ["mk", "mcp", "serve"],
"enabled": true
}
}
}Search, then show
The recommended agent pattern is mk_search first (the snippet triages which page), then mk_show on the winner — it keeps the context window lean.
| Tool | Parameters | Returns |
|---|---|---|
mk_search | query (required), limit, bundle, session_id, collection | id, collection, title, category, status, score, snippet |
mk_show | id (required), session_id, collection | id, collection, path, title, body, front, trust_tier, stale |
mk_list | prefix, category, status, owner, type, collection | id, collection, title, category, status, owner, type, source |
mk_list_collections | — | Every mounted collection with its description and contribution contract. |
mk_save_memory | a structured memory document + scope (personal / team / global) | Where it landed — saved and indexed, or staged for review. |
mk_report_outcome | outcome (required), initial_query, pages, attempted, quality, fallback, session_id | Whether the outcome was recorded to the traversal log and, for a fallback, to an intake draft. |
The read tools are annotated read-only, idempotent, and closed-world, so a harness can call them without a confirmation prompt. The two writers are mk_save_memory, which only appears when a collection opts in, and mk_report_outcome, which records how a retrieval ended to the traversal log and intake drafts — and only where those stores are configured.
Hosted MCP (Streamable HTTP + OIDC)
mk mcp serve-http serves the same MCP tools over Streamable HTTP for the deployment stdio can't model: one meerkat, many people, different people allowed to see different collections. meerkat acts as an OAuth 2.0 protected resource — it validates JWTs from configured OpenID Connect issuers (Entra ID, Google, Okta are configuration, not code) and never issues tokens itself. Authorization is per collection via an auth: block in content-source.yaml (or a standalone policy file via --auth-config).
mk mcp serve-http --port 4005 # binds 127.0.0.1, mounts /mcp
# probes & metrics: /livez /readyz /metrics
# SIGHUP runs every refresh: cycle and rebuilds type: local indexes;
# the auth policy is not reloadedUnauthorized means invisible, not denied
A collection a caller can't read is absent for them — missing from tool descriptions, search results, listings, and even error messages — exactly as if it were never mounted. Capabilities are read, personal-write / team-write / global-write (gating mk_save_memory per scope), and admin; rules union, and there are deliberately no deny rules.
With no auth: block the hosted server is unauthenticated, like stdio — over HTTP. As with mk http serve, terminate TLS at a reverse proxy; meerkat serves plain HTTP.
OpenWebUI (HTTP / OpenAPI)
Serve the knowledge base over HTTP with a bearer token, then point OpenWebUI at the OpenAPI schema. The default bind is loopback.
export MEERKAT_API_KEY=$(openssl rand -hex 32)
mk http serve --port 4004 # binds 127.0.0.1 by default
curl -sS http://127.0.0.1:4004/healthz| Method | Path | Auth |
|---|---|---|
| POST | /search | Bearer |
| POST | /show | Bearer |
| POST | /list | Bearer |
| GET | /collections | Bearer |
| GET | /openapi.json | none |
| GET | /healthz | none |
| GET | /.well-known/security.txt | none |
| GET | / | none |
Auth is deny-by-default: anything not explicitly public requires the bearer token, so an unmatched path returns 401 rather than 404. The server refuses to start without a key (no anonymous mode) or with one shorter than 16 characters; the key is compared in constant time. Pass it as MEERKAT_API_KEY or with --api-key-file (mode 0600) rather than --api-key, which other local users can read from the process list. /healthz, /openapi.json, and /.well-known/security.txt (meerkat's RFC 9116 security contact; --security-txt=false turns it off) are public so probes and tool registration work without it.
Put a reverse proxy in front of it
meerkat serves plain HTTP and terminates no TLS of its own. To reach it from another host, front it with a reverse proxy that terminates TLS rather than binding it to 0.0.0.0 directly.
POST /list accepts the same type filter as the CLI, POST /show returns trust_tier and stale alongside the page, and all three POST bodies accept an optional collection field.
Environment variables
| Variable | Effect |
|---|---|
MEERKAT_KB_DIR | Content directory to serve (the --kb-dir flag wins over it). |
MEERKAT_CONTENT_SOURCE | Path to a content-source.yaml (the --content-source flag wins over it). |
MEERKAT_API_KEY | Bearer token for mk http serve, at least 16 characters (env wins over --api-key and --api-key-file). |
MEERKAT_NO_UPDATE_CHECK=1 | Silence the post-run "newer release available" check. |
MEERKAT_TRACES_ENABLED + OTEL_* | Opt into OpenTelemetry traces for the hosted server. Off by default: with no observability: block and no OTEL_* variable, no SDK is constructed at all. |
Authoritative reference, generated from the component repos. Spot something stale? Tell us.