vkb
Integrates with Ollama for generating embeddings and performing LLM-based summarization, relation extraction, and tagging.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@vkbingest https://arxiv.org/abs/2301.12345"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
vkb — Vector Knowledge Base
A self-contained, local-first semantic memory system built on Postgres + pgvector and Ollama. Exposes a full Model Context Protocol (MCP) server so any MCP-capable host (Claude Desktop, Claude CLI, etc.) can ingest documents, query semantically, and traverse a knowledge graph.
Architecture overview
graph TD
subgraph Clients
A[MCP Host\nClaude Desktop / CLI]
B[Browser UI\nlocalhost:4242]
end
subgraph vkb process
MCP[MCP Server\nstdio or HTTP Streamable :3333]
HTTP[HTTP Server\n:4242]
COORD[Coordinator\nWorker Pool]
subgraph Workers
IW[ingest-worker × N]
FW[finetune-worker]
RW[retune-worker]
end
subgraph Pipelines
IPIPE[Ingest Pipeline]
FPIPE[Finetune Pipeline]
RPIPE[Retune Pipeline]
end
subgraph Adapters
FETCH[fetch\nreadability / pdf / epub]
CHUNK[chunk\nsliding-window]
EMBED[embed\nollama]
SECT[section\nsimilarity-valley]
REL[relation\nheuristic + LLM]
LLM[llm\nollama]
RAW[rawstore\nfilesystem or s3]
end
end
subgraph External
PG[(Postgres\npgvector)]
OLL[Ollama]
FS[Filesystem / S3\nrawstore/]
end
A -->|MCP tools| MCP
B -->|REST / WebSocket| HTTP
MCP --> COORD
HTTP --> COORD
COORD --> IW & FW & RW
IW --> IPIPE
FW --> FPIPE
RW --> RPIPE
IPIPE & FPIPE & RPIPE --> FETCH & CHUNK & EMBED & SECT & REL & LLM & RAW
EMBED -->|vectors| OLL
LLM --> OLL
RAW --> FS
IPIPE & FPIPE & RPIPE -->|upsert| PGRelated MCP server: hypermnesia
Ingestion pipeline
flowchart TD
START([Job queued]) --> DEDUP
DEDUP{Content hash\nalready known?}
DEDUP -- yes, unchanged --> SKIP([Skip — existing entity returned])
DEDUP -- no / changed --> FETCH
subgraph FETCH["fetching — Persist"]
FETCH2[Fetch raw text\nURL → readability / PDF / EPUB\nor staging file]
FETCH2 --> HASH[Compute SHA-256\ncontent hash]
HASH --> WRITE[Write entity.md + chunks.ndjson\nto RawStore]
WRITE --> F_COMMIT[Commit raw_store_key\nto DB ✓]
end
F_COMMIT --> CHUNK_S
subgraph CHUNK_S["chunking"]
CHUNK2[Sliding-window chunk\nwrite chunk rows to DB]
end
CHUNK_S --> EMBED_S
subgraph EMBED_S["embedding"]
EMBED2[Embed each chunk\nvia Ollama]
end
EMBED_S --> SECT_S
subgraph SECT_S["sectioning"]
SECT2[Group chunks into sections\nsimilarity-valley or positional]
end
SECT_S --> SUM_S
subgraph SUM_S["summarising"]
SUM_C[Summarise each chunk — LLM]
SUM_SEC[Summarise each section — LLM]
SUM_E[Summarise entity — LLM]
end
SUM_S --> LINK_S
subgraph LINK_S["linking"]
REL_H[Heuristic relation extraction]
REL_L[LLM relation extraction\noptional]
end
LINK_S --> TAG_S
subgraph TAG_S["tagging"]
TAG[Extract tags from meta\nAssert tag:* relations\nbetween co-tagged entities]
end
TAG_S --> DONE([Entity ready — status = ready])Finetune pipeline
The finetune pipeline enriches already-ingested entities without re-chunking or re-embedding. It runs as a separate finetune-worker process and is triggered via vkb_finetune (MCP) or POST /finetune (HTTP).
flowchart TD
START([Finetune job queued]) --> EX
subgraph EX["extracting"]
TOP[Find top-N nearest neighbours\nvia embedding similarity]
TOP --> LLM_REL[LLM relation extractor\nupsert content_llm relations]
end
EX --> TAG
subgraph TAG["tagging"]
LLM_TAG[LLM keyword tagger\nmerge tags into meta.tags]
LLM_TAG --> TAG_REL[Assert tag:* relations\nbetween co-tagged entities]
end
TAG --> DONE([Finetune complete])Requirements
Tool | Version |
Node.js | ≥ 22 |
Docker + Docker Compose | any recent |
any recent |
Pull the models vkb uses by default:
ollama pull nomic-embed-text
ollama pull gemma4:e4bQuick start
# 1. Clone and install
git clone <repo-url> galactic-vkb
cd galactic-vkb
npm install
# 2. Configure
cp .env.example .env
# Edit .env if needed — defaults work out of the box with Docker Compose
# 3. Start Postgres (pgvector-enabled)
npm run db:up
# 4. Run migrations
npm run migrate
# 5. Start vkb
npm run dev # development (tsx watch)
# or
npm run build && npm start # productionThe UI is available at http://localhost:4242/ once running. Navigate between the graph view (#viz) and ingest form (#ingest) using the header tabs.
Adding vkb as an MCP server
vkb uses stdio transport when MCP_PORT is 0. The MCP server is also accessible over HTTP Streamable (MCP 2025-03-26 spec) when MCP_PORT > 0 (default 3333), with session management for multi-client use.
Claude Desktop (claude_desktop_config.json)
Open (or create) ~/.claude/claude_desktop_config.json and add an entry under mcpServers:
{
"mcpServers": {
"vkb": {
"command": "node",
"args": ["/absolute/path/to/galactic-vkb/dist/index.js"],
"env": {
"DATABASE_URL": "postgres://vkb:vkb@localhost:5433/vkb",
"OLLAMA_BASE_URL": "http://localhost:11434",
"EMBED_MODEL": "nomic-embed-text",
"LLM_MODEL": "gemma4:e4b",
"MCP_PORT": "0"
}
}
}
}Tip: Run
npm run buildfirst sodist/index.jsexists.MCP_PORT=0forces stdio mode — no HTTP server is started.
For development (no build step), use tsx instead:
{
"mcpServers": {
"vkb": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/galactic-vkb/src/index.ts"],
"env": {
"DATABASE_URL": "postgres://vkb:vkb@localhost:5433/vkb",
"OLLAMA_BASE_URL": "http://localhost:11434",
"MCP_PORT": "0"
}
}
}
}Claude CLI (inline)
MCP_PORT=0 DATABASE_URL=postgres://vkb:vkb@localhost:5433/vkb \
claude --mcp-server "vkb:node /absolute/path/to/galactic-vkb/dist/index.js"Available MCP tools
Tool | Description |
| Submit text, a URL, or a file path for ingestion. Optional |
| Submit up to 200 items in a single call. Each item has the same shape as |
| Poll a background job by ID. Returns |
| Semantic search across all ingested content. Supports |
| Fetch an entity or chunk by ID ( |
| Read the raw stored text for an entity or chunk from the RawStore. |
| Assert an explicit relation between any two entity or chunk IDs. |
| Retrieve an N-hop relation subgraph from a seed node. |
| Delete an entity and all its data (chunks, sections, relations, RawStore files). Non-reversible. |
| Queue a finetune job: LLM relation extraction + LLM keyword tagging. No re-chunking or re-embedding. Accepts optional |
| Trigger a re-embedding / relation refresh sweep immediately. |
| Full system snapshot (entity/chunk/relation counts, queue depth, worker state, config). |
| Run all pending SQL migrations. Idempotent. |
vkb_relate — the feedback loop tool
"The
vkb_relatetool is underrated. As Claude works with your data and draws connections, you can have it assert new relations back into the graph. Over time Claude becomes a contributor to the knowledge base, not just a consumer. That's a genuinely interesting feedback loop."
Every relation asserted via vkb_relate is marked origin: asserted — it is never pruned by retune sweeps and carries confidence: 1.0. The optional weight parameter (float 0–1) lets you express relative strength; if omitted it is computed automatically from the cosine similarity between the two nodes. This makes Claude's synthesis durable: connections it draws during a session persist and become first-class edges that future queries and vkb_neighbors traversals can follow.
Typical pattern:
# 1. Query for relevant chunks
vkb_query { text: "transformer attention mechanism" }
# 2. Identify a cross-document insight, then assert it
vkb_relate { source_id: "<chunk-A>", target_id: "<chunk-B>",
rel_type: "shares_mechanism_with" }
# 3. Traverse what's grown
vkb_neighbors { id: "<chunk-A>", hops: 2 }You can also use vkb_finetune to have the LLM automatically extract relations and keyword tags across a set of entities — a useful complement to explicit vkb_relate calls when working with a large corpus.
vkb_neighbors — N-hop subgraph retrieval
Walks the relation graph outward from a seed node up to hops steps (default 2, max 5). Returns:
nodes— every reachable entity or chunk, annotated withkind,hopdistance from the seed, and its summary.edges— all relations between discovered nodes (not just the traversal path), enabling local graph rendering or further reasoning.
Parameter | Default | Description |
| required | Seed entity or chunk UUID |
|
| Traversal depth (1–5) |
|
| Skip edges below this confidence |
| — | Only follow edges of this type |
|
| Cap on total nodes returned |
HTTP API
The observability server runs on OBS_PORT (default 4242) and exposes both the SPA and a REST API.
All responses follow the envelope { ok: true, data: … } / { ok: false, error: "…" }.
When Postgres is unreachable, routes that need the DB return 503 with "Database unavailable".
Authentication
Set OBS_SECRET=your-secret in .env. When set, all API requests must include:
Authorization: Bearer your-secretHealth
GET /healthReturns the live status of each dependency:
{ "ok": true, "uptime": 42.3, "postgres": true, "ollama": true }Returns 503 with "postgres": false when Postgres is unreachable.
Ingest
# URL
curl -X POST http://localhost:4242/ingest \
-H "Content-Type: application/json" \
-d '{ "type": "url", "ref": "https://example.com/article" }'
# Local file (supports .md, .txt, .pdf, .epub, .yaml, .json)
curl -X POST http://localhost:4242/ingest \
-H "Content-Type: application/json" \
-d '{ "type": "doc", "ref": "/absolute/path/to/file.md" }'
# Inline text
curl -X POST http://localhost:4242/ingest \
-H "Content-Type: application/json" \
-d '{ "type": "note", "text": "Some text…", "source_context": "conversation", "meta": { "tags": ["example"] } }'Response:
{ "ok": true, "data": { "job_id": "3f2a1b4c-…", "entity_id": "9d8e7f6a-…" } }Ingestion is asynchronous — poll /jobs for completion. Inline text is deduplicated by SHA-256 content hash; if identical content already exists as a ready entity, the job is skipped and the existing entity is returned.
Re-ingest
# Re-run pipeline for a single entity (must have stored raw content)
curl -X POST http://localhost:4242/reingest \
-H "Content-Type: application/json" \
-d '{ "entity_id": "9d8e7f6a-…" }'
# Re-queue all entities that have stored raw content
curl -X POST http://localhost:4242/reingest \
-H "Content-Type: application/json" \
-d '{}'Finetune
Runs LLM relation extraction and keyword tagging on already-ingested entities without re-chunking or re-embedding:
# Finetune specific entities
curl -X POST http://localhost:4242/finetune \
-H "Content-Type: application/json" \
-d '{ "entity_ids": ["9d8e7f6a-…"] }'
# Finetune all entities of a given type
curl -X POST http://localhost:4242/finetune \
-H "Content-Type: application/json" \
-d '{ "scope": "url" }'Query
curl -X POST http://localhost:4242/query \
-H "Content-Type: application/json" \
-d '{ "text": "How does quantum entanglement work?", "k": 5, "threshold": 0.7 }'Retune
Starts a background retune sweep (re-embeds stale chunks, prunes weak relations):
curl -X POST http://localhost:4242/retune \
-H "Content-Type: application/json" \
-d '{ "scope": "all", "force": false }'Jobs
GET /jobs?kind=ingest&stage=queued&limit=50Query param | Values | Description |
|
| Filter by job type |
|
| Filter by stage |
| 1–200 (default 50) | Max results |
Results include the entity ref and meta for context.
Entities
GET /entities?type=url&status=ready&source_context=external&q=quantum&id=<uuid>&from=2025-01-01&limit=50&offset=0
GET /entities/broken
GET /entities/projection?offset=0&limit=500
GET /entities/:id
GET /entities/:id/raw
DELETE /entities/:id
POST /entities/bulk-action/entities/broken returns non-ready entities annotated with their latest job and a remediation hint:
reingest— raw content is available, pipeline can be re-runno_raw— source content was not persisted; manual intervention neededstuck— an active job exists but has not progressed
/entities/projection returns a paginated UMAP 3D projection (mean-pooled chunk embeddings per entity), used by the graph view. The projection is cached and recomputed in the background whenever an ingest job completes.
POST /entities/bulk-action applies an action to a list of entity IDs:
{ "ids": ["<uuid>", "…"], "action": "delete" | "reingest" | "reingest_force" | "finetune" }Returns { results, succeeded, failed }.
Chunks
GET /chunks?entity_id=<uuid>&limit=500&offset=0
GET /chunks/projection?offset=0&limit=500
GET /chunks/:id
GET /chunks/:id/raw/chunks/projection returns a paginated UMAP 3D projection of individual chunk embeddings (same cache/versioning as entity projection).
Relations
GET /relations?origin=heuristic&rel_type=related_to&min_confidence=0.7&limit=50Query param | Description |
|
|
| Relation label string |
| Float 0–1 |
| Float |
|
|
| 1–50000 (default 50) |
Status
GET /statusReturns entity/chunk/relation counts, queue depths, worker state, index status, and active config.
WebSocket event stream
Connect to ws://localhost:4242/stream (or wss:// with TLS) to receive live pipeline events. Browsers that cannot send custom headers can authenticate via query param: ?token=<OBS_SECRET>.
Event type | Payload fields | Description |
|
| Job moved to a new pipeline stage |
|
| Job finished successfully |
|
| Job failed — payload contains error detail |
|
| Sent every ~10 s per worker; confirms the coordinator is alive |
|
| A worker process exited unexpectedly and is being respawned |
|
| UMAP projection recomputed after a job completes |
|
| Coordinator queued a periodic retune sweep |
|
| Postgres connection lost — UI shows an alert banner |
|
| Postgres connection restored |
npm scripts
Script | Description |
| Start with tsx watch (hot reload) |
| Compile TypeScript → |
| Run compiled build |
| Apply database migrations |
| Start Dockerised Postgres |
| Stop containers |
| Wipe volume and restart Postgres |
| Build and package as a |
Configuration
All settings are read from environment variables (or a .env file). Defaults are shown.
Infrastructure
Variable | Default | Description |
|
| Postgres connection string |
|
| Raw content storage: |
|
| Root path for filesystem rawstore |
| — | S3 bucket name (when |
| — | S3-compatible endpoint URL (optional override) |
Ollama / models
Variable | Default | Description |
|
| Ollama API base URL |
|
| Embedding model |
|
| Embedding dimension (must match model output) |
|
| LLM model for relation extraction and summarisation |
|
| Use LLM to extract relations (set |
|
| Candidate chunks the LLM considers per extraction pass |
Chunking & sectioning
Variable | Default | Description |
|
| Target chunk size in tokens |
|
| Overlap between adjacent chunks |
|
| Sectioning strategy: |
|
| Cosine similarity drop that triggers a section boundary |
|
| Sliding window size for valley detection |
|
| Max chunks per section |
Relations
Variable | Default | Description |
|
| Minimum cosine similarity to create a relation |
|
| Nearest neighbours considered per chunk |
|
| Increment applied on relation confirmation |
|
| Days before unconfirmed relations are pruned |
|
| Confidence below which relations are pruned on retune |
Summarisation
Variable | Default | Description |
|
| Parallel LLM calls during the summarising stage |
|
| Max characters fed to the entity-level summary prompt (~3 k tokens) |
Vector index
Variable | Default | Description |
|
| Chunk count above which an ivfflat index is created/maintained |
|
| Number of ivfflat lists (tune alongside chunk count) |
Workers & jobs
Variable | Default | Description |
|
| Number of ingest worker processes |
|
| Max times a failed job is re-queued |
|
| How often the retune worker runs automatically ( |
|
| Regenerate entity summaries during retune |
|
| Days before completed/failed jobs are expired |
Servers & security
Variable | Default | Description |
|
| MCP server port ( |
|
| Observability HTTP/WebSocket server port |
| — | Bearer token for the REST/browser API on |
| — | Bearer token for the HTTP MCP endpoint on |
| — | Path to TLS certificate file (enables HTTPS/WSS on all servers) |
| — | Path to TLS private key file |
|
| Log verbosity: |
Note — stdio mode has no auth layer. When
MCP_PORT=0the process communicates over its own stdin/stdout pipe;MCP_SECRETandOBS_SECREThave no effect on it. Only setMCP_SECRETwhen running in HTTP mode and exposing the port outside localhost — most MCP clients (including Claude Desktop) do not send anAuthorizationheader, so settingMCP_SECRETin a Claude Desktop config will silently block all tool calls with401 Unauthorized.
Custom prompts
Provide paths to YAML or plain-text files to override the built-in LLM prompts:
Variable | Description |
| Entity summary prompt |
| Per-chunk summary prompt |
| Section summary prompt |
| Relation extraction prompt |
Observability UI
The SPA at http://localhost:4242/ has two views, selectable from the header:
Graph (#viz)
Entity nodes coloured by status (ready / pending / error)
Edges coloured by origin (heuristic / semantic / LLM / asserted)
Click a node to inspect entity details, chunks, and relations
Confidence histogram for relations
Ingest (#ingest)
Submit URLs, local files, or inline text
Live job progress via WebSocket
Per-job stage tracker
A banner appears at the top of the UI if Postgres becomes unreachable while the server is running, and dismisses automatically on reconnection.
Database resilience
On startup vkb probes the Postgres connection up to 10 times (3-second intervals) before giving up. This means it tolerates the Docker container taking a few seconds to become ready after docker compose up.
While running:
HTTP routes that need the DB return 503 when the connection is lost
The coordinator broadcasts a
db_unavailableWebSocket event on the first failed heartbeatIngest workers pause for 30 seconds between retries instead of the normal 2-second poll interval
A
db_availableevent is broadcast and the UI banner clears automatically when connectivity is restored
TLS
Set TLS_CERT and TLS_KEY to paths of a certificate and private key to enable HTTPS and WSS on all servers. A self-signed cert for local development can be generated with:
scripts/gen-cert.ps1This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/inkiebeard/galactic-vkb'
If you have feedback or need assistance with the MCP directory API, please join our Discord server