AI agents and MCP
Connect Claude Code, Cursor, Codex or a chat client to Camplax over MCP — add the URL, approve in your browser, no key to paste.
Camplax is built for agents, not just people. An agent that can create a project in one call does not need a Vercel account and a Supabase account — it needs one endpoint: the Camplax MCP server.
https://camplax.dev/mcp (also mounted at /v1/mcp)
It speaks stateless streamable HTTP — one JSON-RPC 2.0 message per POST — and serves MCP 2026-07-28 (per-request metadata and server/discover) alongside the initialize handshake of 2025-11-25, 2025-06-18 and 2025-03-26, so older and newer clients connect to the same URL.
The quickest way in is the Connect page in the console: pick your AI app and it shows that app's exact steps, with copy buttons and a one-click install for Cursor and VS Code. Or wire most coding agents in one line:
npx add-mcp https://camplax.dev/mcp -g
add-mcp writes the config entry for Claude Code, Cursor, Codex, VS Code and the rest; -g adds it for every folder, not just the one you are in. With the Camplax CLI, camplax connect does the same for the agents it finds and installs the Camplax skill beside it. There is no key to copy: the first time the agent connects, your browser opens on Camplax, you press Approve, and you are done.
Connect from your agent
Add the URL, then approve in the browser. Nothing to paste.
# Claude Code — then run /mcp inside Claude Code and pick camplax to sign in
claude mcp add --scope user --transport http camplax https://camplax.dev/mcp
// Cursor — .cursor/mcp.json, then press Connect next to camplax in Settings → MCP
{
"mcpServers": {
"camplax": { "url": "https://camplax.dev/mcp" }
}
}
# Codex — opens the browser sign-in; if it does not, run: codex mcp login camplax
codex mcp add camplax --url https://camplax.dev/mcp
Chat clients — Claude Desktop, claude.ai, ChatGPT — work the same way: add https://camplax.dev/mcp as a custom connector and approve.
Without --scope user, Claude Code saves the server for the folder you ran the command in only, and it is missing everywhere else.
The approve page names the app asking and where it sends you back, and asks you to confirm it is you, because the connection acts as you for months. It also asks what the app may reach: all your projects (the default) or one of them, and whether it is read-only. That choice holds on every call the app makes for as long as the connection lasts, and refreshing the connection keeps it — the URL can narrow it further but never widen it. Afterwards the agent holds its own cxm_… token and renews it by itself. Account → Sessions → Connected apps shows each app with what it can reach; press Disconnect to cut one off, then connect it again to choose differently.
Codex auto-discovers .agents/skills/camplax/SKILL.md; the same skill is published by the MCP endpoint as a resource.
Without a browser: CI and scripts
A machine nobody is sitting at — CI, a cron box, a container — cannot press Approve, so it sends a token instead. This is optional; nothing on Camplax needs it.
| Credential | Where it comes from | Reaches |
|---|---|---|
cx_cli_… | camplax login (opens the browser once), or Account → Access tokens → New token | everything you can reach |
cx_live_… | a project key | one project, limited to its scopes |
Set it as CAMPLAX_TOKEN and send it as a header:
claude mcp add --scope user --transport http camplax https://camplax.dev/mcp \
--header "Authorization: Bearer $CAMPLAX_TOKEN"
A project key can deploy and read its own project — deploy needs the deploy scope — but it cannot create_project, import_project or probe_project. Those need a user credential.
How the browser sign-in works
Camplax runs an OAuth 2.1 server with PKCE. Clients discover it from the MCP endpoint's 401, which points at the protected-resource document.
Discovery documents live at:
https://camplax.dev/.well-known/oauth-authorization-server
https://camplax.dev/.well-known/oauth-authorization-server/mcp
https://camplax.dev/.well-known/oauth-protected-resource
https://camplax.dev/.well-known/oauth-protected-resource/mcp
https://camplax.dev/.well-known/oauth-protected-resource/v1/mcp
https://camplax.dev/.well-known/mcp.json
The flow, if your client does not automate it:
- Register (RFC 7591):
POST /oauth/registerwithredirect_uris, optionalclient_name,grant_types,scope. Onlyauthorization_code/refresh_token, response typecode, auth methodnone, scopemcpare accepted;redirect_urismust behttps://, a loopbackhttp://localhost/127.0.0.1/[::1](any port is accepted at authorize time, per RFC 8252 §7.3, so a portless loopback registration works), or a native app scheme — reverse-domain (com.example.app:/callback) or a known coding agent's own (cursor://,vscode://,windsurf://). Response201carriesclient_id— there is no client secret. Registration is also optional in the other direction: aclient_idmay simply be anhttps://URL hosting a Client ID Metadata Document — the server fetches it (one bounded request, cached briefly), requires the document'sclient_idto equal the URL, and treats itsredirect_urisas the allowlist. Discovery advertisesclient_id_metadata_document_supported; a pre-registered client always wins over a URL-shaped one. - Authorize:
GET /oauth/authorize?client_id=…&redirect_uri=…&response_type=code&code_challenge=…&code_challenge_method=S256&scope=mcp— S256 is the only PKCE method. Signed-out users are bounced through console sign-in first; then a consent page names the client and the redirect origin. Approving asks you to confirm it is you — your password (or, with no password on the account, a sign-in in the last ten minutes), plus your authenticator code when two-factor is on — because the grant acts as you for months. Approving redirects back with acodeand anissnaminghttps://camplax.dev(RFC 9207), which a client should check before redeeming the code. - Token:
POST /oauth/token(JSON or form) withgrant_type=authorization_code,code,client_id,code_verifier,redirect_uri. Response:access_token(cxm_…),refresh_token(cxr_…),token_type: "Bearer",expires_in,scope.
The lifetimes and rules that matter:
| Item | Value |
|---|---|
| Authorization code TTL | 10 minutes, single use |
| Access token TTL | 1 hour |
| Refresh token TTL | 30 days unused |
| Grant lifetime | 90 days from when you approved it, however often it refreshes — then the client asks you to approve it again |
| Refresh rotation | every use mints a new pair; replaying an old refresh token revokes the grant |
| Scope | mcp only — one scope, the whole surface |
Every connected client is listed under Account → Sessions → Connected apps, with Disconnect (GET /v1/account/connections, DELETE /v1/account/connections/:clientId, console session only). Signing out everywhere else, resetting your password and changing your sign-in email end every grant and every CLI token too.
Token responses are Cache-Control: no-store. The unauthenticated /mcp request returns 401 with a WWW-Authenticate pointer to the protected-resource document, which is how clients find all of this on their own.
Scope the connection to one project
Three query params turn the shared endpoint into a scoped connector — the same pattern Supabase uses for its scoped MCP:
https://camplax.dev/mcp?project=my-app
https://camplax.dev/mcp?project=my-app&read_only=1
https://camplax.dev/mcp?project=my-app&features=docs,database,debugging
project=<slug>binds the connection.list_projectsreturns only that project, every tool that takesslugdefaults to it, and any other slug is rejected with aproject_scopeerror. The slug must resolve to a project the credential can already see — anything else is a flat404, so the parameter cannot probe for project names.read_onlymakes the connection a viewer — presence turns it on; only an explicitread_only=0(orfalse/no/off) turns it back off. Every tool not annotated read-only —create_project,import_project,deploy,probe_project— fails with aread_onlyerror, and so does every write action inside a grouped tool (env set,database migrate, …).readonlyis accepted as an alias.tools/liststops advertising the writes, but they are not merely hidden — the calls fail closed.features=<group,…>limits the connection to those tool groups:account,docs,development,debugging,database,storage,domains,users,email,automation(a single tool name works too).whoamiis always available. Anything outside the list is absent fromtools/listand fails withfeature_disabledif called; an unknown group is a400, never an unscoped connection.
Use it when an agent only ever works on one app, or when a third-party agent should see but never touch: a bound, read-only connector cannot reach your other projects and cannot change this one.
The approve page offers the same two limits — one project, read-only — and starts on whatever the URL asked for. A limit chosen there belongs to the connection itself: a URL that names a different project gets a 404, and read_only=0 cannot lift it. whoami reports the bound project and readOnly: true whichever way they were set. A project binding is checked against your seat on every call, so a project that is deleted, or one you lose your seat on, ends the connection with a 404.
The tools
Eight verbs cover context, shipping, migration and live verification. The catalog stays small on purpose — agents burn tokens on a 40-tool catalog and then call the wrong one.
| Tool | Arguments | What it does |
|---|---|---|
whoami | — | Which identity is connected: session, CLI token, OAuth token or API key, and its scopes |
list_projects | — | The projects this identity can see |
project_context | slug | Repo, build config, release evidence, permissions, secret names — never values |
create_project | name, slug, optional templateId | Provision a new app; starts in provisioning |
import_project | name, slug, optional siteUrl, sourceDatabaseUrl, githubRepo | Migrate a Vercel + Supabase app; the old site stays up |
deploy | slug, optional branch, sourceKey | Queue a production deploy; sourceKey (from PUT /v1/projects/:slug/source) ships an uploaded folder instead of a repo. Poll project_status |
project_status | slug | Provisioning, migration or deploy progress |
probe_project | slug, optional only | Bounded live checks per feature — pass, fail or skip |
Every result carries the data as JSON text and as structuredContent; a list comes back as { "items": [...] } there, because clients require an object.
A ready deploy is production evidence only when project_context.release.live is true — queued, building and deploying mean work in progress, not deployed.
Beside these, grouped tools carry the console surfaces (env, database, logs, realtime, …), each with an action argument. Two are worth calling out:
diagnose(groupdebugging, read-only) —{action: "run", slug, environment?}returns ranked findings for production or one preview: a failed deploy, an error group that is still firing, a 5xx rate over 10%, down uptime checks, failing cron jobs, tables without a primary key, foreign keys without an index, wide-open tables, and realtime channels pointed at tables that do not exist. Each finding names the next call. Behind the findings sit the deploy status, the top unresolved error groups (message and path — never stacks), recent error log lines and a schema summary. It honoursfeatures=: a section is included only when the connection could call the tool it summarizes (schema needsdatabase, lines needlogs), and a project key only sees what its scopes unlock — the rest is listed asskipped, never reported as healthy.config(groupdevelopment) —plancompares acamplax.json(and the SQL files of its migrations folder) with the live project, andapplyruns the reviewed plan by its hash. See camplax.json as code.planis a read, so it works on aread_onlyconnection;applyis a write and needs amanagekey or a build seat.applynever runs migration files: a plan with pending migrations is refused there and runs only throughapply_with_migrations, which is marked destructive and also needs thedatabasegroup on the connection —features=developmentalone cannot run SQL.
No repo connected? project_context.nextAction will say so. Two ways to ship anyway: the owner connects GitHub once, or the agent deploys the local folder directly — run camplax deploy --local from the project directory (it packs, uploads and queues in one step), or do it by hand: PUT /v1/projects/:slug/source with a gzipped tar body, then deploy with the returned sourceKey.
Rate limits
Two buckets gate the endpoint, both 120 requests per minute: one per client IP (before auth, so forged bearers are cheap to refuse) and one per identity after it. OAuth endpoints have their own — 20 client registrations per IP per hour, 60 token calls per IP per minute. A 429 carries Retry-After.
Search the docs from the agent
The docs tool searches these pages from inside the MCP connection — no browsing, no guessing from another platform's docs:
| Action | Arguments | Returns |
|---|---|---|
search | query, optional limit (1–10) | The best-matching sections, each with a deep link and a snippet |
read | page, optional anchor | One page (or one section) as markdown |
list | — | Every page with its one-line summary |
It is read-only, needs no project, and works with any credential — the docs are public. Connect with ?features=docs for a docs-only agent.
What agents cannot do
Deliberately absent from the MCP: billing, creating API keys, deleting projects, and reading secret values. project_context returns secret names and presence only. There is no workaround, and that is the point — an agent can ship and verify an app without ever holding a secret or spending money.
Reading the docs
/llms.txt— the short agent briefing./llms-full.txt— every docs page in one file.- Every docs page has a markdown mirror:
/docs/getting-started.mdfor/docs/getting-started.html, and so on.
When MCP is unavailable, the same verbs exist on the CLI with --json — camplax deploy --json, camplax import --name <slug> --site <url> --db <postgres-url> --json, camplax probe --json. Never scrape the console HTML.