The MCP tools, one by one.

Every tool the app gives a connected AI: what each takes, what it answers, in what order. Call site_status first; path may be any folder in the project.

A connected AI — Claude Code, Codex, Cursor or any MCP client — sees twelve tools from the app. Two are about publishing; the other ten are about a project's local site, and they go through the same code the app's own buttons use, so the app window and the AI never disagree about what is running.

Two rules hold for every site tool:

  • path may be any folder inside the project — its root, a nested folder, a repository cloned in it, a folder it reaches through a symlink. The app walks up until it finds the project. A path outside every project answers {ok: false, error: "No Vibecode & Deploy project contains …"}.
  • Call site_status first. It says what the project is, whether the site runs, the address and the database details. The server's own instructions say the same, so a tool that reads only those knows.

Every answer is JSON: {ok: true, …} or {ok: false, error} with an error a person can read. Nothing here deletes a project, its database or its images.

Publishing

publish_project

{path} — publishes the project the folder belongs to, through the app, with the hosting credential still in the app. Answers {ok: true, url, customDomain?, changes} or, the first time, firstPublish: true. A leaked key stops it with {ok: false, blocked: [{kind, file, line}]}; a host that still needs connecting answers needsToken: true.

list_projects

No arguments. The library: name, path, live URL, custom domain and the time of the last publish for each project.

The local site

site_status

{path}. Everything about the site in one answer:

  • project — {id, name, path}
  • kind — wordpress, php, static or node-web
  • running, url — the address the app shows (the local name with https when it has one), localUrl — the same site by 127.0.0.1 and port, for a tool that does not trust the local certificate: send the name as Host
  • phpMyAdminUrl, php — the version
  • containers (wp, db, pma), network, dbVolume
  • db — {host, name, user, password, server, tablePrefix}, the prefix read from the generated runtime config, which is what the container actually uses
  • wpConfigOverrides — the constants in force, see site_config_get

site_start, site_stop, site_restart

{path}. The same paths as the app's Start and Stop buttons, so the registry, the generated config and the window all follow. site_start blocks until the site is ready — the database takes connections and the address answers — and leaves a running site alone. site_stop on a stopped site is fine. All three answer the same payload as site_status; the port may have changed. Docker not running is an error, never a silent launch.

wp

{path, args, timeoutSec?}. WP-CLI against the running site: args is everything after wp, flags passed through untouched — --skip-plugins --skip-themes when a plugin fatals on load, --format=json for output that parses, which it does because stdout and stderr come back apart. Answers {ok, exitCode, stdout, stderr}; a non-zero exit is ok: false with the first stderr line as error and the streams still included. Non-interactive. The default deadline is ten minutes.

site_logs

{path, source?, since?, lines?, grep?}. source is container (the site container's log: Apache and PHP errors, with the file, line and stack trace of a fatal), debug (wp-content/debug.log, when there is one) or all, the default. since is a duration like 10m or an ISO timestamp; lines defaults to the last 200; grep keeps lines containing the text. Answers {ok, container?: string[], debug?: string[]} with a note when a source is missing — and the note says to set WP_DEBUG and WP_DEBUG_LOG with site_config_set when there is no debug log yet.

site_config_get, site_config_set

{path} and {path, constants, restart?}. Constants the app writes into the generated runtime wp-config.php on every start — WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY, SCRIPT_DEBUG, SAVEQUERIES, or the site's own keys. constants maps a name to a string, number or boolean; null removes one. Names must match ^[A-Z_][A-Z0-9_]*$; values are written as PHP with proper escaping. Kept on the project, so they survive restarts, and never written into the folder. With restart: true the site restarts now; otherwise the answer says whether a restart is still needed.

db_query

{path, sql, readonly?}. SQL straight at the site's database, past WordPress — for when WordPress will not load even with plugins skipped. Read-only by default: only SELECT, SHOW, DESCRIBE and EXPLAIN, run inside a read-only transaction besides; readonly: false allows a write. Answers {ok, columns, rows, truncated} — rows as objects, capped at 1000; every value a string as the database client printed it, numbers included, except NULL, which is null.

site_login

{path, user?}. Auth cookies that make requests to the running WordPress site an administrator's for one hour, made by WordPress itself through WP-CLI with plugins and themes skipped; nothing is written anywhere. The default user is the administrator with the lowest user ID, which may be an import or service account — the answer names it. Answers {ok, user, expires, cookies: [{name, value}], cookieHeader, curl}, the last a ready curl -b … /wp-admin/.

What a session looks like

  1. site_status with the folder the agent is in. Running? If not, site_start.
  2. site_config_set {WP_DEBUG: true, WP_DEBUG_LOG: true} with restart: true, reproduce the problem in the browser.
  3. site_logs with grep: "Fatal" — the file and the line.
  4. Fix the plugin; site_restart; site_login and fetch /wp-admin/ to check.
  5. site_config_set with the two constants set to null, restart: true.

For another MCP client

The server is one file the app ships; the AI page shows its path under the rows. Register it as node <path> in your client. There is no token to configure: the server reads the app's own key from your home folder, and the app must be running.