wp-remote-bridge
Provides guarded SQL access to a WordPress site's MariaDB database, with read-only queries by default, policy-controlled writes, backup requirements, statement blacklists, response limits, and audit logging.
Provides guarded SQL access to a WordPress site's MySQL database, with read-only queries by default, policy-controlled writes, backup requirements, statement blacklists, response limits, and audit logging.
Provides controlled, audited, reversible access to a WordPress site, including guarded SQL queries against its database and running pre-written PHP scripts with WordPress fully loaded via wp_run.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
wp-remote-bridge
A local MCP server (Node/TypeScript, stdio transport) plus a small PHP bridge that gives an AI agent — or any MCP client — controlled, audited, reversible access to a WordPress site hosted on shared hosting with no SSH, no WP-CLI, and no remote MySQL (port 3306 closed). The only access the host gives you is FTP/SFTP (and sometimes phpMyAdmin).
Two capabilities:
Guarded SQL against the site's MySQL/MariaDB database (read by default, write only under explicit, time-boxed, per-environment policy).
Script execution (
wp_run): run a PHP script, written and tested ahead of time, with WordPress fully loaded — the equivalent ofwp eval-fileon a server that doesn't have WP-CLI.
Both are reached over HTTPS through a tiny PHP file dropped at a random path in the web root; FTP/SFTP is only used for the bridge's own lifecycle (deploy/enable/disable/remove) and for uploading wp_run scripts.
Architecture
┌──────────────────────────┐ stdio ┌────────────────────────────────┐
│ MCP client (agent) │ ───────────────────▶ │ wp-remote-bridge (Node/TS) │
└──────────────────────────┘ │ - typed MCP tools (zod) │
│ - client-side SQL hint │
│ - HMAC request signing │
│ - local JSONL audit log │
└───────┬────────────────┬───────┘
HTTPS (signed POST) │ │ SFTP/FTP(S)
▼ ▼ (deploy/enable/disable/
┌─────────────────────────┐ remove bridge, upload
│ <webRoot>/<random-path>/│ wp_run scripts)
│ bridge.php │
│ run.php │
│ .htaccess │
└───────┬─────────────────┘
│ DB credentials read from the site itself
│ wp_run: require <webRoot>/wp-load.php
▼ PDO, localhost
┌─────────────────────────┐
│ MySQL / MariaDB │
└─────────────────────────┘
┌─────────────────────────┐
│ <privateRoot>/wrb/ │ scripts/, logs/, nonces/,
│ │ enabled-until, secret
└─────────────────────────┘ (outside the web root)Related MCP server: AgentLinkWP
Security model
Signed, obfuscated protocol. A single
POST, JSON body base64-encoded in fieldp(plain SQL or PHP script names in a POST body are often blocked by mod_security on shared hosting). HeadersX-WRB-Ts(epoch seconds),X-WRB-Nonce(128-bit random),X-WRB-Sig = HMAC-SHA256(secret, ts\nnonce\nsha256(body)). The bridge rejects clock skew > 60 s, a replayed nonce, or a signature that failshash_equals.Random bridge path. The bridge files live at a random, unguessable path under the web root, generated once and kept in local client state.
Kill switch. The bridge only answers while
<privateRoot>/wrb/enabled-until(outside the web root) holds a future timestamp; otherwise it returns a bare404with no body.bridge_enablesets it (2 h default, 8 h max);bridge_disableclears it. Expiry is automatic even if you forget to disable it.Per-environment policy baked into the bridge. Each environment declares
allowWrite(SQL writes) andallowRun(wp_run), bothfalseby default. The policy is written into the bridge file itself at deploy time and enforced there — not read from the request, so even a valid HMAC signature can't write to or run a script on a closed environment. Changing the policy requires redeploying.Bridge-side SQL classification is the only authority. The client does a cosmetic classification for agent ergonomics, but the PHP bridge classifies every statement itself (read / write / forbidden) and never trusts the client's judgment.
Read-only transactions, one statement per call. Reads run inside
START TRANSACTION READ ONLY+ROLLBACK; PDO has multi-statements and emulated prepares disabled; a bare;outside a string/comment is rejected.Hard blacklist, rejected in every mode:
DROP DATABASE,CREATE USER,GRANT,REVOKE,SET GLOBAL,INTO OUTFILE,INTO DUMPFILE,LOAD DATA,LOAD_FILE(,SHUTDOWN,KILL,INSTALL PLUGIN,CALL,PREPARE,HANDLER,LOCK TABLES.Bounds. At most 1000 rows and 2 MB per response (
truncated: truebeyond that), a 15 s statement timeout.Backup before write. A real (non-dry-run) write requires a
backup_reffrom a backup taken on the touched table less than 30 minutes earlier; destructive tools default todry_run: true.No secrets anywhere in output. Credentials, passwords, and HMAC secrets never appear in responses, logs, or error messages; PDO errors and script output are sanitized first.
Audit on both sides. Every call is logged by the bridge (
<privateRoot>/wrb/logs/wrb.log, outside the web root) and by the client (~/.wp-remote-bridge/audit.jsonl): timestamp, environment, action, a SHA-256 fingerprint of the SQL or script, truncated SQL, duration, rows/affected or status — never the SQL parameters or any secret.
Requirements
Node.js ≥ 20
A target site running PHP ≥ 8.1 with the
pdo_mysqlextensionMySQL or MariaDB reachable as
localhostfrom PHP on that host (this is exactly the case the tool is built for: the database isn't reachable from outside)FTP, FTPS, or SFTP access to the site
Install & build
git clone https://github.com/seangoogoo/wp-remote-bridge-mcp.git
cd wp-remote-bridge-mcp
npm install
npm run build # tsc → dist/
npm run gen-secret # prints a 64-hex-character HMAC secret, one per environmentConfiguration
Nothing site-specific is committed to this repository. You describe each site in your own, gitignored envs.json (see envs.example.json for the shape, with no secrets):
{
"<env-name>": {
"label": "human-readable label",
"siteUrl": "https://example.com",
"transport": {
"type": "local" | "ftp" | "ftps" | "sftp",
"host": "…", // required unless type is "local"
"port": 22, // optional
"webRoot": "/www", // where the bridge files get dropped
"privateRoot": "/" // where wrb/ (outside the web root) is created; must not be inside webRoot
},
"dbCredentials": {
"mode": "wp-config" // reads literal define('DB_*', '…') from wp-config.php, no execution
// or:
// "mode": "include", "includePath": "../secrets.php",
// "constants": { "name": "DB_NAME", "user": "DB_USER", "password": "DB_PASSWORD", "host": "DB_HOST" }
},
"serverSecret": {
"mode": "file" // bridge_deploy drops the HMAC secret at <privateRoot>/wrb/secret
// or:
// "mode": "env", "name": "SOME_CONSTANT" // exposed by the dbCredentials "include" file; requires that mode
},
"tablePrefix": "wp_", // WordPress table prefix, used by the wp_option_* tools (default "wp_")
"allowWrite": false, // SQL writes; false by default
"allowRun": false // wp_run; false by default
}
}Credentials and secrets are never stored in envs.json. They're read from the process environment (e.g. via .envrc/direnv):
Variable | Purpose |
| Path to your |
| Local state + audit log directory (default |
| HMAC secret for environment |
| FTP/SFTP credentials for |
Naming rule (envVarName in src/config.ts): <PREFIX>_<ENV-UPPERCASED-WITH-DASHES-AS-UNDERSCORES>, e.g. environment site-staging → WRB_SECRET_SITE_STAGING, WRB_FTP_USER_SITE_STAGING.
See .env.example for a template to copy into .envrc or your shell environment.
Registering with an MCP client
For Claude Code (or any client reading .mcp.json):
{
"mcpServers": {
"wp-remote-bridge": {
"command": "node",
"args": ["/absolute/path/to/wp-remote-bridge-mcp/dist/index.js"],
"env": {
"WRB_ENVS": "/absolute/path/to/your/envs.json",
"WRB_SECRET_STAGING": "…",
"WRB_FTP_USER_STAGING": "…",
"WRB_FTP_PASSWORD_STAGING": "…"
}
}
}
}The Inspector doesn't inherit your shell environment either; pass variables explicitly:
npx @modelcontextprotocol/inspector --cli -e WRB_ENVS=/tmp/envs.json -e WRB_HOME=/tmp/wrb-home \
-e WRB_SECRET_LOCAL=<hex> node dist/index.js --method tools/listTypical workflow
bridge_deploy— dropsbridge.php,run.php,.htaccessat a random path underwebRoot, createswrb/(scripts/,logs/,nonces/) outside the web root, and bakes the environment'sallowWrite/allowRunpolicy into the bridge. Re-running it redeploys in place (needed after any policy change).bridge_enable— opens the kill switch for up to 8 hours (2 h default).Work:
db_list_tables/db_describe/db_query/db_explainto read;db_backup_tablethendb_execute(withconfirmand a freshbackup_ref) for a guarded write; or upload and run a script:wp_script_uploaduploads a PHP script towrb/scripts/and returns its SHA-256.wp_runexecutes it with WordPress loaded, verifying that SHA-256 first. Scripts that process many items must batch themselves: a positional<label> [offset] [limit]convention (shared hosts cut long-running HTTP requests), each call reporting how many items it processed and the next offset, and must be idempotent — rerunning a batch must not create duplicates. The bridge installs a minimalWP_CLIshim:log()/warning()/success()append to the captured output,error()appends an error line, setsstatus: 'error', and halts the script (likeWP_CLI::halt);WP_CLI::runcommand()is not supported and throws — no nested WP-CLI subcommands.wp_script_removedeletes the script once you're done with it.
bridge_disable— close the kill switch when done (it also expires on its own).
After upgrading wp-remote-bridge, run bridge_deploy again on each environment: the bridge file embeds its configuration (policy, table prefix) and only supports the actions it was rendered with.
Tools
Tool | Main inputs | Effect | Guard |
|
| Local deploy state, then a signed | — |
|
| Deploys/redeploys | — |
|
| Opens the kill switch. | — |
|
| Closes the kill switch. | — |
|
| Deletes the bridge files and confirms a | — |
|
| Tables: engine, estimated rows, size. | read-only |
|
| Columns, indexes, | read-only |
|
|
| I1, I2, I5 |
|
|
| read-only |
|
| Paginated SQL export to a timestamped local backup; returns a | read-only |
|
|
|
|
|
| Reads one row of | read-only |
|
| Writes one row of |
|
|
| Serialization-aware search/replace in one table with a single-column primary key (PHP-serialized and double-serialized values are rewritten with correct lengths; cells holding PHP objects or invalid serialized data are skipped and counted). Processes one batch per call in primary-key order: call again with | real run: |
|
| Refreshes the cached schema snapshot backing the | read-only |
|
| Uploads a script to | — |
|
| Runs an uploaded script with WordPress loaded; returns captured output. |
|
|
| Deletes a script from | — |
MCP resource: wrb://{env}/schema — a cached table/column schema snapshot for env, useful as background context for an agent (kept fresh by db_schema_refresh).
Testing
npm test # vitest
npx tsc --noEmitPure TypeScript tests (config parsing, HMAC signing, tool wiring against an in-memory MCP transport) run everywhere. The PHP bridge integration tests additionally spin up php -S against a rendered bridge and need a local MySQL/MariaDB instance with a wrb_test database and a wrb_test/wrb_test_pw user (see tests/helpers/bridge-harness.ts); they detect that setup automatically and are skipped if it's not present.
Limitations
Not a general file manager: only the bridge's own files and
wp_runscripts are written by FTP/SFTP. Managing the rest of the site's files is out of scope.No phpMyAdmin scraping — that approach is fragile and version-dependent.
db_executeonly targets a single table per statement (needed to determine which backup is required); multi-table writes must be split into several calls.db_executewithdry_runrelies on a transaction rollback, so the bridge refuses a dry run whose SQL mentions a non-InnoDB table or a view (conservative whole-word match on table names). Writes made by triggers on other tables are not detected.wp_runhas no nested WP-CLI subcommand support (WP_CLI::runcommand()throws) — it's a minimalwp eval-fileequivalent, not a full WP-CLI shim.Destructive DDL (
DROP DATABASE,GRANT,CREATE USER, …) is hard-blocked everywhere, with no override.The tool has no built-in notion of "production": closing an environment to writes is a configuration choice (
allowWrite/allowRun: false) you make per site, not something the tool infers.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage WordPress blogs and WooCommerce shops from Claude, ChatGPT, Cursor and other MCP apps.
Run all of WordPress from Claude, ChatGPT or Cursor: SEO, security, your WooCommerce shop and Elementor pages, on up to 10 websites in one connector. Start free. SEO: titles, descriptions and focus keywords for every page, post and product. Security & updates: core, plugin and theme updates with a live test, site health and HTTPS checks, users and roles. WooCommerce shop: products, prices, orders, coupons and customers. Elementor and many more page builders: texts, links and images. Blog posts, pages, media, comments and menus. 10 websites in one connector: all your sites in the same chat. Deploy (Agency): build a site locally and push it to your own server. Just say it: "Write SEO titles for every product." "Install all updates and check that the site still works." "Show today's orders and create a weekend coupon." You choose what your AI may change, with one switch. Install the free AlphaBridge MCP plugin from WordPress.org, connect and approve on your own site. No account, no key to copy.
Hosted WordPress MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Secure MCP Server for WordPress connects AI assistants and agents to WordPress with secure, controlled access. It lets AI interact with WordPress through MCP while helping organizations manage access, enforce policies, protect non-human identities (NHI), and require human approval for sensitive actions. Use it to securely connect tools such as ChatGPT, Claude, and Cursor with WordPress. Marketplace Link: https://wordpress.org/plugins/miniorange-secure-mcp-server/ Official website: https://plugins.miniorange.com/mcp-server-ai-policy-enforcement-wordpress ChatGpt Marketplace: https://chatgpt.com/plugins/plugin_asdk_app_6a312802286c8191bad0a7278a4e53ef Claude Marketplace: https://claude.ai/directory/miniorange-mcp-for-wordpress Cursor Marketplace: https://cursor.com/marketplace/miniorange
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to interact with WordPress sites over MCP, providing read-only tools for AEO/GEO readiness, AI visibility, traffic, request logs, bot identification, page checks, and schema/markdown previews, plus opt-in write tools to draft, edit, and publish posts with permission checks and auditing.2GPL 2.0
- FlicenseNot gradedqualityBmaintenanceEnables MCP-compatible AI agents to securely manage multiple self-hosted WordPress sites, including content editing, theme management, and maintenance operations with fine-grained permission controls.-
- AlicenseBqualityCmaintenanceEnables MCP-compatible clients to securely operate a WordPress site through a signed REST API, managing plugins, content, themes, menus, media, users, WooCommerce, Elementor, audits, and allowlisted WP-CLI commands.5865 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants and agents to communicate directly with a WordPress environment, executing PHP code and WP-CLI commands, inspecting and editing theme and plugin files in a sandbox, and managing posts, content, and Gutenberg blocks in real time.MIT