Skip to content

Releases: cabroe/automad-mcp-server

v0.7.1

Choose a tag to compare

@cabroe cabroe released this 26 Jul 17:14

Added

  • Theme dev server — gains / /
    actions. runs

automad-mcp-server@0.7.1 dev
tsx src/index.ts as a detached background process, writes
, and returns the local URL (port
discovered in this order: explicit arg →
( / ) → the first marker in the
log, up to 20 s). sends SIGTERM, escalates to SIGKILL after 5 s,
and removes the record. reports the running state without
blocking. A second call for the same theme returns until
is called. Live-verified end-to-end against
: the dev server's PHP+esbuild pipeline starts and
returns the expected content.

  • Two new theme analyzer findings — (locale masks
    declared in but no file on disk) and
    (a page mask of the form whose
    snippet doesn't exist). Both are severity , ordered alongside the
    existing / checks.
  • KB pages split — (the bundled knowledge base) is now a
    thin re-exporter over 13 per-page modules in . The split is
    invisible at runtime but makes adding / auditing individual KB pages
    tractable. A drift test pins order and shape.
  • GitHub Pages landing page — is now a self-contained
    single-file landing page (no script, no remote stylesheet, no remote
    image; pinned by ). The tool table is generated from
    the capability registry via

automad-mcp-server@0.7.1 docs:sync
node --import tsx scripts/sync.ts

docs:sync: no change (tools table: already in sync; homepage table: already in sync; README.md: already in sync; CLAUDE.md: already in sync; index.html: already in sync), so it cannot drift from
the code. Counts (tool count, read/destructive, test count) are
fenced markers that regenerate alongside the README.

  • **
    === build ===

automad-mcp-server@0.7.1 build
tsc

=== lint ===

automad-mcp-server@0.7.1 lint
eslint src tests

=== test ===

automad-mcp-server@0.7.1 test
vitest run

RUN v2.1.9 /Users/cabroe/Projects/automad-mcp-server

✓ tests/unit/theme-schema.test.ts (10 tests) 25ms
✓ tests/unit/theme-analyzer.test.ts (18 tests) 146ms
✓ tests/unit/auth.test.ts (15 tests) 222ms
✓ tests/unit/capabilities.test.ts (11 tests) 14ms
✓ tests/unit/domains/theme.test.ts (16 tests) 578ms
✓ handleTheme > build runs install + build (skipped if no package.json scripts) 444ms
✓ tests/unit/write-guard.test.ts (14 tests) 50ms
✓ tests/unit/server.test.ts (9 tests) 61ms
✓ tests/unit/docs-drift.test.ts (9 tests) 9ms
✓ tests/unit/drift.test.ts (11 tests) 20ms
✓ tests/unit/domains/site.test.ts (11 tests) 11ms
✓ tests/unit/theme-scaffold.test.ts (5 tests) 49ms
✓ tests/unit/schemas.test.ts (17 tests) 15ms
✓ tests/unit/theme-fs.test.ts (10 tests) 45ms
✓ tests/unit/config.test.ts (12 tests) 8ms
↓ tests/e2e/live.test.ts (4 tests | 4 skipped)
✓ tests/unit/domains/media.test.ts (7 tests) 13ms
✓ tests/unit/domains/discover.test.ts (9 tests) 9ms
✓ tests/unit/capabilities-tools.test.ts (6 tests) 11ms
✓ tests/unit/theme-editor.test.ts (6 tests) 58ms
✓ tests/unit/resources.test.ts (5 tests) 33ms
✓ tests/unit/docs-kb.test.ts (10 tests) 20ms
✓ tests/unit/domains/config.test.ts (4 tests) 13ms
✓ tests/unit/client.test.ts (59 tests) 3043ms
✓ HttpClient (v2 /_api) > CSRF-mismatch loop after session timeout -> force re-auth then surface AUTH 504ms
✓ HttpClient (v2 /_api) > CSRF-mismatch loop with dead session AND dead re-auth -> AUTH (not 403) 501ms
✓ tests/unit/theme-dev.test.ts (29 tests) 3032ms
✓ startDev > writes dev.json with the spawned pid and reports running:true 1005ms
✓ startDev > runs npm install only when node_modules is missing 502ms
✓ startDev > returns port:null after portTimeoutMs when no marker appears 503ms
✓ startDev > clears a stale dev.json (dead pid) and proceeds 1002ms
✓ tests/unit/theme/generate.test.ts (8 tests) 10ms
✓ tests/unit/domains/shared.test.ts (3 tests) 18ms
✓ tests/unit/domains/docs.test.ts (6 tests) 9ms
✓ tests/unit/errors.test.ts (6 tests) 9ms
✓ tests/unit/theme/diff.test.ts (4 tests) 5ms
✓ tests/unit/theme/manager.test.ts (3 tests) 7ms
✓ tests/unit/logger.test.ts (2 tests) 7ms
✓ tests/unit/domains/pages.test.ts (33 tests) 4048ms
✓ handlePages (v2 /_api) > get retries on 404 (v2 commit-lag) and eventually surfaces NOT_FOUND 3012ms

Test Files 31 passed | 1 skipped (32)
Tests 368 passed | 4 skipped (372)
Start at 19:14:01
Duration 4.99s (transform 861ms, setup 0ms, collect 2.62s, tests 11.60s, environment 9ms, prepare 2.58s)

=== test:coverage (thresholds gate) ===

automad-mcp-server@0.7.1 test:coverage
vitest run --coverage

RUN v2.1.9 /Users/cabroe/Projects/automad-mcp-server
Coverage enabled with v8

✓ tests/unit/theme-schema.test.ts (10 tests) 28ms
✓ tests/unit/theme-analyzer.test.ts (18 tests) 137ms
✓ tests/unit/auth.test.ts (15 tests) 223ms
✓ tests/unit/capabilities.test.ts (11 tests) 16ms
✓ tests/unit/domains/theme.test.ts (16 tests) 579ms
✓ handleTheme > build runs install + build (skipped if no package.json scripts) 442ms
✓ tests/unit/write-guard.test.ts (14 tests) 50ms
✓ tests/unit/server.test.ts (9 tests) 71ms
✓ tests/unit/docs-drift.test.ts (9 tests) 11ms
✓ tests/unit/drift.test.ts (11 tests) 22ms
✓ tests/unit/domains/site.test.ts (11 tests) 19ms
✓ tests/unit/theme-scaffold.test.ts (5 tests) 46ms
✓ tests/unit/schemas.test.ts (17 tests) 26ms
✓ tests/unit/theme-fs.test.ts (10 tests) 44ms
✓ tests/unit/config.test.ts (12 tests) 9ms
↓ tests/e2e/live.test.ts (4 tests | 4 skipped)
✓ tests/unit/domains/media.test.ts (7 tests) 13ms
✓ tests/unit/domains/discover.test.ts (9 tests) 13ms
✓ tests/unit/capabilities-tools.test.ts (6 tests) 14ms
✓ tests/unit/theme-editor.test.ts (6 tests) 87ms
✓ tests/unit/resources.test.ts (5 tests) 42ms
✓ tests/unit/theme-dev.test.ts (29 tests) 3040ms
✓ startDev > writes dev.json with the spawned pid and reports running:true 1006ms
✓ startDev > runs npm install only when node_modules is missing 502ms
✓ startDev > returns port:null after portTimeoutMs when no marker appears 501ms
✓ startDev > clears a stale dev.json (dead pid) and proceeds 1002ms
✓ tests/unit/client.test.ts (59 tests) 3045ms
✓ HttpClient (v2 /_api) > CSRF-mismatch loop after session timeout -> force re-auth then surface AUTH 501ms
✓ HttpClient (v2 /_api) > CSRF-mismatch loop with dead session AND dead re-auth -> AUTH (not 403) 503ms
✓ tests/unit/docs-kb.test.ts (10 tests) 39ms
✓ tests/unit/domains/config.test.ts (4 tests) 11ms
✓ tests/unit/theme/generate.test.ts (8 tests) 9ms
✓ tests/unit/theme/diff.test.ts (4 tests) 6ms
✓ tests/unit/domains/shared.test.ts (3 tests) 12ms
✓ tests/unit/domains/docs.test.ts (6 tests) 12ms
✓ tests/unit/errors.test.ts (6 tests) 19ms
✓ tests/unit/theme/manager.test.ts (3 tests) 16ms
✓ tests/unit/domains/pages.test.ts (33 tests) 4046ms
✓ handlePages (v2 /_api) > get retries on 404 (v2 commit-lag) and eventually surfaces NOT_FOUND 3008ms
✓ tests/unit/logger.test.ts (2 tests) 5ms

Test Files 31 passed | 1 skipped (32)
Tests 368 passed | 4 skipped (372)
Start at 19:14:07
Duration 5.52s (transform 735ms, setup 0ms, collect 2.79s, tests 11.71s, environment 12ms, prepare 3.80s)

% Coverage report from v8
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 90.98 | 85.51 | 95.15 | 90.98 |
src | 88.63 | 84.7 | 97.56 | 88.63 |
auth.ts | 91.17 | 76.81 | 100 | 91.17 | ...85,217,219-223
client.ts | 96.16 | 88.99 | 92.3 | 96.16 | ...22,194,367-372
config.ts | 92.38 | 83.33 | 100 | 92.38 | 76-80,125,127-128
errors.ts | 100 | 87.5 | 100 | 100 | 33
logger.ts | 80 | 0 | 100 | 80 | 9-12
prompts.ts | 69.6 | 57.14 | 100 | 69.6 | ...,81-93,128-137
schemas.ts | 100 | 100 | 100 | 100 |
server.ts | 65.07 | 72.72 | 100 | 65.07 | ...43-150,158-169
write-guard.ts | 100 | 100 | 100 | 100 |
src/capabilities | 96.25 | 88.05 | 100 | 96.25 |
registry.ts | 99.1 | 90 | 100 | 99.1 | 297-298
tools.ts | 87.32 | 82.35 | 100 | 87.32 | ...09,118,140-141
src/docs | 100 | 83.33 | 100 | 100 |
kb.ts | 100 | 83.33 | 100 | 100 | 58,81-87
src/docs/kb/pages | 100 | 100 | 100 | 100 |
blocks.ts | 100 | 100 | 100 | 100 |
...n-pitfalls.ts | 100 | 100 | 100 | 100 |
...structures.ts | 100 | 100 | 100 | 100 |
...-functions.ts | 100 | 100 | 100 | 100 |
...ng-started.ts | 100 | 100 | 100 | 100 |
headless-api.ts | 100 | 100 | 100 | 100 |
i18n.ts | 100 | 100 | 100 | 100 |
...resolution.ts | 100 | 100 | 100 | 100 |
index.ts | 100 | 100 | 100 | 100 |
navigation.ts | 100 | 100 | 100 | 100 |
runtime-lang.ts | 100 | 100 | 100 | 100 |
...nheritance.ts | 100 | 100 | 100 | 100 |
...ate-syntax.ts | 100 | 100 | 100 | 100 |
theme-json.ts | 100 | 100 | 100 | 100 |
src/domains | 95.48 | 78.92 | 100 | 95.48 |
config.ts | ...

Read more

v0.7.0

Choose a tag to compare

@cabroe cabroe released this 26 Jul 16:47

Install: npx -y automad-mcp-server (published as automad-mcp-server on npm — the earlier scoped name @automadcms/mcp-server was never published).

Added

  • Theme dev serverautomad_theme gains dev / dev_stop / dev_status
    actions. dev runs npm run dev as a detached background process, writes
    <theme>/.automad-mcp/{dev.json,dev.log}, and returns the local URL (port
    discovered in this order: explicit port arg → package.json scripts.dev
    (--port=N / PORT=N) → the first http://localhost:<port> marker in the
    log, up to 20 s). dev_stop sends SIGTERM, escalates to SIGKILL after 5 s,
    and removes the record. dev_status reports the running state without
    blocking. A second dev call for the same theme returns CONFLICT until
    dev_stop is called. Live-verified end-to-end against
    automad/automad:v2: the dev server's PHP+esbuild pipeline starts and
    curl http://localhost:8000 returns the expected content.
  • Two new theme analyzer findingsLANG_WITHOUT_I18N (locale masks
    declared in theme.json but no i18n/<locale>.json file on disk) and
    MAIN_SNIPPET_UNDEFINED (a page mask of the form +snippetName whose
    snippet doesn't exist). Both are severity info, ordered alongside the
    existing define/use checks.
  • KB pages splitdocs/kb.ts (the bundled knowledge base) is now a
    thin re-exporter over 13 per-page modules in docs/kb/pages/. The split is
    invisible at runtime but makes adding / auditing individual KB pages
    tractable. A drift test pins KB_PAGES order and shape.
  • GitHub Pages landing pagedocs/index.html is now a self-contained
    single-file landing page (no script, no remote stylesheet, no remote
    image; pinned by docs-drift.test.ts). The tool table is generated from
    the capability registry via npm run docs:sync, so it cannot drift from
    the code. Counts (tool count, read/destructive, test count) are
    fenced markers that regenerate alongside the README.
  • scripts/verify.ts — single-command pre-PR gate: runs
    buildlintvitest runvitest run --coverage (with the
    vitest.config.ts thresholds enforcing 80% stmt / 70% branch) →
    docs:sync --check. Fails fast on the first broken step.
  • scripts/smoke.ts — end-to-end driver that spawns the built server
    over stdio, runs theme.scaffoldtheme.dev (polls until running) →
    curl (HTTP 2xx/3xx) → theme.dev_stop. Lives in npm run smoke; no
    live Automad instance required (AUTOMAD_MODE=docs + a starter kit path
    is enough).
  • scripts/commit.ts — Conventional-Commits validator. npm run commit:check validates HEAD (for CI / pre-push); node scripts/commit.ts '<msg>' validates + commits. Type- and scope-allow-lists are a single
    source of truth.
  • scripts/docs-sync-all.ts — one-shot: runs docs:sync (static
    markers) and docs:sync:tests (TESTCOUNT via vitest) back-to-back.
    Available as npm run docs:sync:all.
  • scripts/release.ts --push / --release flags — after tagging,
    --push runs git push --follow-tags; --release additionally runs
    gh release create vX.Y.Z with the matching CHANGELOG section as the
    release body (extracted via a new extractChangelogSection export).
    Convenience scripts npm run release:full:{patch,minor,major} wrap
    all three steps.

Changed

  • pages.create now accepts a template field as "{theme}/{template}"
    (no .php)
    — v2 splits on / and appends .php, so
    my-theme/page resolves to packages/my-theme/page.php. Lets a page
    bind to a theme that isn't the site default (the site default itself
    is not exposed via the API; it is set in the dashboard). Documented
    in schemas.ts JSDoc and in the README.
  • theme.scaffold now verifies the starter-kit layout before copying
    (canonical layout: theme.json, components/, blocks/,
    client/index.ts, esbuild.js). A stale or wrong starter-kit
    directory now returns VALIDATION with a concrete missing-list
    instead of silently producing a broken theme.
  • Knowledge-base size unchanged at runtime — splitting the KB into
    per-page modules does not affect the embedded content; the public
    surface (automad://docs, automad://docs/{slug}) is identical.
  • ThemeFs abstractionThemeFs interface gained
    appendLog(path, content) and readLogTail(path, maxBytes) with a
    1 MiB cap and 256 KiB tail retention in the LocalThemeFs
    implementation. The dev server uses these exclusively; no direct
    node:fs calls in new code.
  • Documented the template field's "{theme}/{template}" convention
    in schemas.ts JSDoc and the README. A new README section ("Bind a
    page to a specific theme/template") walks through it with a worked
    example.
  • README + GitHub Pages homepage — both now describe the theme dev
    server, the verify / smoke / commit-check scripts, the
    release:full:* flow, and the page-template convention. The
    homepage's "Theme tooling" card mentions the dev server; the tools
    table shows dev / dev_stop / dev_status for automad_theme.

Fixed

  • Two analyzer findings double-fired on generated output — both
    LANG_WITHOUT_I18N and MAIN_SNIPPET_UNDEFINED previously reported
    against <stubs>/<output> directories that ship with the starter
    kit. The scan is now scoped to snippets/ and components/ for the
    relevant lookups and skips generated paths, so live reports contain
    only findings the user can act on.
  • MAIN_SNIPPET_UNDEFINED had a brittle fixture — the test
    referenced a mainSnippet field on the analyzer's output that the
    code didn't actually return. Added the missing field to the
    ThemeAnalysis fixture literal so the test exercises the production
    code path.
  • page-format.ts was dead code — the file existed for an old
    page-format reader, but nothing in the live HTTP path uses it. The
    tests that referenced it still passed because they imported the
    module directly. Removed the file; tests now use the real
    v2 /_api/page/data response path. No live consumer was affected.
  • docs/kb headless.ts was renamed to headless-api.ts to
    match the spec/plan wording and to avoid colliding with any future
    "headless" UI module. Pure rename; no behavior change.
  • release.ts extraction off-by-oneextractChangelogSection
    originally searched the slice for the next ## [ header before
    stripping the current one, so end was always 0 and the function
    returned a single newline. The function now strips the current
    header first; returns the full section block up to the next ## [
    (or end of file).

v0.6.0 — capability registry as single source of truth

Choose a tag to compare

@cabroe cabroe released this 25 Jul 18:19

The capability registry is now the actual single source of truth, and the discovery facade (automad_discover) scales with it. Same tool surface as v0.5.x — 8 tools, 4 resources, 5 prompts — but one declaration per tool instead of five places that had to agree.

What changed

src/capabilities/registry.ts declares each tool once (title, summary, description, runtime requirement, actions with readOnly/destructive/internal flags). Everything else is derived from it:

Previously hand-maintained Now
WriteAction union (41 literals in write-guard.ts) type-level derivation from the registry
READ_ACTIONS / DESTRUCTIVE_ACTIONS actionsWhere(a => a.readOnly / a.destructive)
a z.enum([...]) per tool in schemas.ts actionEnum("automad_pages")
eight registerTool(...) blocks in server.ts one loop over TOOL_BINDINGS
EXPECTED_ACTIONS, WRITE_ACTION_PREFIX removed — duplicated data the registry already carries
INTERNAL_ACTIONS allowlist in the drift test internal(...) in the registry

New wiring layer src/capabilities/tools.ts: one bind(name, schema, handler) per tool, keyed by a record total over ToolName, with the runtime gate (requires: live | themes | none) taken from the registry. server.ts no longer knows any individual tool.

Scaling pattern

  • New action — one line in the registry. tsc then fails in the domain router's Record<Action, WriteAction> map and its non-exhaustive switch, and nowhere else. Zod enum, write-guard, automad_discover and the generated README table follow automatically.
  • New tool — registry entry + schema in TOOL_INPUT_SCHEMAS + domain router + one bind(...). Both records are total over ToolName, so a missing piece is a compile error.

Fixed

  • automad_discover can describe itself. describe with tool: "automad_discover" returned NOT_FOUND — the schema map had forgotten the tool it belongs to. It now reads TOOL_INPUT_SCHEMAS, the same schemas the server registers from.

Discovery output (additive)

list and describe now also report writeAction (the guard action, e.g. pages.delete) and requires (live / themes / none); describe additionally returns the tool's title and summary alongside the full description. Existing fields are unchanged.

Tests

321 tests (up from 303). The drift tests changed role: instead of comparing two hand-maintained lists — impossible to drift now — they pin the runtime derivations TypeScript can't see: actual Zod enum values, actual guard sets, internal actions hidden from every advertised surface, and real WriteGuard behavior in each write mode. New tests/unit/capabilities-tools.test.ts covers the binding layer's gates.

Generated docs unchanged: 8 tools / 21 read actions / 11 destructive actions.

Full changelog: CHANGELOG.md

Beta: functional and live-verified; not yet published to npm.

v0.5.0 — beta

v0.5.0 — beta Pre-release
Pre-release

Choose a tag to compare

@cabroe cabroe released this 25 Jul 00:39

First tagged release. MCP server for Automad v2 over stdio — verified against a live automad/automad:v2 (beta.51) and covered by unit + opt-in live E2E tests.

Highlights

7 tools (each dispatches on an action):

  • automad_pages — list, get, create, update, delete, move, duplicate, publish, batch_update (create/update auto-publish unless publish:false)
  • automad_media — list, upload
  • automad_shared — get, set
  • automad_config — get, set
  • automad_site — info, search, health
  • automad_docsoffline bundled knowledge base (list/search/get), works with no live instance
  • automad_theme — list, install, activate, uninstall, scaffold, build, read, write, files, analyze, validate, schema, diff, generate

4 resources: automad://themes, automad://themes/{slug}/schema, automad://docs, automad://docs/{slug}

5 workflow prompts: create_blog_post, scaffold_theme, analyze_theme, check_headless_setup, find_docs

Two modes via AUTOMAD_MODE: full (live instance) and docs (standalone docs + theme tooling, no credentials).

Notable fixes

  • pages.list no longer 403s ("CSRF token mismatch") — the body-less POST now attaches __csrf__.
  • Cold-start login false negative fixed — AuthManager retries the auth probe on a transient 403 while the session/CSRF settle; genuine failures still fail fast.

Install

npx @automadcms/mcp-server

See the README for Claude Desktop/Code, Cursor, Cline, and Zed configs. Full changelog: CHANGELOG.md.

Beta: functional and live-verified; not yet published to npm.