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.
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.
| Tool | Actions |
|---|---|
whoami | (none); also lists the weblogs the token can act on |
weblogs | list, get, create, update |
entries | list, get (by id or anchor), create, update, trash, restore, delete_forever, preview, revisions, revision, restore_revision (0.4.0) |
categories | list, create, update, delete, list_tags |
media | list, get, import, update, delete, list_directories, create_directory |
pages | list, get, create, update, delete |
redirects | list, create, delete |
audits | seo, seo_entry (0.4.0), media |
users | list, create, update |
site_settings | get, update |
maintenance | flush_cache, rebuild_index, regenerate_renditions |
The full reference is the "MCP" section of docs/api/README.md in the Roller repository.
A token's role and weblog limit are a ceiling on what its owner can already do; they never grant more.
| The agent should | Mint |
|---|---|
| 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 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.
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.
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.
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.weblogs update (ADMIN token) sets icon, about, analytics, newsletter list, business, place (placeType LodgingBusiness with placeLocality, placeLat/placeLng) and booking link.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.entries create with jsonLdType EVENT, eventStart/eventEnd as wall-clock times in the weblog's zone (2026-08-02T19:30) and eventLocation.clear, for example clear: ["featuredImageId"] or clear: ["geoLatitude", "geoLongitude"] (pairs go together).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.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.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.update already changes only the fields sent, so a search description fix sends just searchDescription - no text.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.revisionNote on update labels the revision the update leaves.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.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)./api/mcp: 401 "MCP accepts API tokens only."site_settings update cannot change site.absoluteurl (403 "site.absoluteurl is managed in the admin UI."). Password-reset and set-password links are built from it, so changing it through the API would let a leaked token take over an admin account. Set it in Admin -> Global Config.Origin header gets 403.., .., or containing /, \ or ;): a tool error, and nothing runs.media import refuses any host that resolves to a non-public address (loopback, private ranges, Tailscale/CGNAT, link-local and cloud metadata, Docker networks, non-global IPv6), ports other than 80 and 443, user:password@ URLs, more than 3 redirects, anything that is not a JPEG, PNG, GIF or WebP by its content (SVG included), files over the per-file limit, and fetches over 30 seconds. Each is a 400 saying why.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.