API and MCP

Everything the admin UI does, it does over the same REST API you can call yourself. On top of that API, WordPuppi ships an MCP server so an AI assistant can create sites, write content, and deploy them through conversation. This page covers keys, the two step auth flow, and both MCP transports.

API keys

Create keys on any site’s Settings, API Keys page.

Authenticating

API keys are not sent to the API directly. You exchange one for a short lived JWT, then use the JWT as a bearer token.

Then:

# 1. Exchange the API key for a JWT
curl -X POST https://app.wordpuppi.com/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"api_key": "YOUR_API_KEY"}'

# 2. Call the API with the JWT
curl https://app.wordpuppi.com/api/v1/sites \
  -H "Authorization: Bearer <token from step 1>"

A tour of the REST surface

Everything below is JWT authenticated and site scoped by slug.

Other prefixes worth knowing: /api/v1/sites/{slug}/media, /api/v1/sites/{slug}/static, /api/v1/sites/{slug}/forms, /api/v1/sites/{slug}/content-types, /api/v1/barks, /api/v1/distribution, and /api/accounts.

The MCP server

WordPuppi ships 43 MCP tools covering sites, content, forms, themes, media, members, distribution, fediverse, and Cloudflare deployment, plus four guided prompts (site_setup, theme_picker, form_builder, deploy_site) and 14 theme kits.

Every tool carries MCP annotations, so a client can auto approve reads and warn on destructive calls: list_*, get_*, read_*, and status are read only; delete_* are destructive and non idempotent; build, deploy, Cloudflare, distribution, and fediverse tools are open world.

There are two transports onto the same tool surface.

TransportServed byAuthUse it for
stdiothe wpp-mcp binaryAPI key, or email and password via wpp_loginClaude Code, Cursor, Windsurf, a local command server in Claude Desktop
Streamable HTTPwpp-core at /mcpOAuth 2.1 with PKCE, no client secretclaude.ai, Claude Desktop, Claude Code, ChatGPT custom connectors

Remote (paste one URL)

Add a custom connector

In Claude, go to Settings, Connectors, Add custom connector.

Paste the URL

Use https://app.wordpuppi.com/mcp.

Leave both Advanced fields empty

There is no OAuth Client ID and no Client Secret to enter. Leave both blank.

Connect

You are sent to the WordPuppi sign-in page, and that is the whole flow.

Claude Code can do the same from the CLI:

claude mcp add --transport http wordpuppi https://app.wordpuppi.com/mcp
# then, inside Claude Code:
/mcp

Local (stdio)

# Auto-loads saved config, or starts disconnected so the client can call wpp_login
wpp-mcp serve

# Or be explicit with a persistent API key (best for headless use)
wpp-mcp serve --api-url https://app.wordpuppi.com --api-key lo-xxx

# Save credentials once for later `serve` runs
wpp-mcp setup --api-url https://app.wordpuppi.com --email you@example.com --password ...

serve also picks up WPP_EMAIL, WPP_PASSWORD, and WPP_API_URL from the environment. Config for Claude Code looks like this:

{
  "mcpServers": {
    "wordpuppi": {
      "command": "wpp-mcp",
      "args": ["serve", "--api-url", "https://app.wordpuppi.com", "--api-key", "lo-xxx"]
    }
  }
}

Cursor and Windsurf take the same command in their own MCP config.

A typical MCP session

Create the site, choose a theme through settings, write content as HTML with status set to published, build, then deploy:

wpp_create_site then wpp_update_settings then wpp_create_content then wpp_build_site then wpp_deploy_cloudflare.

Security notes

  • stdio runs on your machine and your credentials never leave it.
  • Remote uses OAuth 2.1 with PKCE and per session JWTs minted from validated access tokens. No client secret is stored or transmitted. Tokens are bound to the requested resource, and a 401 from /mcp tells the client where the resource metadata lives and which scopes it needs.
  • Business logic errors come back as tool results marked as errors. Only transport and auth failures surface as MCP protocol errors.
Esc