MCP Server for Client Content and Site Updates
How clients connect Claude or Codex to their AstroWEBTheme site to draft and update text and images safely, how the endpoint is secured, and what is planned for developer-side theme updates.

AstroWEBTheme includes an optional client content MCP: a secured endpoint on the site itself that lets a client’s AI assistant draft and update the site’s text and images. Nothing a client’s assistant writes goes live on its own. A person reviews and publishes it.
A second, developer-side MCP for planning theme-core updates is still planned.
What clients can do
A connected assistant can:
- list and read entries in the collections you allow
- create or update a draft from plain Markdown
- upload images with alt text
- ask for a review with
request_publish
It cannot publish, delete, change settings, read users, or touch anything outside the allowed collections. It has no access to the dashboard.
Where changes are stored
The endpoint picks the storage automatically.
| Situation | Where drafts go | How they go live |
|---|---|---|
| A D1 database is bound (preferred) | content_entries as draft; images go to R2 and the media library |
An admin publishes from the dashboard. Edits to an entry that is already live are staged as a pending revision and applied by an admin with Restore |
| No database | A content/<token>-<date> git branch, using the GitHub API |
request_publish opens a pull request; merging it publishes |
In git mode the MCP never writes to the base branch and never merges.
Turn it on
- Edit
src/site/config/mcp.mjs: setenabled: true, list the editablecollections, and setdefaultAuthor. In git mode also fill ingit.ownerandgit.repo. - Apply
migrations/014_mcp_tokens.sqlif you use D1. - Create a token for each client (see below).
- Deploy.
The endpoint is https://your-site/mcp/. The trailing slash matters because the site uses trailingSlash: "always".
Create a client token
node devtools/mcp-token.mjs --label "Acme editor" --role editor --days 90 --collections blog
The script prints the token once, plus either the SQL to store its hash in D1 or a JSON entry for the MCP_TOKENS secret when there is no database. Only the SHA-256 of the secret is ever stored. Tokens must expire (1 to 365 days), can be limited to some collections, and are revoked by removing the entry or setting revoked_at.
| Role | Can |
|---|---|
viewer |
read content |
editor |
read, save drafts, upload images, request review |
In git mode, also set a GITHUB_TOKEN secret. Use a fine-grained token restricted to this one repository with only Contents (write) and Pull requests (write).
Connect a client
Claude (custom connector). Add a custom connector with the URL https://your-site/mcp/ and send the token as a bearer token in the Authorization header, if your Claude plan lets you set one for a connector.
Claude Code.
claude mcp add --transport http astroweb-content https://your-site/mcp/ --header "Authorization: Bearer awm_YOUR_TOKEN"
Codex. Add the server to config.toml with the URL and a bearer token environment variable, as described in the Codex MCP documentation for your version.
Client options change between versions. Check the current documentation for your client, and never paste the token into a chat or a shared file.
Tools
| Tool | Needs | What it does |
|---|---|---|
list_content |
viewer | Lists entries in a collection |
get_content |
viewer | Returns one entry as Markdown |
save_draft |
editor | Creates or updates a draft from Markdown |
upload_media |
editor | Uploads a PNG, JPEG, WebP, GIF or AVIF image with alt text |
request_publish |
editor | Marks a draft for review, or opens a pull request in git mode |
Security model
The endpoint is built on the assumption that both the client’s assistant and the content it reads can be manipulated.
- Off by default. Disabled until
enabled: true; a disabled endpoint returns 404. - Scoped tokens. Random 256-bit secrets, stored only as hashes, always expiring, revocable, limited by role and collection. Comparison is constant-time and failures return one generic 401.
- Separate from the dashboard. The endpoint does not use dashboard sessions, so a token cannot reach any admin page or API.
- Nothing publishes itself. There is no publish tool. Live entries are never edited in place, and a git pull request is never merged by the MCP.
- Plain Markdown only. MDX can run code at build time, so input containing imports, exports,
{expressions}, raw HTML, components orjavascript:/data:links is rejected. Frontmatter is generated from validated fields, never accepted from the client. - Images checked by content. The file type is read from its first bytes. SVG and anything unrecognised is refused, size is capped, and file names are rewritten.
- Prompt-injection aware. Content returned to the assistant is labelled as data, and a read can never trigger a write.
- Locked-down paths. In git mode, writes are limited to the allowed collection folders and the media folder, on a
content/branch. - Browser and network protections. Requests with a browser
Originheader are refused unless listed, onlyPOSTwith JSON is accepted, responses areno-store, and bodies are size-limited. - Rate limits and audit. Per-IP and per-token request limits, a separate hourly write limit, and every call is recorded in
audit_log(or the worker log when there is no database) without content. - Errors reveal nothing. Unexpected failures return a generic message.
Admin API hardening
Adding client tokens made it important that the existing dashboard APIs only accept admins. Every /api/admin/content and /api/admin/media handler now requires the admin role; before this change several accepted any signed-in user, and six media handlers checked nothing beyond the middleware.
Limits to know about
- Without KV, rate limits are per worker instance and therefore weaker.
- Applying a staged edit to a live D1 entry (Restore) replaces the body only; title and description changes are recorded on the revision but not applied automatically.
- A stolen token can draft content until it expires or is revoked, so keep lifetimes short and revoke promptly.
Recommended workflow
- The client asks their assistant to read the page and propose changes.
- The assistant saves a draft and uploads images with alt text.
- The assistant calls
request_publish. - You review the draft or pull request and publish or merge it.
Planned developer update server
A separate, local, read-only MCP server for developers is still planned in devtools/mcp/. It would expose theme_info, update_plan, check, doctor and drift_report by wrapping the existing astroweb dry-run commands, and would never write outside core/ or touch secrets. It is not shipped yet. See Theme Update and Core Boundaries for what theme updates preserve.