# SandyWP for AI agents

This page is the connect recipe for Grok Bot, Cursor, Hermes Agent, OpenClaw, and other MCP clients.
Read it before adding SandyWP or calling tools.

## MCP URLs

| URL | What it is | Use it to |
| --- | --- | --- |
| https://app.sandywp.com/mcp | **Product MCP** (Cursor, Claude; includes `sandywp_exec`) | Create sandboxes, magic-login, deploy, install Library plugins and themes, preview private GitHub PRs, run sandbox shell commands |
| https://app.sandywp.com/mcp/openai | **OpenAI product MCP** (ChatGPT / Codex; same tools minus `sandywp_exec`) | The same work without a generic shell tool. OpenAI review rejects freeform command tools. |
| https://docs.sandywp.com/mcp | **Docs MCP only** (Mintlify search) | Search documentation. It is not the SandyWP API. |

Do not connect to docs.sandywp.com/mcp when you need to spin a WordPress sandbox.

## Connect

### OAuth (preferred)

Point Cursor and Claude at https://app.sandywp.com/mcp. Point ChatGPT and Codex at https://app.sandywp.com/mcp/openai.
SandyWP supports OAuth 2.1 with PKCE and Dynamic Client Registration on both URLs. Allowed
redirect URIs include:

- `https://www.cursor.com/agents/mcp/oauth/callback` (Cursor web / cloud agents)
- `http://localhost:8787/callback` (Cursor desktop loopback)
- `cursor://anysphere.cursor-mcp/oauth/callback` (Cursor native scheme)
- `http://127.0.0.1:<port>/callback` (Hermes, OpenClaw, and other local loopback clients)

The user approves access in the browser. You never need to paste a token.

### Bearer personal API token (fallback)

If the client cannot complete OAuth, create a key at https://app.sandywp.com/account?tab=keys
and send `Authorization: Bearer swp_…`. A valid Bearer session is fully authenticated;
do not start a second OAuth dance just because discovery documents also advertise OAuth.

CLI that already works: `npx @sandywp/cli` (`npx @sandywp/cli auth login` then `npx @sandywp/cli add`).

## GitHub App — not deploy keys

Private-repo PR previews use the SandyWP GitHub App (installation tokens).
Do **not** mint per-sandbox SSH deploy keys. `sandywp_connect_repository` is the old
per-site Git Deployment path; users with write/maintain (not Admin) get 404 on deploy keys.

If GitHub is not connected for this account, send the human to:

https://app.sandywp.com/account?tab=github

That dashboard **Connect GitHub** button installs the org GitHub App. Never paste an SSH public key.

## sandywp_preview_github_pr

Use this tool to spin a WordPress preview of a private (or public) GitHub pull request.

Arguments:

- `repository` — `owner/repo` or a GitHub URL (`https://github.com/owner/repo` or `…/pull/12`)
- `pullNumber` — pull request number (optional when the URL already includes `/pull/N`)
- `destination` — `plugin` or `theme`
- `folderName` — WordPress slug the code is unpacked into, e.g. `publishpress-cart`
  (this is `wp-content/plugins/{folderName}` or `wp-content/themes/{folderName}`).
  GitHub zipballs unpack to `{repo}-{branch}/`; always use `folderName`, never that zipball root.

The tool creates or reuses a sandbox the same way `@sandywp-bot deploy` does and fetches the
PR head SHA with a GitHub App installation token. It returns quickly with status
`creating`/`deploying` and a site id — poll `sandywp_site_status` until `ready`, then
`sandywp_magic_login`. If the preview is already ready it returns `site`, `publicUrl`, and
`magicLogin: { url, expiresAt }` (`null` when the connection lacks `sites:login`).

`sandywp_site_status` returns `{ site, wordpress, events, jobs }`. For a ready sandbox,
`wordpress` is `{ version, multisite, plugins: [{ name, title, version, status }] }` read live
from the install, so you do not need a shell command for the WordPress version or plugin list.
The site list never carries wp-admin credentials, and status carries them only when you pass
`includeCredentials: true`; prefer `sandywp_magic_login`, which returns `{ url, expiresAt }`.

## Library and license keys

The user's workspace can keep premium and private plugins and themes in its Library. Each entry's
`kind` says which. To install one by name, call `sandywp_list_plugin_library`, then
`sandywp_deploy_plugin` with `libraryPlugin` set to the slug; a theme is installed and
activated as the site's theme. GitHub-backed entries install their latest release.
`sandywp_add_plugin_library_github` adds a repository; ZIP uploads go through the dashboard or
`npx @sandywp/cli library add <file.zip>`.

A listed entry with `hasLicenseKey: true` has a license key stored. After installing, fetch it
with `sandywp_get_plugin_license` and activate it the way that plugin expects (its own WP-CLI
command over SSH, or its settings screen). That tool needs the separate
`licenses:read` scope, and every call is audited. Treat the key as a secret: do not repeat it
to the user unless asked, and do not write it into files.

## Push local code changes into a sandbox

The MCP server is remote and cannot read local folders, so send the change itself:

- **A code change:** pass the plain output of `git diff` to `sandywp_apply_patch` with
  `directory` set to the plugin or theme folder (for example `wp-content/plugins/<slug>`).
  Use `dryRun: true` to check it applies first. Never base64-encode a diff or file into a
  shell command: agent safety classifiers block encoded payloads.
- **A whole plugin folder:** zip it and run `npx @sandywp/cli deploy <site> <plugin.zip>`
  (replaces the installed copy).
- **Shell access, no MCP:** `git diff | npx @sandywp/cli ssh <site> --cmd "patch -p1 -d /var/www/html/wp-content/plugins/<slug>"`.

## Share a tested fix in a pull request (PR snapshots)

After you reproduce and fix a bug on a sandbox, save it as a PR snapshot so reviewers can open
their own copy of the site you tested:

1. `sandywp_create_pr_snapshot` with `siteId`, `repo` (`owner/repo` the pull request
   targets), `branch` (the pull request head branch) and an optional `landingPath` such as
   `/wp-admin/plugins.php`. It returns `{ id, status, launchUrl }` with status `building`.
2. Poll `sandywp_list_pr_snapshots` with the same `repo` and `branch` until status is
   `ready`. Do not delete the source sandbox before then: deleting it mid-snapshot fails the
   snapshot.
3. Put `launchUrl` in the pull request body. Each click starts a fresh sandbox that ends 60
   minutes after the reviewer goes idle and never lives longer than 24 hours.

Calling `sandywp_create_pr_snapshot` again for the same repo and branch replaces the snapshot and
keeps the same `launchUrl`. SandyWP deletes the snapshot when the pull request closes (the
SandyWP GitHub App must be installed on the repository) or after 60 days;
`sandywp_delete_pr_snapshots` deletes it sooner. These tools need only the `sites:read` and
`sites:write` scopes, and a paid plan. They are on https://app.sandywp.com/mcp, not https://app.sandywp.com/mcp/openai.

## Cursor marketplace plugin

Install the SandyWP Cursor plugin (marketplace listing, or the `cursor-plugin/` source in
https://github.com/rizaardiyanto1412/sandywp). It points at https://app.sandywp.com/mcp (with
`sandywp_exec`) so Grok Bot / Cursor can InstallPlugin and Connect in one tap. Read this page first.

## Discovery files

- https://sandywp.com/agents (this page, `text/markdown`)
- https://sandywp.com/llms.txt
- https://app.sandywp.com/.well-known/mcp.json
- https://app.sandywp.com/.well-known/oauth-protected-resource
