MCP
Give an AI coding agent typed access to your project's WordPress REST API while kizlo dev runs.
An AI agent working on your project can read your code, but it can't ask your WordPress anything. It doesn't know which routes exist, what arguments they take, or what a real response looks like. So it guesses.
kizlo dev serves an MCP endpoint that closes that gap. Point your agent at
it and it can search every route your WordPress serves, read the full contract for any of them, and call them.
npx kizlo devThe address appears in the startup summary:
- WP Local: http://localhost:8080/wp-admin
- Database Connection: 127.0.0.1:3306
- MCP: http://127.0.0.1:8300/mcpIt runs only while kizlo dev runs, and only on 127.0.0.1. There is nothing to deploy and no token to leak,
because the server lives in the CLI rather than in your app.
Tool calls reach WordPress as the user in your .env, which in a dev session is usually an administrator.
An agent that can call kizlo_call can change and delete content, so point this at a WordPress you are
willing to lose.
The tools
Three tools reach every route, instead of one tool per route. A seeded WordPress serves well over a hundred routes, and their schemas would fill an agent's context before it did any work.
| Tool | What it does |
|---|---|
kizlo_search_routes | Ranks routes against a query, or browses them all when you omit one. Filters by namespace, method, and read or write, and pages with a cursor |
kizlo_describe_route | Returns one route's full contract: argument schemas, response schemas per status, and declared error codes |
kizlo_call | Runs a route against your WordPress and returns the response |
An agent works through them in that order: search for a route, describe it, call it.
Search covers more than route names. It indexes paths, namespaces, summaries, descriptions, error codes, and the
property names inside a route's arguments and responses, so "create product custom fields" finds
kizlo.settings.postTypes.update without anyone knowing to look there. Each result names the evidence that
selected it, so an agent can tell a route matched on its own name from one that matched on a field buried in a
response.
Route names match the client you write against, so kizlo.postTypes.book.list is the same
route in a tool result and in your code.
Routes come from the same introspection document the contract watcher already
polls, refreshed every few seconds. Register a post type in WordPress while kizlo dev is running and its
routes show up in the next search, with no restart.
Results come back as structured content as well as text, so a harness that parses tool output reads the ranked
list, the schemas, and the response body directly. When WordPress refuses a call, its error code, message, and
data are passed through unchanged rather than flattened into a sentence.
Connecting your agent
The MCP specification covers the protocol, not where a client keeps its configuration, so every harness has its own file. Kizlo doesn't write any of them. Add the URL from the summary to whichever your agent reads:
{
"mcpServers": {
"kizlo": { "type": "http", "url": "http://127.0.0.1:8300/mcp" }
}
}That file is Claude Code's. Cursor uses .cursor/mcp.json and VS Code uses .vscode/mcp.json, both with the
same shape. Codex keeps its servers in ~/.codex/config.toml:
[mcp_servers.kizlo]
url = "http://127.0.0.1:8300/mcp"Set mcp.port before you write the URL down. Without it the port
steps to whatever is free, so it can move between sessions and leave your config pointing at nothing.
Configuration
One optional field:
import { defineConfig } from "kizlo/config"
export default defineConfig({
mcp: {
port: 8300,
},
})port fixes the port so a harness config keeps working. Left unset, the server starts at 8300 and steps
past anything already listening, which is what lets two projects run kizlo dev at once. Set it and the port
never moves: if something else holds it, kizlo dev stops and says so rather than serving somewhere you didn't
choose.
What it doesn't expose
Only WordPress routes. Your Kizlo procedures aren't reachable: they need a running app and in-process access, which is a different thing from a REST call.
The server also stays off in two cases, and says which in the output. A second kizlo dev on the same project
leaves MCP to the first. A project with no resolvable WordPress connection has no contract to serve, so the
contract watcher runs alone.