For the last couple weeks, I’ve been building a headless Next.js frontend for this site — a project I’ve been calling jazz-nextjs. The idea is straightforward enough: keep WordPress as the content management layer (where I actually like writing) while serving the public-facing site through a modern React frontend hosted on Pantheon’s Next.js infrastructure.
What’s less straightforward is how to make AI development tools actually understand the site they’re building against. Not in the abstract “here’s a REST API endpoint” sense, but natively — the way a developer who’s worked on a codebase for years understands it.
The Model Context Protocol
The Model Context Protocol (MCP) is an open standard, originally developed by Anthropic, for connecting AI systems to external data sources and tools. Think of it as a universal adapter: instead of writing bespoke integrations for every tool an AI might need to use, you expose a standardized server that speaks a common language.
For Claude Code — Anthropic’s CLI for AI-assisted development — MCP servers register as native tools. When it works, you don’t ask Claude to “call this curl command.” You just ask it to look something up, and it does, using the same tool-calling mechanism it uses for reading files or running tests.
The goal here was to get Claude Code to natively understand jazzsequence.com — its post types, taxonomies, content structure, plugins, theme — so that when I’m building the Next.js frontend, Claude doesn’t have to guess at data shapes. It can just ask.
What WordPress Exposes
The foundation is the WordPress Abilities API — a custom API layer built on top of WordPress’s capabilities system. The Abilities API lets plugins register named “abilities” — discrete operations with defined input/output schemas, permission callbacks, and metadata. Think of it as a structured way to say “this site can do X” and describe exactly what X looks like.
The jazzsequence-mcp-abilities plugin registers 34 abilities covering discovery (post types, taxonomies, plugins, theme, menus, shortcodes, hooks, blocks, options, rewrite rules, custom fields, cron jobs), content CRUD (posts and terms), media (upload, update, delete), and operations (cache, cron, options). These are exposed through the MCP Adapter plugin — any ability with meta.mcp.public = true becomes a callable MCP tool. Plus meta-tools for runtime introspection, plus 29 Ninja Forms tools. 66 tools total.
What Had to Be Fixed
In theory, all of this was already built. In practice, nothing was registering.
I used Claude Code to write the connecting tissue (because part of the goal of the Next.js project is to experiment with Claude Code building a comprehensive site). I pointed it at all the available documentation for the Abilities API and MCP Server and even this video that Brad Williams did on the subject.
The first problem was in the plugin itself. The Abilities API requires abilities to be registered on the wp_abilities_api_init action hook — not plugins_loaded, which is where the plugin was initially hooking them. WordPress was silently rejecting every registration attempt because it happened too early in the load order.
The second problem was property naming. The Abilities API uses specific property names: execute_callback, permission_callback, input_schema, output_schema. Claude Code made assumptions about how these properties should be named and guessed wrong. Claude was using shortened versions — execute, permission, input, output — which WordPress would silently ignore. Not a single ability was registering. This had been broken across at least seven plugin releases. Fixing both got 34 abilities working.
The Proxy Problem
MCP servers can speak either stdio or HTTP. Claude Code natively spawns stdio servers. The WordPress MCP endpoint speaks HTTP with session management. The solution is a stdio-to-HTTP proxy: a Node.js script that Claude Code can spawn as a subprocess, translating stdio JSON-RPC into authenticated HTTP requests.
This proxy had its own chain of bugs. A race condition: Claude Code sends initialize and tools/list in rapid succession; the proxy was forwarding both concurrently, so tools/list fired before initialize returned with the session ID. Fix: sequential request queue. Next, there was a protocol version mismatch between what WordPress returns and what Claude Code sends. Fix: intercept and normalize. A timing timeout: two HTTP round-trips to WordPress took ~500ms, which exceeded Claude Code’s threshold for marking a server as having zero capabilities. Fix: respond to tools/list immediately from a local cache, establish the WordPress session in the background.
The hardest bug: Claude Code silently rejects a server’s entire tools list if any tool’s inputSchema properties contain enum or default keywords. Zero tools register, zero error messages, no indication of why. The WordPress tools use enum extensively. The diagnostic approach that finally worked: a minimal two-tool test server (working), then progressively swapping in real WordPress tools until it broke, isolating which schema keyword caused the failure. Fix: strip enum and default from property schemas in the proxy.
The Result
Claude Code now has 66 native tools for interacting with jazzsequence.com. At session startup it loads them from a local cache in under a millisecond; in the background it refreshes from WordPress and updates if anything changed.
From inside a Claude Code session working on the Next.js frontend, asking “what custom post types does this site have?” returns a live answer from the actual WordPress installation — not stale docs, not memory. All have Zod schemas in the Next.js codebase. The MCP integration means Claude can verify those schemas against real data without manually copy-pasting API responses.
Drafted by Claude (Sonnet 4.6) with final editing by a human.

Leave a Reply