Amba

Install

Connect Claude Code, Cursor, Windsurf, or Claude.ai web to the Amba MCP server.

The fastest way to connect any MCP-aware AI coding agent to Amba is to let the CLI do it:

npx -y @layers/amba init

When invoked from a coding agent's bash tool (non-TTY), amba init runs headlessly — no browser, no prompts. It signs you up over HTTPS, provisions a sandbox-tier project, writes .env.local + AMBA.md into your current directory, installs the /amba skill into every detected agent skill directory, and merges an amba entry into every MCP client config it finds on disk. The project's server key goes to your OS keychain (amba server-key prints it), and .amba/ and .env*.local go into the project's .gitignore.

Signup is open, so no invite code is needed. If you have one from an invite email, add --invite <code> (or set AMBA_INVITE_CODE). See Getting access.

✓ Amba ready
  project: proj_abc12… (sandbox-1747234567-abc123@layers.com)
  keys → /Users/me/my-app/.env.local
  server key → OS keychain (service "amba", account "server-key:api.amba.dev:proj_abc12…") (`amba server-key` prints it)
  gitignore → /Users/me/my-app/.gitignore (+ .amba/, .env*.local)
  mcp → /Users/me/.claude.json, /Users/me/.cursor/mcp.json (active next agent launch)
  skill → /Users/me/my-app/.claude/skills/amba/SKILL.md

  next: npm install @layers/amba-web
        Amba.configure({ projectId: process.env.AMBA_PROJECT_ID, clientKey: process.env.AMBA_CLIENT_KEY })
  later: amba claim <your-email> to upgrade past sandbox

The install is complete the moment the CLI exits. The MCP entry has been persisted; it activates automatically on the next launch of your MCP client (Claude Code, Cursor, Windsurf). For tool calls in the current session — useful when the agent that just ran the CLI wants to keep working without you closing it — pass pat as an inline argument on every amba_* tool call (npx @layers/amba token prints it; the CLI keeps it in your OS keychain).

When you're ready to bind the sandbox account to a real email and lift the caps to the Free tier (1,000 MAU, 500 MB DB), run:

npx @layers/amba claim me@example.com

You'll get a confirmation link in your inbox; open it and press Confirm to promote the project and to be able to sign in from a browser if you ever need to.

What the CLI configures

The amba init flow probes these locations and merges into the ones that exist (or scaffolds the global ones if missing):

ClientConfig pathAuthorization header
Claude Code (global)~/.claude.jsonBearer <your-pat>
Claude Code (project-local)<cwd>/.mcp.json (only if it already exists)Bearer ${AMBA_PAT:-}
Cursor (global)~/.cursor/mcp.jsonBearer <your-pat>
Cursor (project-local)<cwd>/.cursor/mcp.json (only if it already exists)Bearer ${env:AMBA_PAT}
Windsurf~/.codeium/windsurf/mcp_config.jsonBearer <your-pat>

The merged entry in a user-scope file (the ones in your home directory) is:

{
  "mcpServers": {
    "amba": {
      "type": "http",
      "url": "https://mcp.amba.dev/mcp",
      "headers": { "Authorization": "Bearer <your-pat>" }
    }
  }
}

The CLI writes user-scope files with mode 0600, so other accounts on the machine cannot read the PAT.

Existing mcpServers.* entries are preserved — only mcpServers.amba is overwritten. When the CLI replaces a different amba entry, it first copies the old file to ~/.amba/backups/ (mode 0600), outside your repository.

Project config files

A project-local .mcp.json or .cursor/mcp.json is committed with the repository, so the CLI rewrites an amba entry in one to reference the AMBA_PAT environment variable, in each client's expansion syntax (${AMBA_PAT:-} for Claude Code, ${env:AMBA_PAT} for Cursor), and prints where the PAT is stored. A project config without an amba entry stays as it is, and the user-scope entry serves that project.

Where a project config references AMBA_PAT, set it to your Personal Access Token (amb_dpat_…) in the environment your editor starts from. When the CLI's stored credential is a PAT (an account from amba init --sandbox or --token), amba token prints it:

export AMBA_PAT="$(npx @layers/amba token)"

After a browser login, amba token prints a session token that expires within a day, and AMBA_PAT also authenticates every amba command, so use the PAT from Tokens instead. Each teammate sets their own PAT, so one committed file works for everyone.

Browser login and MCP

The Bearer in an MCP config is fixed, and the client has no way to renew it, so it has to be a Personal Access Token (amb_dpat_…), which lasts until it is rotated. A browser login (amba login, or the browser step of amba init) gives the CLI a session instead: its access token expires within 24 hours. The CLI renews that session for its own commands, and amba init leaves MCP configs untouched when the stored credential is a session. It prints mcp → not wired: … Wire one with amba init --token <pat> instead. A config that still holds a session token starts failing with 401 INVALID_DEVELOPER_TOKEN once that token expires.

To wire MCP clients for an account you signed in to through the browser:

  1. Use the account's PAT if you have it (from amba init --sandbox, amba_developer_signup, or an earlier rotation).

  2. If you don't have it, mint a new one. This replaces the account's current PAT, so every other tool or machine using the old PAT stops working until you give it the new one.

    In the console, open Tokens. It shows when the current PAT was created and last used. Replace token (or Create token) asks you to confirm, then shows the new PAT once, filled into the amba init --token command and an MCP config entry. From a terminal, the same request is:

    curl -sS -X POST https://api.amba.dev/v1/auth/developer/pat/rotate \
      -H "Authorization: Bearer $(npx @layers/amba token)"

    The response's data.pat is the new token. Agents can call amba_developer_rotate_pat instead.

  3. Wire it in from your project directory:

    npx @layers/amba init --token amb_dpat_…

    This writes the PAT into every detected user-scope MCP client config, as above, and an AMBA_PAT reference into project-local ones. It also links the directory to an Amba project: a directory with .amba/project.json keeps its project and keys, and any other directory gets a project picked or created, with a new client key written to .env.local and a new server key stored in the OS keychain. The CLI does not store a token passed with --token, so your browser login stays the CLI's own credential.

Useful flags

amba init --invite <code>                  # optional invite code for a new account
amba init --sandbox-email me@example.com   # override the auto-generated sandbox email
amba init --no-mcp-config                  # skip the MCP-client-config write step
amba init --json                           # machine-readable summary for agents

amba init auto-detects non-TTY invocations (the bash-tool case) and routes through the sandbox-signup path. Explicit --sandbox, --json, and --invite flags also force it. There is no separate "sandbox vs full" mode on the wire — the only thing the headless path adds is generating an email + password.

In-agent signup (via MCP)

If an agent is already pointed at the Amba MCP server but no developer account exists yet, it can provision one inline by calling the public amba_developer_signup tool. Signup is open, so no invite code is needed; on INVITE_REQUIRED, INVITE_INVALID, or SIGNUPS_CLOSED, follow Getting access. The response includes ready-to-paste config snippets — one per MCP client — so the agent can write the right snippet to disk without inventing JSON shapes:

{
  "pat": "amb_dpat_…",
  "project": {
    "project_id": "…",
    "client_key": "…",
    "server_key": "…",
    "provisioning_status": "provisioning",
    "verify_url": "…",
    "verify_token": "…"
  },
  "mcp_config": {
    "claude_code": {
      "path_hint": "~/.claude.json (user scope; the PAT belongs only here). A project .mcp.json is committed with the repo: there, set the header to \"Bearer ${AMBA_PAT:-}\" and have the customer export AMBA_PAT.",
      "snippet": {
        "mcpServers": {
          "amba": {
            "type": "http",
            "url": "https://mcp.amba.dev/mcp",
            "headers": { "Authorization": "Bearer amb_dpat_…" }
          }
        }
      }
    },
    "cursor": {
      "path_hint": "~/.cursor/mcp.json (user scope; the PAT belongs only here). A project .cursor/mcp.json is committed with the repo: there, set the header to \"Bearer ${env:AMBA_PAT}\" and have the customer export AMBA_PAT.",
      "snippet": {
        "mcpServers": {
          "amba": {
            "type": "http",
            "url": "https://mcp.amba.dev/mcp",
            "headers": { "Authorization": "Bearer amb_dpat_…" }
          }
        }
      }
    },
    "windsurf": {
      "path_hint": "~/.codeium/windsurf/mcp_config.json (user scope)",
      "snippet": {
        "mcpServers": {
          "amba": {
            "serverUrl": "https://mcp.amba.dev/mcp",
            "headers": { "Authorization": "Bearer amb_dpat_…" }
          }
        }
      }
    }
  },
  "agent_instructions": "The PAT is in your hand right now — keep building in THIS session immediately. Pass `pat: \"<the pat from this response>\"` as an inline argument on every subsequent `amba_*` tool call. The inline `pat` arg overrides the inbound Bearer for that one call, so no client config change is needed mid-session. For future sessions: write the matching snippet from `mcp_config` to the customer's MCP client config file (merge with any existing `mcpServers` block — do not overwrite). Next time the MCP client starts it sends the PAT as the inbound Bearer automatically and the inline `pat` arg becomes optional. The customer does not need to do anything."
}

The agent uses the PAT inline on every subsequent amba_* tool call in the same session (no restart, no client config change needed mid-session) and writes the matching snippet to the customer's MCP config so future sessions pick the PAT up as the inbound Bearer automatically.

amba_developer_login returns the same mcp_config shape for returning customers.

Lifting the sandbox cap

Two paths, depending on whether the developer wants to bind a real email address.

Recommended (binds a real email): run npx @layers/amba claim me@example.com from the terminal. Agents without shell access can POST { "email": "<target>" } to /v1/auth/developer/claim with Authorization: Bearer <pat> directly. The backend emails a single-use link to that address; opening it and pressing Confirm promotes the developer from agent_sandbox to verified_free AND updates developers.email so they can sign in from a browser later.

Headless (keeps the auto-generated sandbox email): call amba_developer_verify with the verify_token from the signup response. This lifts the MAU cap immediately but keeps the developer's email as the sandbox-generated sandbox-<epoch>-<nonce>@layers.com. Useful for fully-autonomous CI flows that never have a real inbox to claim against.

{
  "tool": "amba_developer_verify",
  "args": { "verify_token": "<value from project.verify_token>" }
}

The tool returns the developer record with the upgraded tier:

{
  "data": {
    "verified": true,
    "already_verified": false,
    "developer": {
      "id": "dev_…",
      "tier": "verified_free",
      "is_verified": true
    }
  },
  "status": 200
}

Tokens are single-use and expire after 7 days. A re-call with the same token after the developer is verified returns 200 with already_verified: true — safe to retry on a flaky network. The browser-clickable verify_url carries the same token; either path consumes it. The response does not echo the developer email — agents that need it should call amba_developer_me with the inbound PAT.

When to use the in-agent path

For most flows the CLI (amba init) is the canonical path because it also writes .env.local, AMBA.md, the /amba skill bundle, and (where applicable) updates project-local MCP configs. Use the in-agent path (amba_developer_signup + inline pat on subsequent calls) when no shell is available, or when iterating against an MCP client that doesn't expose terminal access to the agent.

Call the MCP server directly via HTTP

https://mcp.amba.dev/mcp is a plain Streamable-HTTP endpoint, not a stdio process — any agent with curl or fetch can call the full tool surface without configuring mcpServers in a host client at all. This is the path to use when:

  • You ran amba init and the MCP entry persisted, but the typed mcp__amba__* tools haven't loaded into your agent's tool list yet (e.g., Claude Code, Codex, Windsurf — they read mcpServers once at session startup).
  • You're driving Amba from a non-MCP-aware agent or a CI script.
  • You want the full 540+ tool surface without picking which subcommand the amba CLI exposes.

Wire shape — standard JSON-RPC 2.0 tools/call:

PAT=$(npx @layers/amba token)
curl -sS https://mcp.amba.dev/mcp \
  -H "Authorization: Bearer $PAT" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "MCP-Protocol-Version: 2025-03-26" \
  -d '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "tools/call",
    "params": {
      "name": "amba_currencies_create",
      "arguments": {
        "project_id": "proj_…",
        "name": "coins",
        "display_name": "Coins"
      }
    }
  }'

The response wraps the tool's actual payload at result.content[0].text as a JSON string:

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "content": [{ "type": "text", "text": "{\"data\":{\"id\":\"cur_…\", \"name\":\"coins\", …}}" }]
  }
}

Pre-auth tools (amba_developer_signup, amba_developer_login, amba_developer_refresh) work without the Authorization header — the rest require a Bearer (PAT or OAuth access token). Standard MCP methods (initialize, tools/list, ping) also work without a Bearer if you want to enumerate the catalog.

Manual setup

If your MCP client isn't in the list above (or you skipped the auto-write with --no-mcp-config), drop the snippet below into its config file.

Server URL: https://mcp.amba.dev/mcp

The ?src=docs on the URLs below records that the install came from these docs; the server works the same with or without it. Use a Personal Access Token (amb_dpat_…) as the Bearer. amba init --sandbox and the amba_developer_signup tool return one for a new account; for an existing account see Browser login and MCP. A browser-login token (amba login, or the console's sign-in) expires within 24 hours, and an MCP client cannot renew a token in its config file. The console's Tokens page mints a PAT. A config holding a token that starts with eyJ holds a session token (written by amba init before 4.1.11, copied from an earlier version of the Tokens page, or copied from amba token after a browser login), and needs the PAT in its place.

Claude Code

Edit ~/.claude.json:

{
  "mcpServers": {
    "amba": {
      "type": "http",
      "url": "https://mcp.amba.dev/mcp?src=docs",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Cursor

Edit ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):

{
  "mcpServers": {
    "amba": {
      "type": "http",
      "url": "https://mcp.amba.dev/mcp?src=docs",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Windsurf

Edit ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "amba": {
      "type": "http",
      "url": "https://mcp.amba.dev/mcp?src=docs",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "amba": {
      "url": "https://mcp.amba.dev/mcp?src=docs",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

The new config takes effect the next time you launch the MCP client. (Claude Code, Codex, and Windsurf load mcpServers at startup; Cursor watches the file and picks up changes on the next message.)

Browser-only clients (Claude.ai web)

Claude.ai connectors use OAuth 2.1 + PKCE — there is no static Bearer to paste. When you add Amba as a connector, Claude.ai's MCP client:

  1. Discovers the authorization server at https://mcp.amba.dev/.well-known/oauth-authorization-server.
  2. Registers itself at https://mcp.amba.dev/register (dynamic client registration) and receives a client_id.
  3. Sends you to https://mcp.amba.dev/authorize to sign in with your Amba developer credentials (the same email + password you use for app.amba.dev). The sign-in page names the app asking for access and the host your browser returns to; sign in only when both are what you expect.
  4. Exchanges the resulting one-time code for an access token at https://mcp.amba.dev/token, with PKCE (S256) enforced.
  5. Refreshes the access token automatically before it expires (1h access token, 30-day refresh). Each refresh replaces the refresh token. If a refresh token that was already used is presented again, the whole connection ends and you sign in again.

To connect:

  1. In Claude.ai, open Settings → Connectors → Add custom connector.
  2. Server URL: https://mcp.amba.dev/mcp.
  3. Click Authorize. You'll be redirected to the Amba sign-in page; complete it and you'll return to Claude.ai with the connector active.

What the server publishes

The MCP server exposes the standard OAuth 2.1 surface for inspection:

  • GET /.well-known/oauth-authorization-server — RFC 8414 authorization-server metadata.
  • GET /.well-known/oauth-protected-resource — RFC 9728 protected-resource metadata.
  • POST /register — RFC 7591 dynamic client registration. Every client registers before it can authorize; redirect_uri at /authorize must match a registered URI exactly.
  • GET /authorize — interactive consent (HTML form). Request errors show on this page; the browser returns to the client only with an authorization code.
  • POST /token — code → access + refresh token; supports grant_type=refresh_token rotation.
  • POST /revoke — RFC 7009 token revocation. Revoking an access or refresh token ends the whole connection it belongs to.
  • GET /jwks — public RSA key (kid: amba-mcp-1) for verifying issued access tokens.

PKCE is mandatory — only code_challenge_method=S256 is accepted. The implicit, password, and client-credentials grants are not supported.

An OAuth connection cannot create a credential or grant access that would outlast it. These need your PAT or a console session, and answer 403 OAUTH_TOKEN_CANNOT_MINT_CREDENTIAL (OAUTH_TOKEN_CANNOT_ROTATE_PAT for the first) over OAuth: rotating the PAT, server keys (amba_api_keys_create with key_type: "server"; client keys work), service accounts, org and project invites, member role changes, ownership transfers, deploying functions (deployed code holds the project's internal token), amba_provision_app, and claiming the account. amba_projects_create over OAuth returns the new project's client key only.

An OAuth connection also cannot send deliveries to a destination that would keep receiving them after you disconnect it. Creating a webhook subscription (amba_webhooks_create, amba_control_webhooks_create), changing a subscription's target_url, setting an email forward (amba_domains_email_forwards_set), turning on catch-all forwarding (amba_domains_email_catch_all_set with enabled: true), and creating or onboarding a payout account (amba_payments_account_create, amba_payments_create_onboarding_link, amba_affiliate_my_payout_create, amba_affiliate_my_payout_onboarding) answer 403 OAUTH_TOKEN_CANNOT_SET_DELIVERY_TARGET over OAuth; use your PAT (pass it as pat) or the console. Pausing or deleting a subscription, deleting a forward, and turning catch-all forwarding off work over OAuth.

What a connection builds stays after it is disconnected: collections, sites, and schedules of functions you deployed keep working.

Disconnecting an app

The console's Connected apps page (app.amba.dev/dashboard/connected-apps) lists every app connected through OAuth, with when you approved it and when it last refreshed. Disconnect ends that connection within 30 seconds. Replacing your personal access token disconnects every app at once.

Authentication

The MCP middleware accepts either:

  • A Bearer token from amba_developer_signup / amba_developer_login / any PAT minted in the dashboard (used by Claude Code, Cursor, Windsurf, and the CLI).
  • An OAuth-issued access token from the flow above (used by Claude.ai web).

Both resolve to the same developer identity for downstream tool calls.

Verifying

After connecting, ask your agent:

List all my Amba projects.

If the agent calls amba_projects_list and returns your projects, you're connected. A 401 means the token is wrong, expired, or (for OAuth) failed signature verification; a MISSING_AUTHORIZATION means the header isn't being sent.