MCP server for AI assistants ============================ Easy Blog Networks runs a hosted `Model Context Protocol `_ (MCP) server at ``https://app.easyblognetworks.com/mcp/``. Connect Claude Code, Claude Desktop, Cursor or any other MCP client to it once and the assistant can list the blogs on the account and write, edit and manage posts, pages, media, categories, tags and site settings on every one of them. Nothing is installed on the blogs: the server forwards each call to the blog's core WordPress REST API with the application password EBN already holds for Automated Content. Setup ----- The server speaks the Streamable HTTP transport in its stateless form: one JSON-RPC 2.0 message per ``POST``, answered with JSON. Authentication is the same HTTP Basic credential the REST API takes, the account email and the API key from the `Account Settings `_ page:: echo -n "EMAIL:API_KEY" | base64 The Settings page shows the snippets below with the token already filled in. Claude Code:: claude mcp add --transport http easyblognetworks \ https://app.easyblognetworks.com/mcp/ \ --header "Authorization: Basic TOKEN" Cursor (``mcp.json``):: { "mcpServers": { "easyblognetworks": { "url": "https://app.easyblognetworks.com/mcp/", "headers": {"Authorization": "Basic TOKEN"} } } } Claude Desktop does not send custom headers to remote servers yet, so it goes through the ``mcp-remote`` bridge (``claude_desktop_config.json``, needs Node.js):: { "mcpServers": { "easyblognetworks": { "command": "npx", "args": ["-y", "mcp-remote", "https://app.easyblognetworks.com/mcp/", "--header", "Authorization: Basic TOKEN"] } } } A collaborator API key works the same way and only sees the networks it was given. Regenerating the API key invalidates the token. Tools ----- Network tools read EBN's database: ``list_networks`` Networks on the account with blog counts and limits. ``list_blogs`` Blogs with ID, domain, network, state, HTTPS and ``rest_api_enabled``, optionally for one network. ``get_blog`` State, DNS, WordPress stats, health score and last backup of one blog. ``list_backups`` The backup EBN keeps for a blog. Download links come from the REST API. ``enable_rest_api`` Issues the blog a new WordPress application password through croner, the same as the "Enable REST API" button on the blog page. Takes about a minute. ``create_blog`` Creates and deploys a new blog in a network, with the same options as the REST API (title, tagline, theme, plugins, slots, HTTPS). Account key only. ``delete_blog`` Schedules a blog for deletion in 24 hours, like the button on the blog page; the deletion can be cancelled there until then. Account key only. Blog tools take a ``blog_id``, check the caller may see that blog, and forward to ``/wp-json/wp/v2/`` on the blog with ``context=edit`` so the agent gets raw, editable content. Only core WordPress endpoints are used, so EBN Core's post limits and blacklists still apply: * ``list_posts``, ``get_post``, ``create_post``, ``update_post`` * ``list_pages``, ``get_page``, ``create_page``, ``update_page`` * ``list_media``, ``upload_media`` (from a public URL or base64, 10 MB) * ``list_categories``, ``create_category``, ``list_tags``, ``create_tag`` * ``get_site_settings``, ``update_site_settings`` Content cannot be deleted through the tools, and there is no theme or plugin management. New posts and pages are drafts unless ``status`` says otherwise. Errors ------ A blog tool fails with a message that names the blog and says what to do: * the blog is not Online yet, * the REST API is not enabled (call ``enable_rest_api``), * the blog could not be reached, * WordPress rejected the stored credentials (call ``enable_rest_api``), * a plugin or security rule answered without JSON, * WordPress returned an error, which is passed on. Tool failures come back as MCP tool results with ``isError`` set, so the agent can read them and react. Malformed requests get JSON-RPC errors, a missing or wrong credential gets HTTP 401, and anything but ``POST`` gets 405. Limits and audit trail ---------------------- Each account may make 60 tool calls per minute; the rate limit counter lives in redis. Every write tool (``create_*``, ``update_*``, ``upload_media``, ``enable_rest_api``, ``delete_blog``) writes an audit log entry of type ``BlogMCP`` on the blog, naming the tool, the account that called it and what changed, so staff can tell a customer what their agent did. Entries are kept for a year. Implementation -------------- Everything lives in ``ebn.mcp``: the JSON-RPC dispatch, the tool registry (``TOOLS``, filled by the ``@tool`` decorator) and the WordPress forwarding (``wp_call``). Calls to blogs time out after 25 seconds so the agent gets an error before Heroku's router drops the request. ``upload_media`` refuses source URLs that resolve to private addresses and does not follow redirects, because the download runs from inside EBN's infrastructure. OAuth, which claude.ai and ChatGPT connectors need, is a follow-up story.