# MCP server

Canonical: https://docs.gemmein.com/mcp
Markdown: https://docs.gemmein.com/mcp.md

Let your AI coding tool read Gemmein’s docs and check your app’s safety rules while it builds. `@gemmein/mcp` gives it Gemmein's guide, SDK reference, rule and error explainers, and a live check of your app's safety rules, as tools inside the editor. It helps your agent use [`@gemmein/sdk`](https://docs.gemmein.com/reference.md) correctly, then checks the result. For AI apps with no terminal (ChatGPT, Claude, Lovable, Replit, Bolt, v0), the same tools and the whole Development build are one address: [the hosted connector](https://docs.gemmein.com/mcp.md#hosted-connector).

> **Which way to connect?** A chat app, a coding tool on your computer, or the guide on its own, side by side, with what each can do and what needs your approval: [Connect your AI](https://docs.gemmein.com/connect.md).

## Set it up

Claude Code:

```
claude mcp add gemmein -- npx -y @gemmein/mcp
```

Cursor, or any client that takes an `mcpServers` block:

```
{ "gemmein": { "command": "npx", "args": ["-y", "@gemmein/mcp"] } }
```

Nine tools are read-only. `check_integration` runs live checks against your app. With a Development secret key it also writes in the Development environment: it creates two test people and a probe record, deletes the record, and signs the test people out of earlier sessions. The test people stay. Live secret keys are refused.

## The hosted connector

Add Gemmein to an AI app as a connector, at one address:

```
https://api.gemmein.com/mcp
```

It speaks MCP over Streamable HTTP and signs in with OAuth. The ten tools of `@gemmein/mcp` and `tools_map` work with no account. Building your app from the chat needs a free Gemmein account at [app.gemmein.com](https://app.gemmein.com/) and one approval in your browser. Signed in, the connector has 146 tools.

`tools_map` is a read-only map of those tools in eight groups (start, build, testing in Development, the site, asking for Live changes, reading Live, customers, and keys and billing), each tool with one line on when to use it. Give it a goal (take payments, put my site live, sign people in, store data, run something on a schedule or event, charge credits for an action, read how the business is doing, handle a customer message, connect a domain, test before publishing) and it returns the matching group and the order to call its tools in. It runs no other tool and changes nothing.

### Add it

- **Claude** (claude.ai on the web or the desktop app): Customize → Connectors → + Add → Add custom connector → name “Gemmein”, URL: the address → Authentication: **Sign in when needed** → Add. Claude connects without signing in, and the first account tool opens the sign-in and the approval on app.gemmein.com. On Team and Enterprise, an Owner adds it in Organization settings → Connectors, then each member connects. Claude Code: `claude mcp add --transport http gemmein https://api.gemmein.com/mcp`.
- **ChatGPT** (on a computer, at chatgpt.com): open Gemmein for Business in [ChatGPT's plugin directory](https://chatgpt.com/plugins/plugin_asdk_app_6ad4368ad2d88191b4b4db7dd75f1961) → Install plugin → Connect Gemmein for Business → sign in → Approve. Or add it by address: Plugins in the left sidebar → Add (top right) → Add custom MCP server → name “Gemmein”, URL: the address, OAuth → approve on app.gemmein.com. Works on every ChatGPT plan. Opening the address in a browser answers 405: it speaks MCP, not web pages.
- **Lovable**: Connectors → + → MCP server → a server name (Gemmein) → Server URL: the address → OAuth → Add & authorize.
- **Replit**: Integrations → MCP Servers → + Add MCP server → a display name and the address → Test & save.
- **Bolt**: the chat box’s + → Connectors → Manage connectors → Custom MCP server → a name, the address, transport HTTP, auth MCP OAuth → Connect.
- **v0**: the prompt form’s + → MCPs → Add MCP → custom → the address → OAuth.
- **Any other client**: the address, with transport “streamable HTTP”.

### What one approval allows

The first time a tool needs your app, the AI app opens your browser on Gemmein’s approval page. You pick the app and press Approve, once. That approval lets the AI app build the app in **Development for 30 days**: collections, plans, relays, AI tools, email templates, test members and sandbox test payments, through the same checks as the command line. It never sees a secret key.

This approval is separate from the command line’s, and lasts longer: a connector’s Development approval lasts 30 days, while the one `npx gemmein login` asks for lasts 7 days. Each is listed on its own on Settings → Connections.

- `pull` reads the app as its `gemmein/` files; `sync` sends them back, showing the difference first (`plan: true` shows it and writes nothing), up to 1 MB a call.
- One at a time: `declare_collection`, `set_payments`, `put_relay`, `put_ai_tool`, `put_ai_alias`, `put_email_template` and `rename_app`.
- Prove it: `test_member_add`, `test_mail`, `test_pay` and `test_outcome`, all in Development. `test_pay` takes an `outcome`: `pays`, `declined` or `asks_to_verify` (Stripe asks the payer to verify, then it pays). Gemmein pays with Stripe’s published sandbox numbers for that outcome, so no payment details pass through the chat.
- `prove_app`: proves a chat-built app in Development as two test members (add them first with `test_member_add`). Each private or direct collection keeps each person to their own records, each relay that fires on a new record runs (its email lands in the test mailbox), and each AI tool answers and spends the credits it states, one line per proof. It refuses on Live and runs at most 10 times a day for an app.
- `app_status`: the app, its public key, and what going live waits on.
- Read Development: `dev_records`, `dev_people`, `dev_errors` and `dev_logs`, under the same customer-details setting as the Live reads. Each read is on Development’s Logs; none reaches Live.
- Delete in Development, at once: `dev_delete_collection`, `dev_delete_relay`, `dev_delete_ai_tool` and `dev_delete_email_template` (the template goes back to Gemmein’s own design). Each is on Development’s Logs, naming the connection. Live keeps its own copy: a promote never removes anything from Live, and the answer names the Live request that would.
- Act on a test member, at once: `test_member_credits`, `test_member_suspend`, `test_member_unsuspend` and `test_member_cancel_subscription`, through the same steps as the Live actions. Only a test member of this app’s Development made with `test_member_add`; anyone else is refused with `not_a_test_member`.
- The site: `site_draft`, `site_put_file` and `site_publish` publish to the private preview; `site_read` reads a file as it is now (up to 256 KB a call), so a change starts from the current page; `site_status`, `site_history`, `site_rollback`, `site_rebuild` and `site_download` manage its versions. `site_test` opens the draft as a customer would before it publishes, and `site_test_result` reads a run again; `site_errors` reads what the published pages hit. `site_share_link` answers the preview’s link, on its own address (`https://<slug>-dev.gemmein.dev/p/<code>`); `site_unpublish` takes the preview offline (every version and file is kept) and `site_revoke_previews` turns the preview link off. Both work on Development only; see [Your site](https://docs.gemmein.com/sites.md).
- Domains: `domain_check` says what public DNS shows, what Gemmein needs and the one record to change; `domain_connect_link` gives you your DNS provider's own guide and the exact steps. Both only read; see [Is your domain working?](https://docs.gemmein.com/sites/domains.md#ai-domain-check)
- Live reads (with the Live-read approval): `live_overview`, `live_people`, `live_revenue`, `live_subscriptions`, `live_records`, `live_errors`, `live_logs`, `live_usage`, `live_webhooks`, `live_emails` and `live_inbox`. `refund_steps` reads one Live payment and answers your exact steps in Stripe for refunding it; refunds are made in Stripe, never from a chat.
- Reply to a customer: `inbox_reply` drafts the reply; you approve the exact text in your browser before it is sent.
- Relays: `relay_runs` lists a relay’s runs and `replay_relay` runs one again.
- Your steps: `connect_payments`, `provider_key` and `request_server_key` hand you a page of Gemmein’s own, where you paste the key for Stripe, an AI provider or a server key; your AI never sees the key.
- Pay for the plan before going live: `buy_plan` hands you Gemmein’s payment page for the app. You pay in Stripe’s window; the tool pays nothing itself. The app then owns the plan and billing starts. The app stays in Development until you approve going live, which then has no payment step. Details: [Go live](https://docs.gemmein.com/console/go-live.md).
- Ask to put a version live: `site_promote`, approved like any Live change.
- Ask to delete the app, permanently: `delete_app`. Your human approves it in their browser with the real counts of what goes, typing the app's exact name and the emailed code (and their authenticator code too, if they have one) — neither can be skipped from here. See [Delete an app](https://docs.gemmein.com/apps.md#delete-an-app).
- Customers’ follows and circles: `people_graph` reads who follows whom, follow requests and circles. `remove_from_circle`, `close_circle` and `remove_follow` moderate them: at once in Development, and on Live as a request you approve.

**Reading Live needs its own approval.** It is a separate line on the approval page. It changes nothing, and every read is on your Logs. What the AI app reads (your customers’ emails, records and revenue among it) goes to that AI app and its provider, so allow it only for an AI app you trust with that data.

**Live changes are approved one at a time.** Each Live change has its own tool, with only its own fields, and asks you to approve that one change in your browser: `request_go_live`, `request_promote`, `request_rename_app`, `request_add_domain`, `request_delete_domain`, `request_accept_terms`, `request_revoke_server_key`, `request_unpublish_site`, `request_delete_collection`, `request_delete_relay`, `request_delete_ai_tool`, `request_grant_credits`, `request_suspend_person`, `request_delete_person` and `request_cancel_subscription`. A change to one customer (credits, a suspension, a cancellation, a reply) is approved as it is. Going live is your approval, then paying for the plan in Stripe’s window, with no code; when `buy_plan` has already paid for the plan, going live has no payment step. A change to how the business runs (a promote, a rename, a domain, deleting Live’s copy of a collection, relay or AI tool, putting a version live, taking the live site offline) also asks for the code Gemmein emails, and your authenticator code if you set one up; the whole list is on [Connect your AI](https://docs.gemmein.com/connect.md#approvals). Your Approve carries the change out, once. `live_action_status` only reads where it stands, and with `wait: true` it answers as soon as the change is done, failed, refused or expired. A secret key, Stripe’s key and an AI provider’s key are pasted on a page of Gemmein’s own, and DNS records are handed over with a link, never in the chat. Paying is your press in Stripe’s window.

The connection is listed on Settings → Connections as “Connector · builds <app> in Development”. Revoke it there and it stops at once; disconnecting in the AI app ends it too. Each step is on Logs. `npx gemmein dev` serves the same `/mcp` on your computer, at `http://127.0.0.1:4545/mcp`, with no sign-in.

| If… | What happens | What you do |
| --- | --- | --- |
| A tool that needs the app is called before sign-in | The AI app gets HTTP 401 with a sign-in pointer and opens the approval page; after Approve it repeats the call | Approve, if you asked for it |
| A Live-reading tool is called on a connection without Live reads | HTTP 403 with `insufficient_scope`; the AI app asks you to approve it | Approve only for an AI app you trust with Live data |
| You press Deny, or wait past 10 minutes | The AI app is told `access_denied`; nothing is handed out | Connect again to ask again |
| The connection was revoked or its 30 days ended | Calls answer HTTP 401 with `invalid_token`, and the AI app asks you to sign in again | Approve again |
| A file in `sync` breaks a rule | That file is refused with the fix; the others apply | Fix the file and send it again |
| A `sync` is more than 1 MB | HTTP 413 with `too_large` | Send the files in more than one call |
| Something is deleted in Development after the app went live | Development’s copy goes at once; Live’s stays, and the next promote doesn’t remove it | To remove Live’s copy, approve `request_delete_relay`, `request_delete_ai_tool` or `request_delete_collection`; a Live collection holding records is deleted only on the dashboard |
| Live has no relay or AI tool by the name asked to delete | `relay_not_found` or `ai_tool_not_found`; nothing is asked of you | Check the name on Live’s Relays or AI page |
| A test-member tool names a real person, or someone in Live | `not_a_test_member`; nothing is done | Use a test member added with `test_member_add`; Live’s customers are changed through the `request_*` tools |
| `site_unpublish` is asked for the live site | `development_only`; nothing is sent | Taking the live site offline is `request_unpublish_site`, with your approval |
| `buy_plan` is called while the account’s last payment has not gone through | `billing_past_due` (402): “Your last payment didn’t go through. Update your card in Stripe, then go live.” No payment page is made | Update the card or pay the open invoice in Stripe’s billing page (Usage), then go live |
| A tool can’t finish | `connector_error`; whatever was applied before it stopped stands | Call `pull`, check the input against the guide (`search_docs`), and try again |

## What your agent gets

- **guide**: the full builder's guide, covering the auth flow, the eight safety rules, record shapes, links, uploads, contention and payments.
- **reference**: every SDK method, with its signature, return shape and error codes.
- **search\_docs**: targeted search over both, when the agent needs one fact.
- **changes**: what changed in each Gemmein release after the version the agent knew (`since`, such as `0.22.1`), or the last three: each change as it was and as it is now, with the error codes it added or removed. It is bundled with the server and works offline; every release in full is at [docs.gemmein.com/changes.txt](https://docs.gemmein.com/changes.txt). Your agent calls it when what it remembers may be older than the installed release.
- **explain\_rule**: what a safety rule allows, what it suits and the mistakes to avoid, or a summary of all eight for planning.
- **explain\_error**: what a `GemmeinError` code means and what to do about it.
- **validate\_collection\_name**: catches a misnamed collection before every read starts returning empty results.
- **explain\_relay**: give it a `gemmein/relays/<name>.json` definition and it returns the sentence the dashboard would show, or the one error that names the bad field. It works offline for all thirteen actions: `write_record`, `grant_access`, `revoke_access`, `grant_credits`, `email_person`, `call_url`, `fulfil_product`, `refund_product`, `grant_plan`, `revoke_plan`, `start_run`, `spend_credits` and `run_function`.
- **reaffirm\_template**: the [CI test script](https://docs.gemmein.com/reaffirm.md), ready to copy.
- **check\_integration**: runs your isolation and access checks live against your own app and returns structured pass/fail. Details below.

## check\_integration

`check_integration` runs the same checks as the [CI test script](https://docs.gemmein.com/reaffirm.md), from inside your agent while it builds. It checks that anonymous access is refused where it must be and that collection names are valid. With your Development secret key, it also checks isolation between two test people, signed in with fresh test sessions. The result comes back as structured pass/fail, so the agent fixes what it finds before the app goes live.

**It catches the bug that passes with one person and breaks with two:** everyone ends up on the same record, which a single-person test never shows. Apps talk to Gemmein in JSON through an API that enforces the safety rules. The API takes named calls only, with no query language, enumerable schema or raw data layer, so attacks that start by mapping the database have nothing to work with. The safety rules are the part to test, and this checks them.

**On the hosted connector**, `check_integration` takes the app’s public `pk_` key and no secret key. The isolation check between two people is `prove_app`, run as the app’s test members, so no key passes through a chat. The secret-key check below belongs to the local server (`npx @gemmein/mcp`).

Pass your `pk_` app key to run the anonymous checks. The isolation check between two people also needs an `sk_dev` key: the Development-only secret key you create in your dashboard. An `sk_dev` key works only against a Development environment: it reaches no real people's data, runs behind the same rules and rate limits as everything else, and the server refuses it on a Live environment. Keep it in server environment variables like any secret. `sk_live` keys are refused for test sessions.

## What happens if…

| If… | What happens | What you do |
| --- | --- | --- |
| `check_integration` gets a key that is not a `pk_` app key | It refuses at once and sends nothing over the network | Pass the app's `pk_` key |
| It gets only the app key | The anonymous checks run; the isolation check between two people is skipped, and the result says how to run it | Add an `sk_dev` key for the full check |
| A collection lets anonymous people read what its rule should keep private | The check fails and names the collection | Fix the collection's rule, then run it again |
| A collection name is invalid, such as `savedGames` | The check fails at that name; `validate_collection_name` suggests the valid one (`saved_games`) | Rename the collection |
| The API cannot be reached | The tool answers with an error instead of waiting | Check the network, then run it again |
| The isolation check finishes | Its probe record is deleted; the two test people stay in Development | Nothing |
| `explain_error` gets a code that is not a stable Gemmein code | It answers with an error saying so | Call it with no code to list the stable ones |
| `explain_relay` gets a definition with a bad field | It answers the one error that names that field, offline | Fix the field it names |
