Quick start

BrainLLM needs a running TriliumNext instance and Bun 1.0+. It uses Bun APIs directly and does not run on Node.

1 · Get Trilium running

Install TriliumNext locally, or run it anywhere reachable over HTTP. Then create a token: Options → ETAPI → Create token.

You can skip the token step by setting TRILIUM_PASSWORD instead — BrainLLM mints a token on first start and caches it.

2 · Install

# via npm (needs bun on PATH)
npx -y brainllm

# or from source
git clone https://github.com/miisodev/BrainLLM
cd BrainLLM && bun install && bun run build

Client configuration

Add BrainLLM to your MCP client's config. The npm form is simplest:

{
  "mcpServers": {
    "BrainLLM": {
      "command": "npx",
      "args": ["-y", "brainllm"],
      "env": {
        "TRILIUM_BASE_URL": "http://localhost:8080",
        "TRILIUM_ETAPI_TOKEN": "your-token-here"
      }
    }
  }
}

Bootstrap the brain

On a fresh Trilium, call bootstrap() once from your client — or run bun run init from a checkout. It builds the five-area tree, engraves each container's purpose, and writes brainllm.json.

It is safe to call anytime. It verifies an existing root live and only builds a fresh tree on a confirmed 404, so a network blip can never silently produce a duplicate brain.

Teach the model to operate it

The tools are deliberately terse — single-word verbs. The operational discipline lives in the bundled skill at skills/brainllm/. Copy it to ~/.claude/skills/brainllm so Claude Code and Cowork load it.

Without it a model can call every tool correctly and still produce an incoherent brain, because correctness of call is not correctness of practice.

Environment variables

VariableRequiredPurpose
TRILIUM_BASE_URLYesURL of your TriliumNext instance
TRILIUM_ETAPI_TOKENYes*ETAPI bearer token
TRILIUM_PASSWORD* Alternative to the token — mints and caches one on first start
BRAINLLM_MODEcore (42 tools, default) or full (+33 raw ETAPI tools)
BRAINLLM_TZIANA timezone for date stamping on hosted deploys
PORTPresence switches to HTTP-connector mode. Never set it for local stdio
MCP_AUTH_TOKENBearer token required on /mcp in HTTP mode
BRAINLLM_CONFIGAbsolute file path for brainllm.json on persistent-volume deploys
BRAINLLM_OWNER_PASSWORDTurns on the OAuth 2.1 surface. Unset means OAuth is disabled entirely
BRAINLLM_PUBLIC_URLOverride the public origin when a proxy rewrites Host

Tool surface

42 tools by default — 32 universal verbs plus 10 dual-mode surface reads. BRAINLLM_MODE=full adds 33 raw ETAPI tools, for a total of 75.

GroupTools
Session lifecyclestart · day · session · remarks · close · backup
Writingremember · diary
Reading & searchrecall · domain · brain · assembly · template · graph · consistency
Updatingrevise · resolve · withdraw · recover · label · forget
Graph & inspectionconnect · explore · outline · inspect · diff
Attachmentsattach · detach
Maintenanceaddendum · maintain · claim · bootstrap
Surface readsmaster · llm · memory · knowledge · insights — each with a _recall skim twin

Every tool is classified read-only, write or destructive so your client can group its permission prompts. The default is unsafe-side: an unclassified tool is treated as a write, never a read.

Kinds and statuses

Fourteen kinds: biography goals preferences responsibilities protocols diary session thread threadEntry user domain information sources log.

Five statuses: active · dormant · resolved · superseded · eternal. Only threads age; eternal is exempt from the sweep entirely.

Session protocol

START   start()                    once, before responding
        [day()]                    when start() flags a new day
DURING  remember · diary · recall · domain · revise · connect …
END     session() → addendum() → maintain()
        → remarks()diary() → close()

close() refuses unless all five pre-close steps have run and session → remarks → diary holds. Gate state lives on today's session note, so it survives a restart. Pass force=true only when a step genuinely has nothing to log — bypassed steps are reported back, never silently skipped.

Transports

ModeTriggerUse for
stdioPORT unsetClaude Desktop, Claude Code — the client spawns BrainLLM as a child process
HTTP connectorPORT setDocker, Railway, any container host. Serves streamable HTTP at /mcp, legacy SSE at /sse (with POST /messages), and /health — both transports behind the same auth gate

For container deploys, mount a volume on the BrainLLM service and set BRAINLLM_CONFIG to a file path inside it — otherwise auto-discovery re-runs on every cold start.

Remote clients and OAuth

Hosted Claude surfaces have no field for a bearer token — their connector UI offers OAuth or nothing. So BrainLLM is its own OAuth 2.1 authorization server, serving both registration mechanisms: Client ID Metadata Documents (what Claude selects) and RFC 7591 dynamic registration at /register (what opencode and other MCP TS SDK clients require).

Set BRAINLLM_OWNER_PASSWORD to turn it on. Leave it unset and the OAuth surface is disabled entirely — an authorization server that authorises whoever finds the URL is worse than none.

Troubleshooting

SymptomFix
Tools time outThe backend is unreachable. Check the instance TRILIUM_BASE_URL names. Every call is bounded at 30s, so a hang fails the tool call rather than the session
start()uninitializedRun bootstrap()
claude.ai says "couldn't reach the MCP server"OAuth discovery failed — almost never a network problem. Check BRAINLLM_OWNER_PASSWORD is set and that the resource URL matches what you typed, trailing slash included
Every call 401s after an hourToken refresh failed because the signing secret changed. BRAINLLM_CONFIG must point at a persistent volume
ENOENT on a hosted startBRAINLLM_CONFIG is a directory, not a file path
revise(find=) returns replaced: 0Read the hint — it names the cause. Usually an entity-escaped search string: bodies accept escaped markup, find= does not

Full reference lives in the README, and the operational guide in skills/brainllm/SKILL.md.