# MCP server

> Run your support inbox from Claude Code, Cursor, Codex, Windsurf, VS Code, Gemini CLI, Zed or any MCP client. Read and write.

Source: https://conciergesdk.com/docs/mcp/

Concierge runs a remote MCP server, so your coding agent can read **and write** your inbox. A reply sent from your editor reaches the customer's app live and by push, exactly like a reply from the dashboard. "Triage my support, fix the crash Ana reported, and reply to her" is one prompt.

| Environment | URL |
|---|---|
| Hosted | `https://mcp.conciergesdk.com/mcp` |
| Local dev | `http://localhost:4100/mcp` |

It speaks MCP over **Streamable HTTP**, so it works with any client that does: Claude Code, Cursor, Codex, Windsurf, VS Code with GitHub Copilot, Gemini CLI, Zed and others. Config for each is below.

## Sign in with OAuth (no key)

Clients that support MCP OAuth (Claude Code, Cursor, Codex) need nothing but the URL:

```sh
claude mcp add --transport http concierge https://mcp.conciergesdk.com/mcp
```

The first call opens your browser: sign in to the dashboard, review what the agent can do, and click **Allow**. The agent registers itself, signs in with PKCE and keeps a refresh token, so you only do this once. It acts as you in the organization that was active when you clicked Allow, and its replies carry your name.

Anyone can register an app under any name, so the consent screen marks every agent **Unverified app** and shows where access will be sent. Allow only if that address is your own computer (localhost or 127.0.0.1) or a site you started the connection from. Redirects must be https or a loopback http address. Clients that redirect to a custom scheme (for example `cursor://`) work only when the server operator allows that scheme; otherwise use an API key below. An agent that asks for `concierge:read` without `concierge:write` gets read only access.

## Or create an API key

Scripts, CI and clients without OAuth sign in with a staff API key: `ck_` plus 40 characters. It's shown once, and only a hash is stored.

- **Dashboard:** create a key in your settings (it calls `POST /v1/api-keys { name }`). List keys with `GET /v1/api-keys` (prefix only) and revoke with `DELETE /v1/api-keys/:id`.
- **CLI, local dev:**

```sh
pnpm --filter mcp key:create
# or, explicitly:
pnpm --filter mcp key:create -- --email you@example.com --org org_... --name "Claude Code"
```

Every flag is optional; it defaults to the seeded demo user and their first organization. `--quiet` prints only the key.

A key belongs to one organization and acts as the person who created it, so replies are signed with their name. It stops working the moment it's revoked or that person leaves the org. Anyone can revoke their own keys; owners and admins can revoke any key in the org.

## Connect your agent

**Claude Code** (with a key; drop the header to use OAuth instead):

```sh
claude mcp add --transport http concierge https://mcp.conciergesdk.com/mcp --header "Authorization: Bearer ck_..."
```

For local dev, use `http://localhost:4100/mcp` instead (start it with `pnpm dev:mcp`).

**Cursor** (`.cursor/mcp.json`) and other clients that take JSON config:

```json
{
  "mcpServers": {
    "concierge": {
      "url": "https://mcp.conciergesdk.com/mcp",
      "headers": { "Authorization": "Bearer ck_..." }
    }
  }
}
```

**Codex** (`~/.codex/config.toml`):

```toml
[mcp_servers.concierge]
url = "https://mcp.conciergesdk.com/mcp"
http_headers = { Authorization = "Bearer ck_..." }
```

**Windsurf** (`~/.codeium/windsurf/mcp_config.json`):

```json
{
  "mcpServers": {
    "concierge": {
      "serverUrl": "https://mcp.conciergesdk.com/mcp",
      "headers": { "Authorization": "Bearer ck_..." }
    }
  }
}
```

**VS Code with GitHub Copilot** (`.vscode/mcp.json`, agent mode):

```json
{
  "servers": {
    "concierge": {
      "type": "http",
      "url": "https://mcp.conciergesdk.com/mcp",
      "headers": { "Authorization": "Bearer ck_..." }
    }
  }
}
```

**Gemini CLI** (`~/.gemini/settings.json`):

```json
{
  "mcpServers": {
    "concierge": {
      "httpUrl": "https://mcp.conciergesdk.com/mcp",
      "headers": { "Authorization": "Bearer ck_..." }
    }
  }
}
```

**Zed** (`settings.json`):

```json
{
  "context_servers": {
    "concierge": {
      "url": "https://mcp.conciergesdk.com/mcp",
      "headers": { "Authorization": "Bearer ck_..." }
    }
  }
}
```

**Any other MCP client:** point it at `https://mcp.conciergesdk.com/mcp` as a Streamable HTTP (sometimes labeled "HTTP") server with the header `Authorization: Bearer ck_...`. Clients that only launch local stdio servers can bridge with `npx mcp-remote https://mcp.conciergesdk.com/mcp --header "Authorization: Bearer ck_..."`.

Then ask things like "triage my support inbox".

## Tools

Every tool returns a short text summary plus structured content. IDs from another organization behave exactly like missing IDs.

| Tool | What it does |
|---|---|
| `list_apps` | Your apps with app keys, connected state and open conversation counts. |
| `list_conversations` | Filter by `status` (open, snoozed, closed, all; default open), `appId`, `priority`, `query` (name, email, user id, message text) and `limit`. |
| `get_conversation` | Messages (paged with `before`), the customer with metadata and breadcrumbs, devices and status. |
| `reply` | Replies as you. The customer gets it live and by push, and the AI pauses on that thread. Optional `status`. Idempotent on `clientId`. |
| `send_card` | A `review` card (store link) or an `offer` card (paywall id), with optional text. |
| `update_conversation` | Set `status`, `priority` or `aiState`. |
| `search_customers` | By name, email, user id, customer id or a metadata value. |
| `get_customer` | Profile, devices and conversation summary. |
| `get_app_stats` | Open, snoozed, closed, unread, AI resolved, median first response, customers and 7 day volume. |
| `get_setup_instructions` | SDK install steps for `ios`, `android` or `react-native`, with your app key filled in. |
| `verify_install` | Whether the first real message arrived, devices per platform, SDK versions and push readiness. |
| `list_knowledge` | The AI agent's knowledge base. |
| `upsert_knowledge` | Adds or updates a knowledge article, matched by id, then by title. Takes the structured fields (`feature`, `screenPath`, `steps`, `planRequired`, `knownIssues`, `relatedScreens`, `deepLinkId`) and `draft: true` to leave it for review. |
| `list_screens` | The feature map: screen ids and titles your app registered with `registerScreen` (never deep links). |
| `list_knowledge_gaps` | Questions the AI couldn't answer, most asked first. |
| `import_conversations` | Imports past conversations from your previous support tool as history (`externalId`, customer `userId`, `status`, `metadata`, `messages` with `role`, `body` and `createdAt`). Idempotent by `externalId`, at most 100 conversations and 2,000 messages a call. Never triggers AI replies, push or emails. Owner or admin, Pro. See [Switch](https://conciergesdk.com/docs/migrate.md#conversation-history). |
| `export_customer_data` | Everything stored for one end user (by `customerId`, or `appId` and `userId`) as JSON, for a GDPR or CCPA access request. Owner or admin. |
| `delete_customer_data` | Permanently deletes all data for one end user, photos included. Owner or admin, never with a read only token, and `confirm` must repeat the `customerId` (or `userId`). |
| `list_incidents` | [Version incidents](https://conciergesdk.com/docs/incidents.md): spikes of similar tickets from one app version, with counts, first and last seen, and the auto reply. |
| `get_incident` | One incident with its newest tickets, OS and build. |
| `set_incident_auto_reply` | The one reply affected users get (open conversations now, new matching tickets as they arrive), or `null` to stop. Pro. |
| `resolve_incident` | Closes an incident; with `fixedInVersion` and `notify` every affected conversation hears it is fixed. `dismiss` for a false alarm. |

## Prompts

- **`triage`** (`/mcp__concierge__triage` in Claude Code): open conversations grouped by urgency, patterns across them, and drafted replies. Nothing is sent without your approval.
- **`build-knowledge`** (`/mcp__concierge__build-knowledge app_...` in your app's repo, optional focus): reads your screens, settings, paywall, flags, errors and strings, proposes missing `registerScreen` calls, shows the articles it plans, then writes them as drafts. See [Knowledge autopilot](https://conciergesdk.com/docs/knowledge.md#build-it-from-your-codebase-mcp).
- **`fix-and-reply`** (`/mcp__concierge__fix-and-reply cnv_...`): finds the bug in your current repo from the report, breadcrumbs and device info, fixes and tests it, then replies and sets the status.

## Resources

`concierge://conversation/{id}` is a Markdown transcript of a conversation. The listing shows the 50 most recent conversations, and ids autocomplete.

## Security notes

- Requests from a browser `Origin` are refused unless it's localhost or on your allow list. CLI agents send no origin and are fine.
- Every request checks the key or token and the person's org membership.
- Anything a customer wrote (messages, names, metadata, breadcrumbs, tool results) reaches the agent inside `<customer_message>` or `<customer_data>` blocks, escaped, with a standing rule that it is data and never instructions. `grant_promo`, `send_card`, `upsert_knowledge` and `call_client_tool` are marked destructive, so clients ask before running them.
- `grant_promo` follows your workspace comp policy: owners and admins only by default, at most 31 days, 20 grants a day and 2 per customer per 30 days, and an optional entitlement list. Change it under Offers, Limits.
- Each person gets 120 MCP requests a minute, and at most 3 `call_client_tool` waits at once.
- OAuth access tokens are short lived JWTs (one hour) issued only for this MCP server; refresh tokens last 30 days.
