membot CLAUDE.md

membot CLAUDE.md is an instructions file for coding agents from evantahler/membot. It costs 5,207 tokens per session, scanned A, original, MIT.

Repository guidance for membot, a Bun command-line tool and MCP server that stores, converts, searches, and retrieves documents for AI agents.

In plain words
What is it for?
Use it when changing membot’s document ingestion, markdown conversion, local embeddings, DuckDB index, search, fetchers, or command-line and server code.
Why use it?
It explains the project’s architecture and strict technical limits, so coding changes remain compatible with its Bun-only runtime and local search system.

Instructions file

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add instructions/evantahler/membot/claude-md
Clone the repo
git clone --depth 1 https://github.com/evantahler/membot

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for membot CLAUDE.md

README.md
[![agentmods](https://agentmods.dev/badge/instructions/evantahler/membot/claude-md.svg)](https://agentmods.dev/instructions/evantahler/membot/claude-md)
Your own site
<a href="https://agentmods.dev/instructions/evantahler/membot/claude-md"><img src="https://agentmods.dev/badge/instructions/evantahler/membot/claude-md.svg" alt="Measured on agentmods" height="20"></a>
Per session 5,207 This file is loaded in full into every session.
When invoked 5,207 The same file — it is already loaded in full.
Security scan A 0 findings. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.05207 $0.05207
Opus 5 $0.02603 $0.02603
Sonnet 5 $0.01041 $0.01041
Haiku 4.5 $0.00521 $0.00521

Measured 4d ago against content hash 19167134725c, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

membot CLAUDE.md scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 4d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

CLAUDE.md · 181 lines

How it starts

The opening of the file, as written. The whole thing — 181 lines — stays where its author put it; the contents beside it link to each section on GitHub.

CLAUDE.md — membot

Guidance for Claude Code when working in this repo. Pair with docs/plan.md (the source-of-truth design doc).

What this project is

membot is a standalone Bun CLI + MCP server (Bun package membot, binary membot) that gives AI agents a persistent, versioned, searchable context store. Files (markdown, PDF, DOCX, HTML, URLs) are ingested, converted to markdown, chunked, embedded locally with @huggingface/transformers (WASM, 384-dim Xenova/bge-small-en-v1.5), and indexed in DuckDB with hybrid search (vector + BM25). Every agent-visible artifact is a row in files, addressed by a virtual logical_path — there is no on-disk tree of stored content.

Reference project (origin):

  • botholomew — the chunker, embedder, markdown-converter, and hybrid search were originally embedded here. The earlier mcpx-based fetcher has been replaced; today's fetcher is a per-service downloader registry (see "Architecture at a glance" below).

Hard constraints

  • Bun-only. No Node-only deps. bun build --compile produces standalone binaries; the runtime must not require Bun installed.
  • Local embeddings only. @huggingface/transformers WASM, Xenova/bge-small-en-v1.5, 384-dim. Never reach for cloud embedding APIs (OpenAI/Voyage/Cohere/Anthropic embeddings) even if a reference project uses them.
  • DuckDB is the only store. Content AND original bytes live in rows (files.content, blobs.bytes), not in a filesystem tree. ~/.membot/index.duckdb holds everything except cached model weights. The DB will get large — that's accepted.
  • Append-only versioning. Every ingest, refresh that finds new bytes, write, or rename creates a new (logical_path, version_id) row. version_id is a TIMESTAMP (ms precision). Default queries flow through current_files / current_chunks views. Delete = tombstone, not a row removal.
  • MCP defaults to current. Every MCP tool acts on the latest non-tombstoned version unless version is passed explicitly.
  • Per-service downloaders + persisted provenance. Remote URLs are dispatched to a source-plugin registry (src/ingest/sources/): GitHub via REST API + PAT, Linear via GraphQL + personal API key. There is no generic-web catch-all and no Google plugin — arbitrary URLs (and Google Docs/Sheets/Slides URLs) are rejected with a clear HelpfulError. Each row persists (downloader, downloader_args) so refresh replays the exact same downloader against the same URL — deterministic, no LLM, no agent loop.
  • Fetches are non-interactive. membot add and membot refresh never prompt or open a browser. Auth failures throw HelpfulError with a concrete next step (membot config set downloaders.<svc>.api_key for token services).
  • No Playwright, no Chromium, no Google ingest. Membot doesn't launch a browser, embed one, or shell out to a third-party CLI. Google Docs/Sheets/Slides aren't a first-class source — the OAuth scope tax to get Drive access (either cloud-platform to gcloud or a manual GCP-project setup) was disproportionate. Workaround for users: export the Drive file as .docx / .xlsx / .pdf and membot add <path>.
  • Native conversion first, LLM fallback for messy/binary input. unpdf, mammoth, turndown handle the common cases. Claude vision captions images; Claude markdown-converter is the last-resort fallback. Missing ANTHROPIC_API_KEY is not a hard error — the pipeline degrades to deterministic surrogates.
  • Textual surrogate is the universal interface. Every artifact (markdown, PDF, image, audio, anything) produces a markdown body that flows through chunking + embedding + FTS. Original bytes live in blobs and are reachable via membot_read bytes=true. Search has zero special cases for binary content.
  • Always describe. files.description is generated for every ingested file, including plain markdown. The string <logical_path>\n<description>\n<heading-breadcrumb>\n\n<chunk_content> is what gets embedded and FTS-indexed (stored as chunks.search_text); chunks.chunk_content keeps the raw body for clean snippet rendering. The description is capped (~240 chars) and the breadcrumb line is present only for heading-scoped markdown chunks, so the whole search_text fits bge-small's 512-token window.
  • Size chunks to the model window, pool the way the model was trained. Chunk sizing (chunker.target_chars/max_chars, default 1400/1800) is budgeted so search_text fits bge-small's 512-token limit — oversize chunks silently embed only their prefix. BGE-v1.5 uses CLS pooling (resolvePooling in embedder.ts), not mean. Markdown is chunked at heading boundaries (fence-safe) with a per-chunk breadcrumb (chunker.markdown_aware, default on). Any change to pooling, chunk sizing, or the search_text shape MUST bump EMBEDDING_REVISION (constants.ts) with a history line; existing stores clear the resulting search-time warning by running membot reindex --embeddings.
  • membot_add accepts directories and globs. Single arg, polymorphic: file path, directory (recursive walk, symlinks followed via realpath dedupe), glob (docs/**/*.md), URL, or inline:<text>. Each matched entry becomes its own version under its own logical_path; partial failures are reported per-entry, not all-or-nothing.
  • CLI auto-renders for the environment. TTY → spinners, progress bars, ANSI colors. Piped/--json/CI=true/NO_COLOR → JSON to stdout, structured logs to stderr, no ANSI bytes. One code path; src/output/tty.ts is the single source of truth for which mode is active.
  • All errors are HelpfulError. Bare throw new Error(...) is forbidden. HelpfulError requires a non-empty hint (statically and at runtime); the hint must name the next action concretely. The same hint string lands in front of both humans (CLI stderr) and LLMs (MCP structuredContent.error.hint and the rendered text content).
  • No any. Both implicit and explicit any are banned (TS strict: true + biome noExplicitAny: error). For untyped third-party APIs, declare a local interface that captures the methods you actually use and cast the import once at the boundary; sprinkle unknown + type guards everywhere else.
  • Every method gets a docstring. Every exported (and most internal) function, method, or class member must have a JSDoc-style comment that explains what it does — preferably also why when the rationale isn't obvious. One short line is fine for trivial wrappers; multi-line comments are appropriate for orchestration paths or anything with a non-obvious contract. Don't restate the signature; explain the intent and the contract.
  • Tests are written alongside code, not bolted on. Every new module ships with unit tests covering the happy path, the error path, and the edge cases (empty input, malformed input, boundary conditions). DB-touching code uses real ephemeral DuckDB files, not mocks. Error types are tested for both their invariants and their rendering.
  • Migrations are logged. Every migration applied at startup writes an info line so users can see what changed when they upgrade.
  • User-facing changes bump package.json. Any change that ships behavior to users (new flag, new command, fixed bug they could observe, output-format change) must increment version in package.json in the same PR. The auto-release workflow only fires when the version changes — no bump means no release and no binaries. Internal-only refactors, comment edits, and test changes don't need a bump.

Read the full file on GitHub · 181 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 4d ago First seen · 181 lines · 5,207 tokens per session scan A 19167134725c

Subscribe to this mod's changes

membot CLAUDE.md is an instructions file published in the GitHub repository evantahler/membot (3 stars, last pushed 2mo ago), licensed MIT. It adds 5,207 tokens to every session, about $0.0260 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.