Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

plone-brain

Local cache, incremental sync and persistent memory for Plone — over the Model Context Protocol (MCP).

plone-brain gives AI assistants a fast, cheap, local view of any Plone 6+ site. It mirrors the content tree into a local SQLite database (with full-text search), keeps it fresh with an incremental sync driven by the modified watermark, and answers reads without hitting the server — reducing tokens burned and HTTP requests while enabling offline reads and cross-session memory.

It is a read/context MCP server: it never writes to Plone. Pair it with a write MCP server such as @plone/mcp or cs-dynamicpages-mcp to get the best of both worlds.


Why

Exploring a Plone site through live API calls is expensive: every question is a network round-trip that returns large JSON payloads (tokens) and adds latency. plone-brain amortizes that cost:

  • Reads are local — answers come from SQLite: 0 network requests, compact output.
  • Sync is incremental — only objects changed since the last watermark are fetched.
  • Context is small — a ~1 KB "brief" replaces large static context files.
  • Memory persists — standing rules/decisions survive across sessions.

Features

  • Local-first: cache the content tree in SQLite with FTS5 full-text search.
  • Incremental sync with deletion reconciliation (stale objects are removed).
  • Progressive disclosure: compact responses by default, full detail on demand.
  • Persistent memory: brain_remember / brain_recall across sessions.
  • Product overlays: auto-detects cs_dynamicpages and adds dynamic-page tools.
  • STDIO transport: works with Opencode, Claude Desktop, and any MCP client.
  • Portable: one generic core for any Plone site; no per-site configuration required.

Requirements

  • Node.js >= 22
  • A Plone 6+ site with plone.restapi enabled (default on Plone 6).
  • Build tooling for the better-sqlite3 native module (prebuilt binaries are usually available; if not, a C/C++ toolchain may be needed).

Install & Quick Start

Opencode

Add the server to your opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "plone-brain": {
      "type": "local",
      "command": ["npx", "-y", "github:codesyntax/plone-brain"],
      "environment": {
        "PLONE_BASE_URL": "https://your-plone.example",
        "PLONE_TOKEN": "{env:PLONE_TOKEN}"
      },
      "enabled": true
    }
  }
}

Then, in a session, ask the assistant to use the plone-brain tools (e.g. brain_status, brain_refresh).

Claude Desktop

claude_desktop_config.json:

{
  "mcpServers": {
    "plone-brain": {
      "command": "npx",
      "args": ["-y", "github:codesyntax/plone-brain"],
      "env": {
        "PLONE_BASE_URL": "https://your-plone.example",
        "PLONE_TOKEN": "your-jwt-token"
      }
    }
  }
}

From a local clone

git clone https://github.com/codesyntax/plone-brain.git
cd plone-brain
npm install
node bin/plone-brain.js        # or: npm start

The launcher runs the TypeScript sources through tsx, so there is no build step required.

First run

Ask the assistant to run:

  1. brain_status() — verify connectivity and cache state.
  2. brain_refresh({ full: true }) — populate the local cache (subsequent refreshes are incremental).

Configuration

Configuration uses the same environment variables as @plone/mcp and cs-dynamicpages-mcp, so one set of credentials can drive both the read and write servers.

Variable Required Description
PLONE_BASE_URL yes Base URL of the Plone site (e.g. https://plone.example).
PLONE_TOKEN yes* JWT bearer token.
PLONE_USERNAME yes* Basic-auth username (alternative to PLONE_TOKEN).
PLONE_PASSWORD yes* Basic-auth password (alternative to PLONE_TOKEN).
BRAIN_DB no SQLite path (default: ./.plone-brain/cache.sqlite).
BRAIN_LANGUAGE no Accept-Language for Plone REST (default: en).

* Use either PLONE_TOKEN or PLONE_USERNAME/PLONE_PASSWORD.

There are three ways to provide configuration:

1) Client config environment (recommended)

Pass variables in the MCP server definition (see the Opencode / Claude Desktop examples above). Opencode supports {env:VAR} substitution, so secrets can stay in your shell environment.

2) .env file

plone-brain loads a .env file from the current working directory:

PLONE_BASE_URL=https://your-plone.example
PLONE_TOKEN=your-jwt-token
BRAIN_DB=./.plone-brain/cache.sqlite
BRAIN_LANGUAGE=en

3) brain_configure at runtime

Useful when connecting to different sites per session:

brain_configure({ "baseUrl": "https://plone.example", "token": "..." })
// or
brain_configure({ "baseUrl": "https://plone.example", "username": "admin", "password": "secret" })

Tools

Core tools

Tool Description
brain_configure({ baseUrl, token, username, password }) Set connection credentials at runtime.
brain_status({ checkConnection }) Cache size, watermark, and last-known auth. Cache-only by default; checkConnection: true adds a live check.
brain_brief() Standing conventions and product notes (~few tokens).
brain_refresh({ full, portal_types, pathPrefix, reconcile }) Sync from Plone. Incremental by default; reconcile (default true) removes deleted objects.
brain_search({ query, portal_type, pathPrefix, fields, limit, offset }) Full-text search + filters over the cache.
brain_get({ idOrPath, fields, raw }) Fetch a single cached object by UID or path.
brain_children({ parent, fields }) List children of a container (by path or UID).
brain_remember({ key, value, tags, namespace }) Persist a note/rule across sessions.
brain_recall({ query, namespace }) Search persisted notes/rules.

Resources: brain://brief, brain://status.

Product overlays

When a site uses a supported add-on, plone-brain auto-detects it and adds tools.

cs_dynamicpages

Activated automatically when DynamicPageRow content exists. Adds:

Tool Description
brain_dynamic_page({ page, summaryOnly }) Assemble a DynamicPage's rows + featured items from cache.
brain_dynamic_rows({ pagePath, row_type, limit }) Query DynamicPageRows across pages.

Combining with other Plone MCP servers

plone-brain is read-only and uses the brain_* namespace, so it never collides with the plone_* tools of the write servers. All servers can share the same PLONE_* environment.

Rule of thumb: read/query with brain_* (local, cheap), write with plone_* (live), then run brain_refresh() to keep the cache consistent.

plone-brain only

Read-only context, search, and memory. No CMS modifications.

{
  "mcp": {
    "plone-brain": {
      "type": "local",
      "command": ["npx", "-y", "github:codesyntax/plone-brain"],
      "environment": { "PLONE_BASE_URL": "https://plone.example", "PLONE_TOKEN": "{env:PLONE_TOKEN}" }
    }
  }
}

plone-brain + @plone/mcp (official)

Read with brain_*, write (CRUD, blocks, workflow, translations) with plone_*.

{
  "mcp": {
    "plone": {
      "type": "local",
      "command": ["npx", "-y", "@plone/mcp"],
      "environment": { "PLONE_BASE_URL": "https://plone.example", "PLONE_TOKEN": "{env:PLONE_TOKEN}" }
    },
    "plone-brain": {
      "type": "local",
      "command": ["npx", "-y", "github:codesyntax/plone-brain"],
      "environment": { "PLONE_BASE_URL": "https://plone.example", "PLONE_TOKEN": "{env:PLONE_TOKEN}" }
    }
  }
}

plone-brain + cs-dynamicpages-mcp

Same as above, plus DynamicPage layout tools.

cs-dynamicpages-mcp already includes the full @plone/mcp toolset (it extends it with the dynamic-page tools). Do not also add @plone/mcp separately — you would register the plone_* tools twice.

{
  "mcp": {
    "cs-dynamicpages": {
      "type": "local",
      "command": ["npx", "-y", "github:codesyntax/cs-dynamicpages-mcp"],
      "environment": {
        "PLONE_BASE_URL": "https://plone.example",
        "PLONE_USERNAME": "admin",
        "PLONE_PASSWORD": "{env:PLONE_PASSWORD}"
      }
    },
    "plone-brain": {
      "type": "local",
      "command": ["npx", "-y", "github:codesyntax/plone-brain"],
      "environment": { "PLONE_BASE_URL": "https://plone.example", "PLONE_TOKEN": "{env:PLONE_TOKEN}" }
    }
  }
}

How caching works

  • Full sync (brain_refresh({ full: true })): fetches every object in scope (with fullobjects) and stores a normalized row per object: uid, path, portal_type, review_state, modified, parent_path, plus the raw JSON and an FTS index. A whole small site can be cached in one request.
  • Incremental sync (brain_refresh()): fetches only objects with modified newer than the last watermark.
  • Deletion reconciliation: after an incremental sync, a metadata-only scan yields the current path list; cached paths that no longer exist upstream are removed. (Full syncs reconcile for free.)
  • Reads (brain_get, brain_search, brain_children, brain_dynamic_page) are served entirely from SQLite: 0 network requests.
  • Memory (brain_remember / brain_recall) is stored in the same SQLite database, namespaced, and persists across sessions.

The SQLite file lives at BRAIN_DB (default ./.plone-brain/cache.sqlite) and is safe to delete to force a cold rebuild.


Development

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest
npm run build       # compile to dist/ (for npm publishing)
npm start           # run the server via tsx

Project layout

src/
  stdio-server.ts           # MCP entry point (STDIO)
  config.ts                 # env + .env configuration
  plone/
    client.ts               # REST client (auth, retries, request counter)
    search.ts               # @search helpers (pagination, metadata-only)
  store/
    db.ts                   # SQLite open + migrations
    schema.sql              # reference schema
    repository.ts           # objects, FTS, memory
  sync/engine.ts            # full/incremental sync + deletion reconciliation
  products/
    registry.ts             # product overlay detection/registration
    cs_dynamicpages/        # cs_dynamicpages overlay
  tools/register.ts         # brain_* tools
  output.ts                 # compact output formatting
bin/plone-brain.js          # tsx launcher (no build required)

License

MIT © 2026 CodeSyntax

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages