Sandbox
@dnotitia/akb

MCP knowledge base for agent memory and vaults

AKB gives agents a shared knowledge store with hybrid search, structured tables, files, and URI-based links between documents. It sits behind MCP so Claude Code, Codex, Cursor, Windsurf, and other MCP-aware clients can use the same vaults with permissions and history.

160 stars8 forksPythonUpdated 6d ago
Who it's for

Builders who want their agents to store, search, and reuse project knowledge across sessions.

What it delivers

You can keep project memory in one place and have agents retrieve or write it without re-explaining context.

What it does

Vault-scoped memory

Stores knowledge in Git-backed vaults with collections, documents, tables, and file attachments.

MCP access

Exposes tools through the Model Context Protocol for direct agent read and write workflows.

Hybrid search

Combines dense semantic search with BM25 keyword search in one retrieval path.

Knowledge graph links

Uses URI-based relations like depends_on and related_to to connect notes and docs.

Agent plugins

Ships ready-made plugins such as akb-wiki, akb-sessions, and akb-claude-code for common workflows.

Access control and versioning

Uses Git history and PostgreSQL-backed permissions so vault content stays traceable and scoped.

How to get it

  1. 1If that credential later leaks, is lost, or has to be taken back, rotate it rather than…
    # Break-glass: replace the credential and print the new one once. Nothing
    # stores or logs the value, and the machine-readable report omits it.
    python -m app.cli issue-recovery-admin-credential \
      --expected-username recovery-admin \
      --expected-email recovery-admin@example.com
  2. 2For routine v2 key rotation, generate a new directory while retaining the current public…
    cd backend
    uv run python -m app.cli generate-local-session-keyset \
      --output-dir /secure/akb/local-session-next \
      --retain-jwks /secure/akb/local-session-current/jwks.json

README

AKB — agents reading and writing into a permissioned knowledge vault of docs, tables, and files, linked by a URI graph

AKB — Agent Knowledge Base

Organizational memory for AI agents. Git-backed knowledge base served over the Model Context Protocol (MCP) — agents read and write directly with hybrid semantic + keyword search, structured tables, files, and a URI graph. Drop-in alternative to Confluence / Notion for Claude Code, Cursor, Windsurf, and any MCP-aware agent.

License: BSL 1.1 npm: akb-mcp MCP

Works with

Any agent client that speaks MCP (Streamable HTTP or stdio):

  • Claude Code — CLI / VS Code / JetBrains
  • Claude Desktop — macOS / Windows
  • Cursor, Windsurf, Cline, Continue — via the akb-mcp stdio proxy
  • Custom agents — direct HTTP POST /mcp/ with a Bearer token

The default flow uses a Personal Access Token. Deployments with the optional MCP OAuth Resource Server path turned on (via Keycloak as the AS — see docs/mcp-clients/web-connectors.md) also accept Claude Code's mcp add --transport http + mcp login flow end-to-end, without a PAT.

MCP protocol compatibility

AKB keeps one tool and authorization core behind two protocol adapters:

SurfaceModernLegacy
Direct HTTP /mcp/2026-07-28 stateless server/discover and per-request _meta with Mcp-Protocol-Version / Mcp-Method (and Mcp-Name for named calls)2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25 initialize + Mcp-Session-Id lifecycle
akb-mcp stdio proxy2026-07-28 discovery and per-request metadata2025-06-18 initialize

The proxy answers either handshake locally and normalizes backend calls to the modern stateless contract when available. Legacy backend sessions are used only during a rolling upgrade when modern discovery is unavailable. A process cannot mix generations, and unsupported revisions or conflicting protocol evidence fail closed before a tool or local-file operation runs.

Plugins

Beyond raw MCP access, AKB ships ready-made agent plugins for Claude Code and Codex that wrap common vault workflows:

  • akb-wiki — ingest a source (local file, web URL, GitHub PR / release / commit, Confluence page, or Jira issue) into the vault as a structured document, and answer questions from the vault with grounded, cited synthesis (read-only).
  • akb-sessions — capture a coding session as structured notes: a session report plus follow-up tasks, learnings, ideas, and decisions.
  • akb-claude-code — a Claude Code lifecycle bridge: hooks anchor each session to your AKB memory vault, injecting preferences and recent learnings at the start and writing a recap at the end.
/plugin marketplace add dnotitia/akb        # Claude Code
codex plugin marketplace add dnotitia/akb   # Codex

Install details and credentials: plugins/.

Try it live

A public demo runs at akb-demo.agent.seahorse.dnotitia.ai. Browse and search a small fictional-organization knowledge base — product docs, a company handbook, agent session notes, and an engineering wiki, cross-linked by the URI graph — right in your browser, no signup. To wire it into your own agent, sign up with any email (a throwaway address is fine) and point the akb-mcp proxy at https://akb-demo.agent.seahorse.dnotitia.ai/mcp/.

⚠️ Throwaway demo. It is public, wiped and re-seeded weekly, and runs on minimal resources with no uptime, privacy, or data guarantees. Don't put anything real or sensitive in it — treat every write as public and ephemeral. For real use, self-host with Docker Compose or Kubernetes.

Why AKB

Most knowledge tools are built for humans clicking through a UI. Agents need a different shape: structured documents, semantic + keyword search in one call, explicit relations, and full version history. AKB gives agents a single set of tools (akb_put, akb_search, akb_browse, akb_relations, …) over a backing store of Git bare repos and a PostgreSQL hybrid index.

Retrieval quality

Memory is only useful if the right note comes back. AKB's hybrid retrieval (dense + BM25, source-level dedup) was benchmarked on LongMemEval-S — 500 long-context questions, ~50 chat sessions per question. Recall@5 = 98.4%, with no reranker in the loop.

SystemR@5nRerankerSource
AKB hybrid98.4%500nothis repo
MemPalace hybrid + rerank98.4%450yesMemPalace
gbrain hybrid97.6%500nogbrain-evals
gbrain vector97.4%500nogbrain-evals

Methodology, per-category breakdown, and a one-command reproducible harness live in eval/longmemeval/. The embedding model differs across systems (AKB: bge-m3@1024), so read this as a stack-level comparison.

Design philosophy

Core stays small; flexibility comes from extension, not built-in automation. AKB does not ship its own consolidator, summariser, or "knowledge gardener" — instead every write records a structured event in the PostgreSQL outbox. When redis_url is configured, the publisher fans those events out to a Redis Stream (akb:events). Operators wire any external consumer (periodic synthesis bot, doc-rot reaper, weekly-digest agent, audit trail, …) on top, with no patches to the core. The base contract is a read/write store; opinions about what to do with the knowledge live outside.

Architecture

┌──────────────────────────────────────────────────────────┐
│                  Access Layer                            │
│   MCP Server  │  REST API  │  Web UI                     │
├──────────────────────────────────────────────────────────┤
│                  Core Services                           │
│   Document (Put/Get)  │  Search (Hybrid: dense+BM25)     │
│   Relations (graph)   │  Session  │  Publications        │
├──────────────────────────────────────────────────────────┤
│                  Storage Layer                           │
│   Git bare repos       │  PostgreSQL 16 (text + meta SoT)│
│                        │  Vector store (driver):         │
│                        │    pgvector        (default, PG)│
│                        │    qdrant          (optional)   │
│                        │    seahorse-cloud  (managed)    │
│                        │    seahorse-db     (self-hosted)│
│                        │    seahorse-db-grpc(experimental)│
└──────────────────────────────────────────────────────────┘

PostgreSQL is the source of truth — chunk text + metadata + BM25 vocab. The vector store is a driver-pluggable derived index holding dense embeddings and corpus-side sparse vectors. Full vector-store loss is recoverable from PG by setting chunks.vector_indexed_at = NULL and letting the indexing worker re-populate.

Key Concepts

  • Vault — A Git bare repo. The unit of access control and physical isolation.
  • Collection — A directory inside a vault. Topical grouping of documents.
  • Document — Markdown + YAML frontmatter, optimised for agent read/write.
  • Hybrid Search — Dense (semantic) + BM25 (lexical) fused via RRF in one call.
  • Relationsdepends_on, related_to, implements in frontmatter form an explicit knowledge graph.
  • Vault isolation in akb_sql — Enforced by PostgreSQL ACL. Each AKB user has a corresponding PG role (akb_user_<uid>) and each vault has three group roles (akb_vault_<vid>_{reader,writer,admin}). akb_sql runs the user's SQL inside a transaction with SET LOCAL ROLE; cross-vault references return PG 42501 directly. No application-side regex inspects user SQL for forbidden identifiers. See docs/designs/pg-native-rbac/.

MCP Tools (selection)

ToolDescription
akb_list_vaults / akb_create_vaultVault management
akb_put / akb_get / akb_update / akb_deleteDocument CRUD (Git commit + indexing)
akb_put_file / akb_get_file / akb_update_file / akb_delete_fileFile attachments — proxy-side (requires local filesystem)
akb_put_image / akb_discard_imageValidated inline Markdown images — proxy-side in akb-mcp 2.2+
akb_create_table / akb_alter_table / akb_drop_table / akb_sqlTabular content — per-doc tables + SQL
akb_browseTree traversal (collection → docs)
akb_search / akb_grepHybrid search (dense + BM25) / literal grep
akb_drill_downSection-level retrieval
akb_relations / akb_link / akb_unlink / akb_graphKnowledge graph
akb_edit / akb_diff / akb_historyIn-place edit, diff, Git history
akb_grant / akb_revoke / akb_set_publicPermission boundaries — per-user, per-org, public
akb_publish / akb_unpublishPublic publication

Agent memory and session lifecycle are not MCP tools — they live on the dedicated /api/v1/agent-sessions REST surface, driven by AKB lifecycle plugins (akb-claude-code, akb-cursor, …) that hook into the agent's own SessionStart / PreCompact / SessionEnd events. As an agent, your own memory vault (agent-memory-{username}) is browsable through the standard akb_search / akb_browse / akb_get tools exactly like any other vault.

The full tool catalogue is exposed via akb_help() from any MCP client.

Inline document images from MCP

Inline images are hidden document attachments, not browsable Files. Upload a local PNG, JPEG, GIF, or WebP (maximum 10 MiB), then insert the returned Markdown without reconstructing its asset URL:

image = akb_put_image(
  parent="akb://eng/coll/specs",
  file_path="/workspace/architecture.png",
  alt_text="Request processing architecture")

akb_put(
  parent="akb://eng/coll/specs",
  title="Request Processing",
  content="# Architecture\n\n" + image.markdown)

For an existing document, use akb_get followed by a targeted akb_edit(base_commit=...). Do not pass only the image fragment to akb_update(content=...), which replaces the complete body. Image bytes are immutable: replacing an image means uploading a new one and editing the Markdown reference. Remove an image by deleting its Markdown expression; use akb_discard_image only for an upload that never reached a successful document commit. Run akb_help(topic="images") for retention and publication behavior.

The image tools require both the matching backend release and akb-mcp 2.2 or newer. For upgrades, deploy the backend first, then publish/install the proxy and restart existing MCP processes so they load the updated tool list.

Document Format

Every vault resource has a location-aware AKB URI — the canonical handle used by every tool and stored in relations. As of 0.3.0:

akb://{vault}                                          vault root (browse target)
akb://{vault}/coll/{coll_path}                         collection (browse target)
akb://{vault}[/coll/{coll_path}]/doc/{filename}        document
akb://{vault}[/coll/{coll_path}]/table/{name}          table
akb://{vault}[/coll/{coll_path}]/file/{uuid}           file

The /coll/{coll_path} segment is omitted for resources at the vault root. Walking up a URI to its parent collection is a pure string operation — paste the parent into akb_browse(uri=...) to list siblings without an extra lookup.

---
title: "Payment API v2 migration plan"
type: plan              # note | report | decision | spec | plan | session | task | reference
status: active          # draft | active | archived | superseded
tags: [payments, api]
domain: engineering
summary: "REST → gRPC transition plan."
depends_on: ["akb://eng/coll/specs/doc/payment-api-v2.md"]
related_to: ["akb://eng/coll/meetings/doc/2026-05-01-payments.md"]
---

# Payment API v2 migration plan
...

Open Knowledge Format (OKF) compatible

A vault is stored as a git tree of .md + YAML-frontmatter files whose identity is the path — the same model as Google Cloud's Open Knowledge Format (OKF v0.1), which AKB independently arrived at before the spec existed. AKB-authored bundles satisfy all three OKF MUST rules, and AKB can export any vault as a conformant OKF bundle (documents, plus tables/files as concept docs) and validate any bundle:

python -m app.cli okf-export --from-git /data/vaults/_worktrees/<vault> \
    --vault <vault> --out ./okf-out/
python -m app.cli okf-validate ./okf-out/

OKF and AKB are complementary — OKF standardizes how knowledge is written down; AKB stores, versions, searches, governs, and serves it to agents. See okf/ for the mapping and a sample bundle.

Quick Start

The default Docker Compose stack runs four long-lived services: PostgreSQL with pgvector, MinIO, the backend, and the frontend. A one-shot minio-bootstrap service creates the local file bucket before the backend starts. For semantic (dense) search you bring an OpenAI-compatible embedding endpoint (OpenAI, OpenRouter, self-hosted vLLM/TEI, etc.). It is not strictly required: with no embed endpoint (or during an outage) the pgvector and Qdrant drivers degrade to BM25-only lexical search rather than returning nothing — dense is genuinely optional end-to-end (the seahorse-db driver is the exception; see Vector store below). Prefer running a separate Qdrant cluster, or pointing at Seahorse? See Vector store below.

# 1. Configure
cp config/app.yaml.example   config/app.yaml
cp config/secret.yaml.example config/secret.yaml
$EDITOR config/secret.yaml   # set embed_api_key and replace system_hmac_secret

# Generate the installation's persistent RSA-3072 local-session keyset.
# This directory is gitignored; back it up with the other installation secrets.
cd backend
uv run python -m app.cli generate-local-session-keyset \
  --output-dir ../config/local-session
cd ..

# 2. Run
docker compose up -d

# 3. Provision the designated recovery administrator (local mode)
#    The password is read from stdin and is never printed by AKB.
docker compose exec -T backend python -m app.cli provision-recovery-admin local \
  --username recovery-admin --email recovery-admin@example.com \
  --password-file - < /secure/operator/recovery-admin.password

# 4. Open
open http://localhost:3000

config/app.yaml and config/secret.yaml are the single source of application configuration. Mount the config/ directory at /etc/akb/ in any deployment. Process composition is the narrow exception: AKB_PROCESS_ROLE=all|api|worker selects the entrypoint role and AKB_TOKENIZER_PROCESSES=1..4 can lower the per-process tokenizer pool for a deployment container. The Kubernetes base owns those two operational values; business, auth, storage, and provider settings remain in the YAML files.

Compose also runs the API and worker separately. See the local deployment guide for existing-volume upgrades, configuration changes, and remote-access URLs.

Ordinary registration always creates a non-admin account, including on an empty database. Administrator bootstrap is available only through the operator CLI; there is no unauthenticated HTTP bootstrap endpoint. The CLI profile must match auth_mode:

# Local: have AKB generate the password only when an operator-owned output
# file is explicitly requested. A new file is created with mode 0600 and the
# password is not written to stdout, stderr, logs, or application config.
python -m app.cli provision-recovery-admin local \
  --username recovery-admin --email recovery-admin@example.com \
  --generate-password-file /secure/operator/recovery-admin.password

# SSO: pre-bind the product administrator to the exact external identity.
# Username and email are snapshots; issuer + subject are the identity key.
python -m app.cli provision-recovery-admin sso \
  --username recovery-admin --email recovery-admin@example.com \
  --issuer https://issuer.example.com/realms/akb \
  --subject exact-provider-subject

The same exact identity is idempotent. A different designation, an existing username/email, or an already-bound external identity fails closed. The SSO command stores no usable local password and does not contact the identity provider. Generated output files are create-only and never overwritten; for a retry after the file exists, pass that file back with --password-file.

The two local forms differ in one further way. --generate-password-file is AKB producing a credential and handing it over, so the account it creates owes a replacement for it: the first session that credential opens can reach the password change and nothing else, exactly as a password reset behaves. --password-file installs a value the caller already holds — AKB delivers it to nobody — so it arms nothing, and an installation that signs in as this account to bootstrap its own service identity keeps working. To force a replacement for a credential supplied that way, rotate it afterwards with the command below; rotation always leaves the account owing a change.

If that credential later leaks, is lost, or has to be taken back, rotate it rather than reprovisioning the account:

# Break-glass: replace the credential and print the new one once. Nothing
# stores or logs the value, and the machine-readable report omits it.
python -m app.cli issue-recovery-admin-credential \
  --expected-username recovery-admin \
  --expected-email recovery-admin@example.com

Rotation names the account it expects and refuses any mismatch, so it cannot act on the wrong one. The credential it replaces stops working immediately, including one currently in use, and sessions held before the rotation are revoked — both are what a compromise response requires. The same operation is available at POST /admin/recovery-admin/issue-credential, which requires an independent service-administrator token rather than a human session. Rotation is not available in sso mode: the identity provider holds the credential, and nothing in a running AKB can replace it.

Open /admin for the separate product-administration surface. In local mode it accepts the provisioned local administrator and returns the same local-session-rs256-v2 profile used by local human authentication, but it refuses non-admin accounts. In sso mode local credentials are absent: /admin uses a dedicated confidential akb-admin Keycloak client with PKCE and nonce, then accepts only the exact pre-bound (issuer, subject) whose AKB account is still active and is_admin=true.

In SSO mode the same /admin surface can configure a built-in upstream IdP, save it disabled, inspect its exact broker redirect URI, and enable or disable its ordinary-login option without redeploying AKB. The option becomes a usable button only when the server-side browser-session capability is ready. Client secrets are write-only, and an enabled provider must be disabled before reconfiguration. See the SSO provider guide, the standards-based generic OIDC integration, and the stricter Keycloak OIDC reference. Existing Kubernetes installations should also follow the local-to-SSO cutover runbook instead of treating auth_mode as a rolling one-line configuration change.

The dedicated admin callback stores no Keycloak access, refresh, or ID token. It creates a short-lived opaque HttpOnly admin cookie plus a CSRF token; PostgreSQL stores only their hashes plus the exact identity snapshot, and rechecks the account, unchanged external binding, and admin flag on every request. Its one-time OIDC state is also bound to a short-lived HttpOnly cookie so a callback copied into another browser fails before token exchange. Configure keycloak_admin_client_secret, register <public_base_url>/api/v1/admin/auth/keycloak/callback and <public_base_url>/admin in the dedicated client, and keep the admin client ID out of every API/MCP resource-client path. Browser-facing AKB and Keycloak URLs must use HTTPS outside the explicit loopback development exception.

Ordinary SSO login uses the separate akb-web client. The browser receives only an opaque HttpOnly AKB session plus a readable CSRF value; SSO does not mint an AKB user JWT. AKB encrypts the Keycloak refresh/ID token set with the independent sso_browser_session_encryption_key and never persists an access token. The client must map Keycloak's identity_provider user-session note into both ID and access tokens with oidc-usersessionmodel-note-mapper; AKB binds that signed broker alias to the selected enabled provider on callback and every refresh. Production HTTPS cookies use the browser-enforced __Host- prefix, Secure, no Domain, and Path=/; loopback HTTP uses isolated development names. Generate the key as 32 random bytes encoded with unpadded base64url and keep it stable across restarts. See the Keycloak boundary for refresh, logout, and back-channel revocation details.

Local login issues only the versioned local-session-rs256-v2 profile: RS256 with an installation-owned RSA-3072 key, an RFC 7638 kid, exact deployment issuer/audience, jti, and a public-only JWKS at GET /api/v1/auth/jwks. AKB never chooses a verifier from an untrusted token alg header. Upgrading from an HS256 release is an intentional forced-login boundary: generate and persist the v2 keyset before rollout, set jwt_algorithm: RS256, and restart all backends together. Existing HS256 user sessions then receive 401 and must sign in again; PATs and service keys are not revoked. The old jwt_secret may be retained for one release only as migration input for short-lived internal HMAC capabilities, or renamed unchanged to system_hmac_secret; it is never accepted as human-session signing material.

For routine v2 key rotation, generate a new directory while retaining the current public JWKS, publish the new immutable Secret/config revision, and roll every backend to that exact pair:

cd backend
uv run python -m app.cli generate-local-session-keyset \
  --output-dir /secure/akb/local-session-next \
  --retain-jwks /secure/akb/local-session-current/jwks.json

Keep a retained public key for at least jwt_expire_hours plus rollout skew, then remove it in a later coordinated keyset revision. Restoring the previous private/JWKS pair is the rollback; never overwrite key files in place.

Vector store (driver-pluggable)

Hybrid search (dense + BM25 sparse, RRF-fused) runs through a driver interface. Five drivers ship; pick at config time:

  • pgvector (default) — uses the same Postgres container that holds application data. The pgvector/pgvector image pre-installs the extension; the driver creates a separate vector_index schema, so the main chunks table stays plain PostgreSQL. RRF fusion runs application-side. No external service to operate.
  • qdrant — runs a separate Qdrant container; native RRF via the Query API. Useful when you already operate Qdrant or want to scale the vector store independently of Postgres.
  • seahorse-cloud — points at a managed

Files in the repo

Repository payload35 top-level entries
  • .agents
  • .claude
  • .claude-plugin
  • .codex
  • .github
  • agents
  • backend
  • config
  • deploy
  • docs
  • eval
  • frontend
  • okf
  • packages
  • plugins
  • scripts
  • templates
  • .gitattributes
  • .gitignore
  • .secrets.baseline
  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • docker-compose.audit.yaml
  • docker-compose.keycloak.yaml
  • docker-compose.qdrant.yaml
  • docker-compose.yaml
  • glama.json
  • LICENSE
  • LICENSE-CHANGE.md
  • llms-install.md
  • README.md
  • SECURITY.md
  • server.json
  • TRADEMARKS.md

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More connectors

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

86k

High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

43k

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

14k
okf-memory/
okf-agent-memory

Git-native persistent memory for AI coding agents. Implements Google OKF v0.2 with sub-300µs in-memory BM25 search, embedded MCP server, and progressive disclosure. Slashes token bloat by 80% with zero external databases or dependencies. Built in pure Go.

547
tirth8205/
code-review-graph

Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.

31k
2akouwu/
reverify

Stop your AI from making things up — it proposes, deterministic tools decide, every claim checked against ground truth with evidence. Grounded facts and context survive resets. Reverse engineering is the proving ground. MCP server + CLI.

1.1k