Skip to main content
The server accepts configuration through environment variables and a settings.json file. Environment variables take precedence, so you can keep a base file and override specific values at runtime.

Environment variables

* Required as a pair. This is the authentication method.
Authentication is MAINWP_USER + MAINWP_APP_PASSWORD. The Abilities API, which is the only API this server calls, authenticates through native WordPress and does not accept MainWP REST API bearer tokens. MAINWP_TOKEN is still read for compatibility and used when no Application Password credentials are present, but it will fail against the Abilities endpoints. Use an Application Password.

Configuration file

Create settings.json in one of these locations, checked in order:
  1. ./settings.json (the server’s working directory)
  2. ~/.config/mainwp-mcp/settings.json
As of @mainwp/mcp 1.2.0, a working-directory settings.json cannot loosen security settings: requireUserConfirmation: false, skipSslVerify: true, and allowHttp: true are ignored there, with a warning on stderr. Set those through environment variables or the per-user file.
Restrict permissions on any file holding credentials:
A JSON schema ships with the package as settings.schema.json for IDE autocompletion.

Field mapping

If both credential methods are present, the username plus Application Password pair takes precedence over apiToken.

Multiple Dashboards

Give each Dashboard its own folder containing its own settings.json, then point each MCP server entry at the right folder with cwd in your AI client’s configuration:
Because ./settings.json is checked first, each server instance picks up the credentials from its own working directory.

First-run setup

As of @mainwp/mcp 1.3.0, a server started without a Dashboard URL or credentials does not fail to launch. It connects to your AI client in setup mode: the MainWP tools stay hidden and two setup tools take their place, so you can finish configuration in the conversation instead of hunting through stderr.
  • mainwp_get_setup_status reports what is missing and returns setup instructions for your assistant to relay. If credentials are present but the Dashboard was unreachable at startup, calling it again retries the connection with the credentials already loaded.
  • mainwp_configure takes a Dashboard URL, username, and Application Password, verifies them against the Dashboard, and saves them.
There are two ways to finish setup. Adding the credentials yourself is the recommended path. Put MAINWP_URL, MAINWP_USER, and MAINWP_APP_PASSWORD in the env block of this server’s entry in your MCP client config, or in ~/.config/mainwp-mcp/settings.json, then restart the client. The password never passes through the conversation. Pasting them in chat is also fine. Your assistant collects the three values and calls mainwp_configure. The server verifies them, writes them to ~/.config/mainwp-mcp/settings.json with owner-only permissions (0600, in a 0700 directory), and scrubs the password from its own logs and from every response. An Application Password is separate from your WordPress login password and you can revoke it from your profile at any time.
Worth knowing before you choose: a password pasted in chat also becomes part of your chat history, which your AI client and provider may retain. If that bothers you later, revoke the password and create a new one.
Once configuration succeeds, the full tool list appears in the same session for clients that honor MCP list-changed notifications. Clients that do not refresh on their own need a reconnect or restart. mainwp_configure refuses rather than saving something that would be ignored or overridden:
  • Any connection environment variable (MAINWP_URL, MAINWP_USER, MAINWP_APP_PASSWORD, MAINWP_TOKEN) is set. Environment variables outrank the file it writes, so finish setup there.
  • A settings.json exists in the server’s working directory. That file is loaded first and would permanently shadow the saved credentials.
  • The server already has credentials loaded, whether or not they are currently working. See the next section.
It writes only those three connection fields, never security settings, and blocking mainwp_configure through blockedTools removes chat-based setup entirely while leaving the manual path available.

Rejected credentials

If the Dashboard rejects the loaded credentials during startup or a setup connection check, the server enters credentials_rejected and keeps the MainWP tools hidden. Calling mainwp_get_setup_status reports the problem without resending those credentials. mainwp_configure cannot replace them from chat. The setup status includes: Correct the value at the source shown in connectionSources, using the field mapping for settings-file names, then restart your MCP client. Use the WordPress login name or email address and an Application Password from that user’s profile, not the display name or account login password. If the diagnostics report that Application Passwords are disabled, restore their availability on the Dashboard before reconnecting. HTTP 401 is treated as a credential rejection. Other HTTP 4xx responses are treated this way only when WordPress reports invalid_username, invalid_email, incorrect_password, application_passwords_disabled, or application_passwords_disabled_for_user. Network failures, TLS errors, HTTP 5xx, and a bare HTTP 403 leave the server in degraded, where mainwp_get_setup_status can retry with the loaded credentials.

Why setup will not replace credentials you already have

Chat-based setup can get you connected the first time, but it can never overwrite a connection you set up yourself. That line is deliberate, and it is worth knowing where it costs you something. The reason is that an assistant acts on text, and text can come from places you did not intend. A page it read, a site name, or an ability description returned by a server could carry instructions aimed at the assistant rather than at you. If setup could overwrite a working configuration, that kind of injected instruction could quietly repoint your server at someone else’s Dashboard, and every command you ran afterwards would go there. Restricting setup to the case where there is nothing to overwrite removes that possibility, because a server that has no credentials has nothing worth stealing. A tempting middle ground is to allow it when the stored credentials are provably wrong, since the Dashboard rejected them with an authentication error. We do not do that, because it would let the remote side decide when your local configuration may be replaced: a Dashboard that had been compromised, or a connection someone was tampering with, could reject a valid login on purpose to unlock the replacement path, without ever knowing your password. If you rotate the Application Password in WordPress, or the stored one is wrong for any other reason, the server stops resending it after a confirmed rejection. Follow the rejected-credentials diagnostics to update the source that supplies the password, then restart the client.

Behavior settings

  • safeMode blocks every destructive operation at the server. Explained on Safety & Permissions.
  • requireUserConfirmation controls the two-step preview/confirm flow for destructive operations (default on). Also on Safety & Permissions.
  • allowedTools / blockedTools filter the tool catalog. See Restrict Available Tools.
  • schemaVerbosity trades tool-description detail for context tokens. See Optimize Token Usage.
  • abilityNamespaces controls which plugins’ abilities are exposed. See Use Abilities from Other Plugins.
  • responseFormat switches tool response JSON between compact (default) and pretty.

Resource limits

These guard against runaway operations and oversized responses.

Input limits

Tool inputs have separate limits from response data. For each string, the called ability’s input schema can declare a positive, safe-integer maxLength. That value replaces the default, including when it is smaller, up to the server ceiling below. A missing or invalid declaration keeps the default. The check follows nested properties and array items schemas. String lengths use JavaScript’s UTF-16 code-unit count; ASCII and base64 characters each count as one. Over-limit values are rejected, not truncated. String-length errors identify the parameter, effective maxLength, and limitSource (default, schema, or ceiling). Transport limits also apply:
  • Inbound stdio: the buffer limit is 72 MiB of UTF-8 bytes for the incoming JSON-RPC data, including the request envelope and other arguments. Non-ASCII text, JSON escaping, or multiple large values can reach this limit before a per-string limit.
  • GET and DELETE: inputs are encoded in the query string. The complete encoded URL must be at most 8,000 characters, even if the schema permits longer strings. Oversized URLs are rejected before sending.
  • POST: inputs use a JSON body. The ability’s schema and the Dashboard’s PHP, web server, or proxy limits can impose additional constraints. A string limit is not a binary upload-size allowance; base64 encoding increases the payload size.
These input and transport limits are not changed by maxResponseSize or maxSessionData.

Retry logic

Transient errors (HTTP 5xx, HTTP 429, network resets) are retried with exponential backoff and jitter. Only read-only operations are retried, which preserves idempotency; writes fail immediately. Permanent errors (4xx other than 429, authentication failures) are never retried. Total time across retries never exceeds requestTimeout, and retries bypass the rate limiter to avoid deadlocks. Disable for automation or testing with retryEnabled: false.

SSL

The server verifies certificates and refuses plain HTTP by default. skipSslVerify and allowHttp exist for local development against self-signed or HTTP-only environments.
skipSslVerify disables certificate verification entirely and allowHttp sends credentials unencrypted. Use them only in isolated development environments, never in production or on untrusted networks.