Roller MCP Runbook

Part of RollerHub. Deployment: RollerOnDocker.

Availability: /api/mcp ships in Roller 0.1.14 and later; blog.wikantik.com runs 0.4.0. weblogs create, media import, clear, event dates and whoami's weblog list need 0.1.15 or later. The items marked (0.4.0) - editing an entry in place, contentHash, fields, entries get by anchor, revisions and the per-entry SEO audit - need 0.4.0 or later.

What it is

Roller serves a Model Context Protocol endpoint at https://<site>/api/mcp (Streamable HTTP, no sessions). Its 11 tools are a facade over Roller's REST API (/api/v1): each tool call runs the matching API route inside Roller, so a token can do exactly what it could do over REST, with the same checks and the same error messages.

ToolActions
whoami(none); also lists the weblogs the token can act on
weblogslist, get, create, update
entrieslist, get (by id or anchor), create, update, trash, restore, delete_forever, preview, revisions, revision, restore_revision (0.4.0)
categorieslist, create, update, delete, list_tags
medialist, get, import, update, delete, list_directories, create_directory
pageslist, get, create, update, delete
redirectslist, create, delete
auditsseo, seo_entry (0.4.0), media
userslist, create, update
site_settingsget, update
maintenanceflush_cache, rebuild_index, regenerate_renditions

The full reference is the "MCP" section of docs/api/README.md in the Roller repository.

Pick the token's role

A token's role and weblog limit are a ceiling on what its owner can already do; they never grant more.

The agent shouldMint
Read and run the audits only"role": "READ"
Publish to one weblog"role": "POST", "weblog": "<handle>"
Publish to several weblogs"role": "POST" (no weblog)
Manage the whole site: weblogs, users, settings, maintenance"role": "ADMIN" (no weblog)

Mint one token per agent, so each can be revoked on its own. An ADMIN token is a strong credential: keep it out of repositories and shared config.

Mint, connect, revoke

  1. Mint with the account's username and password (Basic auth is accepted only for minting, listing and revoking tokens):

    curl -sS -u USER -H 'Content-Type: application/json' \
      -X POST https://blog.wikantik.com/api/v1/tokens \
      -d '{"label": "publishing-agent", "role": "POST", "weblog": "myblog"}'
    

    The raw secret is the token field, shown exactly once. bin/roller-api auth login --url https://blog.wikantik.com is the other route in; it mints an ADMIN token for the CLI.

  2. Connect a client that supports Streamable HTTP with a custom header. Claude Code:

    claude mcp add --transport http roller https://blog.wikantik.com/api/mcp \
      --header "Authorization: Bearer $ROLLER_API_TOKEN"
    

    Use the https:// site URL even from inside the home network: http://docker1.lan:8084 would send the token unencrypted across the LAN.

  3. Revoke with Basic auth: curl -sS -u USER -X DELETE https://blog.wikantik.com/api/v1/tokens/<id>. GET /api/v1/tokens lists the ids. A revoked token stops working at once.

A new weblog, end to end

  1. Create it: weblogs create with handle, name and theme (a wrong theme answers with the valid ids). Any writing token not limited to one weblog may create one when its user could on the Create Weblog page; the user becomes the owner. Reserved paths (api, page, search, ...) are refused as handles.
  2. Configure it: weblogs update (ADMIN token) sets icon, about, analytics, newsletter list, business, place (placeType LodgingBusiness with placeLocality, placeLat/placeLng) and booking link.
  3. Images: media import fetches a JPEG, PNG, GIF or WebP from a public http(s) URL into the weblog's media; set an entry's featuredImageId to the returned id. Uploads must be on in Admin -> Global Config (off by default), and the per-file limit there (2 MB by default) caps an import too.
  4. Events: entries create with jsonLdType EVENT, eventStart/eventEnd as wall-clock times in the weblog's zone (2026-08-02T19:30) and eventLocation.
  5. Removing a value: null never removes anything. Name the fields in clear, for example clear: ["featuredImageId"] or clear: ["geoLatitude", "geoLongitude"] (pairs go together).

Editing an entry (0.4.0)

  1. Find it: entries get with anchor (the URL slug) instead of id. Every entry carries contentHash, the SHA-256 of its stored text - the same scheme as Wikantik's read_page/update_page.
  2. Change part of the text: entries update with edits: [{find, replace}]() and expectedContentHash. Each find must match the stored text exactly once (applied in order; replace: "" deletes); if any edit misses, every failing one is reported and nothing is saved. entries preview with the id takes the same edits and shows the result without saving.
  3. On 409 entry-changed: someone changed the entry since you read it; the error carries currentContentHash. Get it again and redo the edits. Without expectedContentHash the last write wins.
  4. Metadata only: update already changes only the fields sent, so a search description fix sends just searchDescription - no text.
  5. Keep responses small: fields: ["anchor", "searchDescription"] on get, list, create, update and restore returns only those (plus id). An SEO scan of a whole weblog is one list with fields.
  6. Say why: revisionNote on update labels the revision the update leaves.
  7. Look back and undo: entries revisions lists an entry's revisions (newest first, who saved over each, the note); entries revision shows one with a unified diff against the current text (or against another revision); entries restore_revision puts it back, and the replaced text becomes a revision itself.
  8. Check SEO: audits seo lists a weblog's entries with missing or over-long titles and descriptions and duplicates; audits seo_entry checks one entry in full, body included (an <h1> in the body, skipped heading levels, images without alt text, broken in-page and internal links).

What it refuses

Reading a refusal

A refused tool call returns a tool error whose text is the API's problem body, with status and detail:

Every tool call is logged once on the roller.mcp logger (in docker logs): token id, user, tool, action, weblog, status and duration, never the arguments.