Skip to main content
Read this file first. It is the contract for connecting Muse to SandyWP and using the product MCP well. Canonical URL: https://docs.sandywp.com/connectors/muse.md

What SandyWP is

SandyWP creates disposable WordPress sandboxes for plugin and theme makers. Each sandbox is a real, isolated WordPress install with its own database, files, and public URL, and it is safe to throw away. A user asks Muse to list sandboxes, check status, open wp-admin, or create a test site. The connector does not take payment and never sees card details. Billing stays on sandywp.com through Polar.
This brief is for the product MCP at https://app.sandywp.com/mcp. https://docs.sandywp.com/mcp is Mintlify search only and cannot create sandboxes or call SandyWP tools.

Connection details

Fetch https://sandywp.com/agents before connecting. Do not guess the MCP URL from the marketing homepage.

Transport notes

  1. Send POST with Content-Type: application/json and Accept: application/json, text/event-stream. A JSON-only Accept also works.
  2. Unauthenticated GET /mcp returns HTTP 401 plus a JSON probe and WWW-Authenticate pointing at protected-resource metadata. That means the host is up and OAuth is required, so do not treat 401 as a dead server.
  3. Authenticated GET /mcp returns HTTP 200 with the same probe shape. DELETE /mcp returns 405 and sessions need no teardown.
  4. initialize, ping, and tools/list may run without a token so a client can import tool metadata. tools/call without a bearer access token returns HTTP 401.
  5. After initialize, send MCP-Protocol-Version on later requests. An unknown value returns HTTP 400.
  6. Tool results are content[0].text. Objects are pretty-printed JSON in that text field, so read it and do not expect a separate structuredContent object.
Copy-paste initialize (metadata only, no token):
If the SDK proposes 2025-03-26 or 2024-11-05, the server echoes that version. If it proposes anything else, the server answers 2025-06-18 and you continue with the version it returned. After OAuth, send Authorization: Bearer <access_token> on every tools/call.

OAuth with PKCE

SandyWP is both the authorization server and the resource server on https://app.sandywp.com. Public MCP clients register dynamically and complete an authorization-code grant with S256 PKCE. There is no client secret and no refresh-token grant. Discovery documents (no auth): High-level flow:
  1. An unauthenticated tools/call (or GET /mcp) returns HTTP 401 with WWW-Authenticate pointing at protected-resource metadata.
  2. The client reads authorization-server metadata, then registers itself at POST /api/connect/register with its own redirect URIs (RFC 7591). Use the URIs the client actually redirects to, and do not invent or substitute them.
  3. The browser opens /connect/authorize with client_id, an exact registered redirect_uri, S256 code_challenge, requested scopes, and resource=https://app.sandywp.com/mcp.
  4. If the account holder is not signed in, SandyWP sends them through login on https://app.sandywp.com, then back to consent.
  5. The account holder chooses Approve. The browser returns a short-lived, single-use authorization code to the registered redirect URI.
  6. The client POSTs the code, client_id, redirect_uri, and matching code_verifier to /api/connect/token with grant_type=authorization_code.
  7. The response is a bearer access_token (swp_…) and the granted scope. Send that token on later POSTs to /mcp.
Never collect a SandyWP password, a one-time code, or a card number in chat. Consent and login happen on SandyWP’s own pages. A personal API token is a fallback for clients that cannot complete OAuth. Muse should use the OAuth flow above, not ask the user to paste a key. See OAuth connector flow for the registration and token-exchange contracts.

Access and usage

A SandyWP account is required. Free, Plus, Pro, Max, and Workspace plans all work. Guest-only sandboxes are not an MCP login. Rate limits and sandbox quotas follow the connected account’s plan. Creating a sandbox counts against the active-sandbox limit. MCP calls are also rate-limited per token. See Plans and limits. Billing, plan changes, and payment methods stay on sandywp.com through Polar. Never collect card details in chat.

Meta E2E review

Use this account for Meta’s end-to-end connector review. Do not ask for or store a password. If login or consent fails, stop and report the tool or HTTP error. Do not guess a workaround.

Core recipes

Call tools only after OAuth. Use the id and publicUrl values the server returned. Never invent them.

A. List sites and check status

  1. sandywp_list_sites with {}.
  2. Each item includes id, slug, status, and publicUrl when the server has them.
  3. For one site, sandywp_site_status with { "siteId": "<id from the list>" }.
  4. site.status is one of creating, ready, expired, failed, pending_delete, deleted, cleanup_failed. Report the returned status. Do not infer ready from a URL you made up.
  1. Confirm the sandbox status is ready with sandywp_site_status.
  2. sandywp_magic_login with { "siteId": "<id>" }.
  3. Give the user the returned url and expiresAt exactly. The link is a reusable bearer credential until it expires, and anyone who has it can enter wp-admin.
  4. Never construct a login URL. If the tool errors, say so.

C. Create a sandbox

  1. sandywp_create_site with optional { "siteName": "muse-review-test" }. All fields are optional.
  2. By default the tool waits up to about 60 seconds. If site.status is still creating, poll sandywp_site_status with that siteId until ready or failed.
  3. Pass "background": true only when you will poll. Do not tell the user the site is live while status is creating.
  4. Optional create fields include phpVersion (8.5, 8.4, 8.3, 8.2, 8.1, 7.4; default 8.3) and plugins (wordpress.org slugs, max 10).

D. Deploy a plugin (optional)

  1. Confirm the sandbox is ready.
  2. sandywp_deploy_plugin with { "siteId": "<id>", "pluginSlug": "query-monitor" }. Provide exactly one of pluginSlug, zipUrl, or artifactId.
  3. The tool waits up to about 60 seconds. If it returns an in-flight job, poll sandywp_get_job with { "siteId": "<id>", "jobId": "<jobId>" }.
  4. This remote server cannot read local files. For a local ZIP, the user runs npx @sandywp/cli deploy <site> <plugin.zip>.

E. Preview a GitHub pull request (optional)

  1. sandywp_preview_github_pr with { "repository": "owner/repo", "destination": "plugin", "folderName": "my-plugin" }. pullNumber is optional when the repository URL already includes /pull/N.
  2. This uses the account GitHub App. It does not mint per-sandbox SSH deploy keys.
  3. If GitHub is not connected, the tool returns needs_github_connect and a dashboard URL. Send the human to that URL. Do not invent a deploy key.
  4. Poll sandywp_site_status until ready, then sandywp_magic_login if the user needs wp-admin.
sandywp_whoami confirms which account is connected. sandywp_usage reports plan and active-sandbox count.

Rules

  1. Quote only what the server returned. Never invent a site URL, slug, status, job result, or login link; if a tool did not return it, you do not know it.
  2. Never ask for card numbers, security codes, passwords, or one-time codes in chat. Payment and plan changes happen on sandywp.com through Polar, with the user’s approval.
  3. Confirm with the user before calling a destructive tool. That includes sandywp_delete_site, sandywp_reset_site, sandywp_delete_path, sandywp_rotate_demo_launch_webhook_secret, and sandywp_delete_demo_launch_webhook.
  4. sandywp_magic_login mints a sensitive, reusable wp-admin URL. Show it to the user with its expiry. Do not store it in a public place.
  5. Product MCP is https://app.sandywp.com/mcp. Docs MCP is https://docs.sandywp.com/mcp. Do not call product tools against the docs MCP.
  6. If a tool returns isError: true or HTTP 401, say so. A 401 means complete OAuth. Do not fill the gap with invented sandboxes.
  7. Creates and deploys are jobs. Poll sandywp_site_status or sandywp_get_job until a terminal state before claiming success.