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.
| Transport | Served by | Auth | Use it for |
|---|---|---|---|
| stdio | the wpp-mcp binary | API key, or email and password via wpp_login | Claude Code, Cursor, Windsurf, a local command server in Claude Desktop |
| Streamable HTTP | wpp-core at /mcp | OAuth 2.1 with PKCE, no client secret | claude.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
/mcptells 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.