Releases: cabroe/automad-mcp-server
Release list
v0.7.1
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 | ...
v0.7.0
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 server —
automad_themegainsdev/dev_stop/dev_status
actions.devrunsnpm run devas a detached background process, writes
<theme>/.automad-mcp/{dev.json,dev.log}, and returns the local URL (port
discovered in this order: explicitportarg →package.jsonscripts.dev
(--port=N/PORT=N) → the firsthttp://localhost:<port>marker in the
log, up to 20 s).dev_stopsends SIGTERM, escalates to SIGKILL after 5 s,
and removes the record.dev_statusreports the running state without
blocking. A seconddevcall for the same theme returnsCONFLICTuntil
dev_stopis called. Live-verified end-to-end against
automad/automad:v2: the dev server's PHP+esbuild pipeline starts and
curl http://localhost:8000returns the expected content. - Two new theme analyzer findings —
LANG_WITHOUT_I18N(locale masks
declared intheme.jsonbut noi18n/<locale>.jsonfile on disk) and
MAIN_SNIPPET_UNDEFINED(a page mask of the form+snippetNamewhose
snippet doesn't exist). Both are severityinfo, ordered alongside the
existingdefine/usechecks. - KB pages split —
docs/kb.ts(the bundled knowledge base) is now a
thin re-exporter over 13 per-page modules indocs/kb/pages/. The split is
invisible at runtime but makes adding / auditing individual KB pages
tractable. A drift test pinsKB_PAGESorder and shape. - GitHub Pages landing page —
docs/index.htmlis now a self-contained
single-file landing page (no script, no remote stylesheet, no remote
image; pinned bydocs-drift.test.ts). The tool table is generated from
the capability registry vianpm 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
build→lint→vitest run→vitest run --coverage(with the
vitest.config.tsthresholds 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, runstheme.scaffold→theme.dev(polls until running) →
curl(HTTP 2xx/3xx) →theme.dev_stop. Lives innpm 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:checkvalidates 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: runsdocs:sync(static
markers) anddocs:sync:tests(TESTCOUNT via vitest) back-to-back.
Available asnpm run docs:sync:all.scripts/release.ts--push/--releaseflags — after tagging,
--pushrunsgit push --follow-tags;--releaseadditionally runs
gh release create vX.Y.Zwith the matching CHANGELOG section as the
release body (extracted via a newextractChangelogSectionexport).
Convenience scriptsnpm run release:full:{patch,minor,major}wrap
all three steps.
Changed
pages.createnow accepts atemplatefield as"{theme}/{template}"
(no.php) — v2 splits on/and appends.php, so
my-theme/pageresolves topackages/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
inschemas.tsJSDoc and in the README.theme.scaffoldnow 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 returnsVALIDATIONwith 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 abstraction —
ThemeFsinterface gained
appendLog(path, content)andreadLogTail(path, maxBytes)with a
1 MiB cap and 256 KiB tail retention in theLocalThemeFs
implementation. The dev server uses these exclusively; no direct
node:fscalls in new code. - Documented the
templatefield's"{theme}/{template}"convention
inschemas.tsJSDoc 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, theverify/smoke/commit-checkscripts, the
release:full:*flow, and the page-template convention. The
homepage's "Theme tooling" card mentions the dev server; the tools
table showsdev/dev_stop/dev_statusforautomad_theme.
Fixed
- Two analyzer findings double-fired on generated output — both
LANG_WITHOUT_I18NandMAIN_SNIPPET_UNDEFINEDpreviously reported
against<stubs>/<output>directories that ship with the starter
kit. The scan is now scoped tosnippets/andcomponents/for the
relevant lookups and skips generated paths, so live reports contain
only findings the user can act on. MAIN_SNIPPET_UNDEFINEDhad a brittle fixture — the test
referenced amainSnippetfield on the analyzer's output that the
code didn't actually return. Added the missing field to the
ThemeAnalysisfixture literal so the test exercises the production
code path.page-format.tswas 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/dataresponse path. No live consumer was affected.docs/kbheadless.tswas renamed toheadless-api.tsto
match the spec/plan wording and to avoid colliding with any future
"headless" UI module. Pure rename; no behavior change.release.tsextraction off-by-one —extractChangelogSection
originally searched the slice for the next## [header before
stripping the current one, soendwas 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
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.
tscthen fails in the domain router'sRecord<Action, WriteAction>map and its non-exhaustiveswitch, and nowhere else. Zod enum, write-guard,automad_discoverand the generated README table follow automatically. - New tool — registry entry + schema in
TOOL_INPUT_SCHEMAS+ domain router + onebind(...). Both records are total overToolName, so a missing piece is a compile error.
Fixed
automad_discovercan describe itself.describewithtool: "automad_discover"returnedNOT_FOUND— the schema map had forgotten the tool it belongs to. It now readsTOOL_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
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 unlesspublish:false)automad_media— list, uploadautomad_shared— get, setautomad_config— get, setautomad_site— info, search, healthautomad_docs— offline bundled knowledge base (list/search/get), works with no live instanceautomad_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.listno longer 403s ("CSRF token mismatch") — the body-less POST now attaches__csrf__.- Cold-start login false negative fixed —
AuthManagerretries the auth probe on a transient 403 while the session/CSRF settle; genuine failures still fail fast.
Install
npx @automadcms/mcp-serverSee 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.