@cloudgrid-io/mcp
Official# @cloudgrid-io/mcp
An MCP server for CloudGrid. It exposes the CloudGrid actions as MCP tools.
It ships in two editions from one codebase:
- **Local (stdio)** — runs on your machine, full toolset including the CLI-wrapping
tools. This README covers it. For Claude Code, Cursor, Claude Desktop.
- **Web (hosted HTTP)** — a lighter toolset (17 direct-API tools, no CLI
wrappers) for web clients like claude.ai. See [REMOTE.md](REMOTE.md).
The local edition wraps the `grid` CLI for authenticated operations (the CLI
handles auth, grid context, and error formatting) and calls the API directly for the
plug, login, and visibility tools.
## CLI compatibility
The MCP uses an installed CLI only when it meets the platform floor
(`MIN_CLI_VERSION`, currently 0.15.26 — kept in lockstep with the API's live
`cli_compat` floor, which rejects older CLIs with HTTP 426). Below the floor it
skips the local/global CLI and lazily fetches `@cloudgrid-io/cli@latest` via
npx instead. A CI drift guard (`npm run test:drift-guard`) asserts every
wrapped verb exists in the CLI help.
### `GRID_AUTH_STALE` is out of scope
Strict-OIDC (org-owned) grids can return `GRID_AUTH_STALE` when the caller's
session predates an SSO policy change. The MCP does not target such grids and
does not handle this code: if a `GRID_AUTH_STALE` response is ever seen, point
the user at Console SSO to re-authenticate — the MCP will not silently retry.
## Prerequisite
Install and log in to the CLI:
```
npm install -g @cloudgrid-io/cli
grid login
```
The server reads no credentials directly. It runs `grid`, which uses its own
stored credentials at `~/.cloudgrid/credentials`.
## Run
```
npx -y @cloudgrid-io/mcp
```
Or from a clone:
```
cd mcp
npm install
npm start
```
It speaks MCP over stdio. Point any MCP client at the `cloudgrid-mcp` command.
### One CloudGrid server per client
Run EITHER this local (stdio) server — standalone or via the Claude Code
plugin, which bundles it — OR the hosted connector (`mcp.cloudgrid.io`) in a
given client. Never both: each `grid_*`
tool appears twice, the two servers hold separate sign-in state (the local
edition shares the CLI's `~/.cloudgrid/credentials`; the connector holds an
OAuth session), and they can disagree about whether you are signed in — the
model may then act on whichever answered last. How to tell: duplicate
`grid_*` entries in the client's tool list, or two CloudGrid servers in its
MCP/connector settings. Which to keep: the local edition wherever you build
runtime apps from folders (it has `path` + the CLI); the connector in
chat-only clients where you publish single pages.
### QA session log (optional)
Set `CLOUDGRID_QA_SLACK_WEBHOOK` to an **internal / private** Slack incoming-webhook
URL to receive a per-session QA log (`log-<Client>-<transport>-mcp.txt`) when a
local (stdio) session goes live, fails, or is abandoned. The log carries the
user's first message (when the host forwards it) and identity details, so the
target channel MUST be internal. The hosted edition never posts a session log
at all (its servers carry no webhook); a hosted `grid_note` is filed with the
CloudGrid API instead — see "Privacy Policy" below. Unset → the feature is fully dark (nothing captured, nothing
posted). `CLOUDGRID_QA_IDLE_MS` overrides the abandoned-idle window (default
900000 = 15 min).
<!-- gen:tools -->
## Tools
38 tools registered (15 shared across both editions, 23 local-only). No deprecated aliases. MIN_CLI_VERSION: 0.15.26.
### Direct-API tools (both editions)
| Tool | Wraps | Summary |
|---|---|---|
| `grid_pickup` | API | Pick up an app: make your OWN COPY of any app you can see (like a git fork) into a grid you can build in. |
| `grid_pull` | API | Pull an app to continue/edit it IN PLACE — like `git clone` of the SAME entity: your next grid_plug (with its target_... |
| `grid_collab` | API | Collab: GET PUSH ACCESS to the SAME live entity that someone else owns — you become a collaborator on THAT entity, no... |
| `grid_create_grid` | API | Create a new grid (workspace) for the signed-in user — they become its admin. |
| `grid_note` | API | Leave a one-paragraph summary of what you built this session and why, for the local edition's optional QA session log... |
| `grid_plug` | API | Plug an app, website, game, or HTML page into CloudGrid at a live public URL. |
| `grid_get_app_source` | API | Retrieve the CURRENT deployed HTML of an inspiration/drop inline as text, so you can edit it and re-plug the SAME URL... |
| `grid_visibility` | API | Change who can see a CloudGrid inspiration OR runtime app/agent. |
| `grid_check_deploy` | API | Check whether an async runtime-app build (a deploy, which here means a plug) has finished and the app is live. |
| `grid_list_grids` | API | List the signed-in user's grids, each with slug, name, role, and provisioning status. |
| `grid_start` | API | A short orientation for building with CloudGrid: the account this session is signed in as and its active grid, how to... |
| `grid_get_template` | corpus | Load a specific CloudGrid workflow, template, example, rule, troubleshooting note, or reference doc by name — determi... |
| `grid_report` | API | Report a genuine CloudGrid failure to the CloudGrid team — ONLY with the user's explicit consent. |
| `grid_hello` | API | Create a minimal hello page and plug it into the user's grid, returning the live URL. |
| `grid_delete` | `grid delete entity` | Archive a CloudGrid inspiration. |
The direct-API tools call the platform without the CLI, so they also work in
the web edition. `grid_login` writes the same `~/.cloudgrid/credentials` the
CLI uses, so the two share one identity.
### CLI-wrapping tools (local edition only)
| Tool | Wraps | Summary |
|---|---|---|
| `grid_login` | API | Start a CLI-free CloudGrid sign-in. |
| `grid_login_status` | API | Finish a sign-in started by grid_login. |
| `grid_create_project` | `grid new` | Scaffold a new CloudGrid app or agent folder (cloudgrid.yaml + a web service), optionally pre-declaring resources. |
| `grid_view_logs` | `grid logs` | Tail recent logs for an entity. |
| `grid_share` | `grid visibility set` | Set an entity's visibility and print its URL. |
| `grid_feedback` | `grid feedback list` | List recent feedback events for the active grid. |
| `grid_whoami` | `grid whoami` | Show the signed-in user and active grid. |
| `grid_switch_grid` | `grid use` | Switch the active grid. |
| `grid_logout` | `grid logout` | Sign out and clear local credentials. |
| `grid_status` | `grid status` | Grid dashboard, entity detail, or deploy snapshot. |
| `grid_info` | `grid info` | Show metadata for a CloudGrid entity. |
| `grid_get` | `grid get <grids|entities|spaces> --json` | List CloudGrid grids, entities, or spaces. |
| `grid_describe_grid` | `grid describe grid <slug> --json` | Show a grid's detail: role, members, spaces, tier, wildcard-TLS state. |
| `grid_edit_existing_app` | `grid pull` | Continue/edit an EXISTING entity locally: download its source + cloudgrid.yaml and link the folder so your next `grid... |
| `grid_rename` | `grid rename` | Rename a CloudGrid entity's display name (slug stays the same). |
| `grid_take_offline` | `grid unplug` | Take an entity off the grid. |
| `grid_rollback_deploy` | `grid rollback` | Rollback an entity to a previous version. |
| `grid_list_versions` | `grid versions` | List published versions for an entity. |
| `grid_set_env` | `grid env` | Manage environment variables for an entity. |
| `grid_set_secret` | `grid secrets` | Set or list secret names for an entity. |
| `grid_scaffold` | `grid scaffold` | Scaffold service folders declared in cloudgrid.yaml (idempotent). |
| `grid_diagnose` | `grid doctor` | Run CloudGrid diagnostics on the local environment. |
| `grid_get_url` | `grid open --print` | Return the public URL for an entity. |
<!-- /gen:tools -->
`grid_share` and `grid_visibility` overlap on purpose: `grid_share`
wraps the CLI and defaults to `link`; `grid_visibility` is direct API, takes an
explicit scope, and defaults its target to the session's last drop — it is the one
the web edition gets.
All tools carry MCP annotations (`readOnlyHint`, `destructiveHint`,
`openWorldHint`) for clients that support them.
## Test
A smoke test spawns the server with a real MCP client, lists the tools, and calls
the read-only `grid_feedback` tool end to end:
```
cd mcp
npm install
npm run smoke
```
It needs a logged-in CLI on `$PATH`.
### Live smoke scripts
Three more scripts run against the live API on demand. They are not part of CI.
A new plug needs sign-in, so none of them creates an anonymous drop. Each one
skips a step, and says why, when the variables that step needs are missing.
Tokens come only from the environment and are never printed.
| Script | Without secrets | With secrets |
|---|---|---|
| `npm run smoke:drop` | A signed-out create with `anon: true` returns `needs_auth` and publishes nothing. | `CLOUDGRID_TOKEN` (and `CLOUDGRID_SMOKE_GRID` if the account has several grids): a signed-in create publishes an owned page and its URL serves. |
| `npm run smoke:redrop` | Same signed-out check, through the web edition. | `CLOUDGRID_SMOKE_REDROP_ENTITY_ID` + `CLOUDGRID_SMOKE_REDROP_OWNER_TOKEN` (or `CLOUDGRID_SMOKE_REDROP_OWNER_TOKEN_FILE`) for an existing anonymous drop: the edit updates it in place, with no sign-in. The owner token is re-minted on every edit; a token file is rewritten with the fresh one. |
| `npm run smoke:claim` | Skips. | `CLOUDGRID_TOKEN`: a signed-in plug. Add `CLOUDGRID_SMOKE_CLAIM_ENTITY_ID` + `CLOUDGRID_SMOKE_CLAIM_TOKEN` for an existing anonymous drop: `grid_pull` claims it into the account. A drop can be claimed once. |
The signed-out check is refused inside the server before any publish request is
sent, so it is safe to run at any time.
## Design
- Shells out with `execFile` and an argument array, so there is no shell and no
injection surface.
- `grid_view_logs` never uses `--follow`; a streaming call would never return.
- Stateless. Each call is one CLI invocation.
## Privacy Policy
Full policy: <https://cloudgrid.io/privacy>
### What this MCP server does with your data
**Authentication.** `grid_login` opens a browser-based Google sign-in flow via
CloudGrid's API (`/auth/login`). The returned JWT is stored at
`~/.cloudgrid/credentials` with `0600` permissions — the CLI and the MCP share
one credential file by design (`auth.js:7-8`). The web edition has no sign-in
tool: the AI tool's connector signs in with OAuth, and this server issues its own
short-lived access token, with the CloudGrid session sealed inside it, so the
session is never handed to the AI tool.
**Deploying.** `grid_plug` uploads your app files (HTML, source, assets) to
`https://api.cloudgrid.io/api/v2/plug`. Before upload, inline sources are
scanned for known API-key patterns (OpenRouter, Anthropic, OpenAI, Google,
GitHub, AWS, Slack, Stripe); a match blocks the deploy. This is not a guarantee
— a determined model can obfuscate a key past the scan — but it stops the
good-faith "embed it so it works" path. Auth headers (Bearer JWT, grid slug) are
attached to identify the caller.
**Error reporting.** `grid_report` sends an error report to
`https://api.cloudgrid.io/api/v2/errors`. The tool description instructs the
model to call it only with the user's explicit consent, and setting
`CLOUDGRID_TELEMETRY=off` disables it unconditionally — but there is no
programmatic consent check in the report handler itself. The payload includes an
error summary, diagnostic metadata (platform, Node version, transport), a flag
recording whether the user agreed to share the conversation (`deploy.js:544`),
and a `context` object the model supplies — which by its declared shape
(`register.js:964-975`) can carry the failing inputs (for example the HTML or
args) and the original request. The transcript itself is not sent by this tool.
The report scrub is **two-layer**: values AND the `message` field are scanned
for known secret shapes (API key prefixes such as `sk-ant-`, `sk-proj-`,
`AKIA…`; JWTs; PEM private-key blocks; Bearer tokens; basic-auth URL
credentials) and matches are replaced with `[REDACTED]`. Arbitrary or
unknown-shape secrets that do not match a known pattern still transit
unredacted. The client-side scrub (`scrubReportContext` + `scrubSecretValues`
in deploy.js) runs before the POST; the server endpoint applies a server-side
key-name filter on top. To keep a secret out of a report, do not place it in
the `message` or `context` you send, or set `CLOUDGRID_TELEMETRY=off` to
disable reporting entirely.
**Version check.** At startup the local edition makes one request to
`https://registry.npmjs.org/@cloudgrid-io/mcp/latest` to detect a stale `.mcpb`
install, which never auto-updates. It sends no account data and no user content.
The hosted edition does not do this.
**QA session log.** When `CLOUDGRID_QA_SLACK_WEBHOOK` is set (unset by default,
fully dark otherwise), the local (stdio) server posts a per-session QA log to an
internal Slack channel on deploy, error, idle timeout, or process shutdown. The
log includes: session metadata (user ID, email, grid slug, client name), the
user's first message (scrubbed, capped at 2 000 chars), a per-tool-call trail
with allowlisted arg keys only, and an optional narrative set via `grid_note` or
`grid_plug`'s `session_note` (capped at 4 000 chars). The first message is
captured from the `CLOUDGRID_USER_REQUEST` environment variable (forwarded by
Claude Code) or `grid_plug`'s `user_request` argument (local edition only).
**The hosted edition sends nothing on its own**: its `grid_plug` has no
`user_request` or `session_note`, and there is no post on a plug, on idle, or on
close. The one hosted post is `grid_note`, which the user asks for: it POSTs the
note to the CloudGrid API (`/api/v2/feedback`) under the user's own sign-in,
with the grid slug and the client name — no email address, no tool-call trail,
nothing else from the conversation; signed out, it returns `needs_auth` and
sends nothing. `grid_report` (both editions) sends a failure report to the
CloudGrid API only after the user agrees: the message and context the model
passes plus the client name and platform, and the conversation only when they
explicitly agree to that. Free-text values
(the first message, the narrative, tool arguments, and error messages) pass
through a scrubber that redacts JWTs, PEM keys, Bearer tokens, and known
provider API-key formats. Identity fields (`_keyResult` values and the header
block) and structured result data bypass the text scrubber.
**No other data collection by this server.** The server does not track usage
analytics or transmit data beyond the flows listed above and the authenticated
CloudGrid API calls the individual tools make (e.g. `/grids`,
`/pickup`, `/deploys`, `/inspirations/{id}/source`). CLI-wrapping tools shell
out to the CloudGrid `grid` CLI, which shares the same
`~/.cloudgrid/credentials` file and does not send additional telemetry through
this server. They prefer the CLI bundled in the `.mcpb` or a compatible `grid`
already on your `PATH`; if neither is present or new enough, they fall back to
`npx -y @cloudgrid-io/cli@latest` (`cli.js:411-424`), which **downloads and runs
the CLI from the npm registry** (`registry.npmjs.org`) on first use — software
installation, and the second flow that can reach npm (the startup version check
above is the first). Platform-level data handling is covered by the linked
privacy policy.
TDQS
Scored across 38 tools
Several tools overlap heavily: grid_pull and grid_edit_existing_app both wrap `grid pull`; grid_share and grid_visibility both set visibility; and grid_get, grid_info, grid_status, grid_describe_grid, grid_list_grids, and grid_whoami all read entity/grid metadata with fuzzy boundaries. grid_hello is a special-case grid_plug, and grid_note vs grid_report both report information, so the long descriptions are compensating for genuinely confusable tools.
Nearly everything uses a predictable grid_ snake_case prefix with verb_noun or verb form (grid_create_grid, grid_list_versions, grid_check_deploy, grid_rollback_deploy). A few names are vague verbs (grid_get, grid_info, grid_status) and some are noun-style (grid_whoami, grid_pickup, grid_collab), but overall the pattern is readable and consistent.
38 tools is well above the comfortable range and includes redundant pairs (grid_pull/grid_edit_existing_app, grid_share/grid_visibility) that inflate the surface. While the platform's domain is genuinely broad (deploy, auth, env, secrets, versions, grids, templates), the count is heavy enough to burden selection.
Coverage spans auth, grid management, scaffolding, plugging/deploying, versioning/rollback, env and secrets, visibility, logs, diagnostics, and feedback, leaving few obvious dead ends. Minor gaps exist around entity deletion (only archive) and env variable removal, but agents can generally complete the lifecycle.