Connecting EmDash to Pizza Bot: draft blog posts from chat

5 min read

Connecting EmDash to Pizza Bot: draft blog posts from chat

Every post on this site since the migration started life as a chat message. "Write something about Cloudflare Workers and D1, draft it to my blog." Pizza Bot researches the topic, writes the Markdown, and queues it on EmDash as a draft, where I read it, edit it, and hit publish. Under the hood it is two pieces of configuration: an MCP server that gives Pizza Bot access to EmDash, and a skill that tells it how to use those tools. This post is that setup, end to end.

The endpoint you need is https://www.daniel-mitchell.com/_emdash/api/mcp.

What you need before you start

  • Pizza Bot running. The desktop app with its embedded local backend, the browser interface pointing at an api-server, the terminal CLI, or a standalone backend.
  • An EmDash site with its MCP endpoint enabled and reachable over HTTPS.
  • A credential for that endpoint, stored as an environment variable rather than pasted into a config file.
  • A model provider configured under Settings, then a model selected for the task.

Step 1 — Add the EmDash MCP server

Pizza Bot is local-first: your conversations, memory and configuration live on your machine, and it talks to nothing you have not configured. MCP servers are how external tools get in, and Pizza Bot ships with none bound to EmDash, so you add one. There are two routes, and they are equivalent:

  1. The MCP Servers screen in the app.
  2. Editing <PIZZA_DATA_ROOT>/.mcp.json — the default data root is ~/.pizza-bot-oss.

Either way the entry needs three things: a name, the endpoint URL, and a way to authenticate.

Pizza Bot's MCP Servers screen showing the daniel-mitchell-blog-emdash server, with its endpoint URL and the Authorization header that carries the EmDash token.

That Authorization header takes an EmDash personal access token. Create one in the EmDash admin interface (not Pizza Bot's Settings) under Settings → API Tokens: give it a name, grant only the scopes the skill needs (content:read, content:write and media:write are enough to draft a post with a header image), then copy the value. EmDash shows the plaintext once and stores only its hash, so paste it straight into the header above, or into EMDASH_MCP_TOKEN below. If it is ever exposed, revoke it from the same screen and issue a replacement.

{
  "mcpServers": {
    "daniel-mitchell-blog-emdash": {
      "url": "https://www.daniel-mitchell.com/_emdash/api/mcp",
      "headers": {
        "Authorization": "Bearer ${EMDASH_MCP_TOKEN}"
      }
    }
  }
}

Two details matter more than they look:

  • The name is a contract. The blog skill declares a dependency on tools from a server called daniel-mitchell-blog-emdash. Rename the server and the skill quietly loses its tools.
  • Keep the token out of the file. Put the credential in an environment variable — EMDASH_MCP_TOKEN above — and reference it. The MCP Servers screen stores secrets for you; a hand-edited .mcp.json should hold references rather than literal credentials, and it should never be committed. A standalone backend also needs those variables present on the backend host, not just on your laptop.

Step 2 — Confirm the server is connected

Open the MCP Servers screen and check the status of the EmDash entry. A healthy connection exposes the tools the skill will use:

  • content_create, content_update, content_publish, content_schedule — write and manage entries.
  • content_list, content_get, search — read what is already there.
  • media_upload, media_update — images and their metadata.
  • schema_get_collection, taxonomy_list — discover which fields and terms the site actually has.

If a server shows as failed, open its detail screen and reconnect it.

Step 3 — Enable the blog skill

The skill is daniel-mitchell-emdash-blog, and its header declares the whole dependency in one line:

tools:
  - "mcp:daniel-mitchell-blog-emdash:*"

That means the skill only becomes ready once the MCP tools it requires are enabled and connected. A skill sitting next to a disconnected server looks installed but will not run.

Skills are labelled in the library by where they came from: Built-in skills ship with Pizza Bot, Plugin skills arrive with a plugin, Custom skills are yours, and Customized means you have overridden a Built-in or Plugin skill. Editing a Built-in or Plugin skill creates that override without changing the original, and deleting the override restores the original version again. So if you want a different house style — a mandatory outline step, a fixed closing call to action — that is where to put it.

Step 4 — Use it

Ask in chat. Something like "Create a new draft on my blog explaining how we cut cold starts on Workers" is enough. The skill works through an outline, a draft, a formatting and keyword pass, and then a content creation call against EmDash.

Two things happen behind the scenes that are worth understanding:

  • Pizza Bot delegates to a tool-scoped skill worker. The worker sees the skill's instructions and only the tools the skill declares, and the Activity panel shows the delegated work as it runs.
  • Skills can require human approval before consequential tools run. If you want a confirm-before-publish gate, that is the setting to turn on.

The result is a draft, not a live post. Publishing is a separate, deliberate step, and later edits create a new draft rather than rewriting the live page.

How a draft maps onto EmDash fields

  • The headline you give in chat becomes title.
  • The summary paragraph becomes excerpt.
  • The Markdown body is stored in content, as Portable Text.
  • Three to five keywords become tags, through the tag taxonomy.
  • A header image is uploaded with media_upload, then attached to the post as featured_image.

Troubleshooting

  • The skill is unavailable. Open it and review its required tools. Confirm each required MCP server is enabled and connected, reconnect any server that failed, and confirm the skill itself is enabled.
  • The server connects but the skill still will not run. Check the server name. A renamed EmDash server no longer matches the mcp:daniel-mitchell-blog-emdash:* declaration.
  • Authentication failures. Confirm the environment variable exists and is visible to the backend actually in use, then regenerate the token if it has been rotated. Do not put the token in a URL.
  • The post is not on the site. It is a draft. Check the drafts list, and publish when you are happy with it.

Try it

If you already run EmDash, the whole integration is one MCP entry and one enabled skill — about five minutes of setup, and after that "draft this to my blog" works from the chat box. Start with a draft of something you have already written by hand and compare the two; that is the fastest way to decide how much of your voice the skill needs to be told about.