Conventions for the plugin shipped from this repository. User-facing setup instructions live at https://docs.mainwp.com/mcp-server; this file is for people editing the plugin.
.claude-plugin/marketplace.json marketplace manifest (name: mainwp-mcp, owner: MainWP)
plugins/mainwp/.claude-plugin/plugin.json plugin manifest (name: mainwp)
plugins/mainwp/.mcp.json MCP server config, no env map
plugins/mainwp/commands/*.md 10 commands, registered as /mainwp:<basename>
plugins/mainwp/skills/mainwp-dashboard/ synced mirror of the skill
.agents/skills/mainwp-dashboard/ canonical skill, edited here
Install:
/plugin marketplace add mainwp/mainwp-mcp
/plugin install mainwp@mainwp-mcp
Under the plugin the MCP server registers as plugin:mainwp:mainwp and its
tools appear as mcp__plugin_mainwp_mainwp__<tool>, not the
mcp__mainwp__<tool> a manual server config produces. Confirmed 2026-08-01 on
Claude Code 2.1.220 with a live tool call through the plugin, so the prefix is
safe to use in matchers and examples.
If a user already has a manually configured mainwp MCP server, the plugin's
server never starts: Claude Code suppresses a plugin MCP server it considers a
duplicate of a manual entry, matching on the server name or on an identical
command and args (debug log: Suppressing plugin MCP server "plugin:mainwp:mainwp": duplicates manually-configured "mainwp"). The manual
entry silently wins and the plugin contributes only its commands and skill. The
fix is removing the manual entry and keeping the plugin's. If the manual entry
carried an env block, move everything in it (credentials and any MAINWP_*
settings such as safe mode or tool filters) into the environment that launches
Claude Code before removing it; the plugin ships no env map, so its server
reads MAINWP_* only from that environment.
.agents/skills/mainwp-dashboard/ is canonical. Edit there.
npm run sync-skill copies it to plugins/mainwp/skills/mainwp-dashboard/,
and CI byte-compares the two copies (scripts/check-skill-sync.ts). A drifted
mirror fails the build.
Never edit the copy under plugins/. The next sync overwrites it, and until
then the two agents read different instructions.
The canonical location is the cross-agent one on purpose: .agents/skills/ is
what Codex CLI and other agent-skills clients read, so the skill is not a
Claude Code artifact that other clients borrow.
plugins/mainwp/.claude-plugin/plugin.json carries its own version, unrelated
to the server version in package.json. Bump it only in a plugin-release PR.
The server version bumps only at publish time on main. Do not bump either one
to make a local build or a test run pass.
No tool-name enumeration. The tool list comes from the Dashboard's ability
catalog at runtime and varies with the Dashboard version, installed extensions,
and MAINWP_ALLOWED_TOOLS/MAINWP_BLOCKED_TOOLS. A hardcoded list goes stale
and teaches the agent to call tools that do not exist on that connection. Point
at the mainwp://help and mainwp://categories resources instead, and
describe capabilities in plain language.
Do not restate the server's confirmation-flow text. The server owns the
preview, token, and confirmation wording, including the difference between a
real dry_run preview and confirm-without-preview. A second copy in the skill
drifts from the code and can end up claiming a preview happened when none did.
The skill may say that destructive operations stop for confirmation; the exact
mechanics come from what the server returns.
No credential literals. Nothing in plugins/ carries a Dashboard URL,
username, password, or token, including as a placeholder that looks fillable.
Commands never synthesize site IDs. A command that needs a site ID resolves
it from the Dashboard (a site lookup or the mainwp://site/{id} resource) and
stops if it cannot. Guessing an ID targets whatever site happens to hold that
row.
The setup command reports presence only. It prints whether a variable is set, never its value, length, or a masked form, and never dumps the environment or reads credential files.
plugins/mainwp/.mcp.json declares command and args and nothing else. The
server inherits the environment Claude Code runs it in, which is where the
user's credentials already are.
An explicit "MAINWP_URL": "${MAINWP_URL}" style map was tried and rejected.
Claude Code passes an unset ${VAR} through literally, so a user with nothing
configured would hand the server the string ${MAINWP_URL} as a URL and get a
validation failure instead of the friendly missing-configuration guidance the
server prints when the variable is genuinely absent. The ${VAR:-} form avoids
the literal, but it clobbers genuinely set inherited values with empty. Plain
inheritance keeps unset variables unset and set variables intact.
Adding, removing, or changing a configuration option updates both of these in the same change:
- The docs-site configuration reference
(https://docs.mainwp.com/mcp-server/reference/configuration, source in the
mainwp/docsrepo). .agents/skills/mainwp-dashboard/references/safety-and-limits.md, when the option's semantics are something the skill teaches: safe mode, confirmation gating, tool filtering, response and pagination caps, retry behavior.
Then run npm run sync-skill so the plugin mirror matches. A config change
that skips step 2 leaves the agent teaching behavior the server no longer has.
Verified against codex-cli 0.144.1.
Skills load from ~/.agents/skills/ for user-wide use, or .agents/skills/ in
a repo checkout. Copy the canonical skill directory to
~/.agents/skills/mainwp-dashboard/.
The server goes in Codex's config.toml:
[mcp_servers.mainwp]
command = "npx"
args = ["-y", "@mainwp/mcp"]
env_vars = [
"MAINWP_URL",
"MAINWP_USER",
"MAINWP_APP_PASSWORD",
"MAINWP_TOKEN",
"MAINWP_SAFE_MODE",
"MAINWP_REQUIRE_USER_CONFIRMATION",
"MAINWP_ALLOWED_TOOLS",
"MAINWP_BLOCKED_TOOLS",
"MAINWP_SKIP_SSL_VERIFY",
]That list is the common set, not all of them. Add any other MAINWP_*
variable you use from the configuration reference.
env_vars is a list of variable names passed through from the parent
environment; no values appear in the TOML. Codex also supports an env table
of literal key/value pairs. Do not put an application password there. A user
who insists on it should chmod 600 the TOML file.
For users who cannot use environment variables at all, the server's
settings.json configuration file is the fallback. It holds a plaintext
application password, so chmod 700 its directory and chmod 600 the file.
The server does not enforce file modes; that is the user's responsibility.
Checked 2026-07-30 against Claude Code 2.1.220 and codex-cli 0.144.1; plugin MCP behavior rechecked 2026-08-01.
Two caveats from those checks:
- Plugin MCP servers do load in
claude -pheadless sessions on 2.1.220. The 2026-07-30 observation that they did not was the duplicate suppression above: the test machine had a manualmainwpentry. On a machine with one, scripted runs should configure the server directly rather than relying on the plugin. - Running
npx -y @mainwp/mcpwith a working directory inside a checkout of this repo resolves to the local package and fails with "command not found", becausenpm execmatches the local package name before the registry. This only bites during development. Run from a directory outside the checkout, or point the config at the builtdist/index.js.