Documentation menu

MCP — Claude Desktop / ChatGPT integration

Wire Atrium's tool catalog into any MCP-aware AI host. Same tools as the in-app /admin/ai agent, role-tier-filtered per caller, hosted-confirmation contract for mutations, full audit.

Atrium speaks the Model Context Protocol so any MCP-aware host (Claude Desktop, ChatGPT Connectors, Cursor, custom agents) can list and call Atrium tools directly. The endpoint reuses the agent's tool catalog, filtered per caller's role.

Endpoint: POST https://atrium.ommax-intelligence.de/api/mcp — JSON-RPC 2.0 over HTTP. Auth: Authorization: Bearer atr_live_<token> (same Atrium API key as everywhere else).

Quick start — /admin/mcp

The in-app tutorial page does the boring parts for you:

  1. Sign in to Atrium → your avatar menu (top right) → MCP setup. Admins also have it as a tab in the admin nav; both land on /admin/mcp, which every signed-in user may open. The /admin prefix is a naming quirk, same as /admin/api-keys.
  2. Mint key for MCP — one click. Token is shown ONCE and is auto-pasted into the Claude Desktop config block + ChatGPT header snippet below it. The key inherits your current Atrium role at every call.
  3. Copy the Claude Desktop config (or the ChatGPT instructions) and follow the per-host setup below.
  4. The page also lists, in real time, the exact tool surface your host will see — driven by the same tools/list call the host will make.

Why we built this

The choice we explicitly made: don't replace the in-app agent, share its catalog. A typed-tool layer (with confirm-flow, audit log, RBAC, structured I/O) is the security architecture; MCP is "the same surface, addressable from elsewhere". An MCP server without typed tools would lose the confirm gate, granular audit, and LLM ergonomics — those properties are what we wanted to preserve.

The hand-rolled implementation is ~250 LOC (no SDK). MCP's HTTP transport is plain JSON-RPC 2.0 with three meaningful methods (initialize, tools/list, tools/call); a 200kb SDK would have added more weight than value.

Role-tiered catalog

Each tool declares a minRole. The MCP server filters tools/list per caller. RBAC is re-checked at tools/call time so a name discovered out-of-band can't bypass the catalog.

Caller's roleSeesPractical use
user (default)list_apps, get_app, list_my_groups, request_app_accessSelf-service: "what apps exist", "which groups am I in", "request access to X"
adminAll of the above + group/app curation, search_users, action / secret listings, runtime health, Zitadel URL discoveryDay-to-day directory management
super_adminEverything — incl. set_app_public, mint_api_key, Zitadel target / signing-key toolsSensitive infra

See AI agent for the full catalog.

Mutations are open — host confirms

Unlike the first MCP cut (read-only), the current endpoint does allow mutate tools. Justification: every MCP-aware client (Claude Desktop, ChatGPT) renders a per-tool-call approval prompt before execution, so the host IS the confirm UX. We additionally write AtriumAiAuditLog rows tagged conversationId = "mcp:<apiKeyId>" so MCP-driven calls show up alongside in-app activity.

If you don't trust your host's confirm UX, use a user-tier API key — only request_app_access is mutating at that tier, and it goes to a PENDING queue an admin reviews on /admin/access-requests anyway.

Wire into Claude Desktop

Claude Desktop loads MCP servers from ~/Library/Application Support/Claude/claude_desktop_config.json on macOS (or %APPDATA%\Claude\claude_desktop_config.json on Windows). Merge this in, swap the placeholder for your token, then quit + relaunch Claude Desktop:

{
  "mcpServers": {
    "atrium": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://atrium.ommax-intelligence.de/api/mcp",
        "--header",
        "Authorization:Bearer atr_live_YOUR_KEY_HERE"
      ]
    }
  }
}

mcp-remote is a small shim — Claude Desktop's built-in transport is stdio, the shim bridges to a remote HTTP endpoint and injects the auth header. No manual install (npx fetches it).

Wire into ChatGPT

ChatGPT Connectors (Plus / Team / Enterprise required):

  1. Settings → ConnectorsAdd MCP server.
  2. URL: https://atrium.ommax-intelligence.de/api/mcp.
  3. Authentication: Custom header, value: Authorization: Bearer atr_live_YOUR_KEY_HERE.
  4. Save. ChatGPT probes tools/list and surfaces Atrium tools when you @-mention the connector.

Audit + observability

  • Per-call audit: AtriumAiAuditLog row on every tools/call, with conversationId = "mcp:<apiKeyId>". Same kind of row the in-app agent writes — filterable by source.
  • Per-key usage: the existing AtriumApiKeyUse audit applies. /admin/api-keys shows last-used timestamps + per-key call paths.
  • Role drift: if you remove someone's atrium:admin role in Zitadel, their MCP key catalogs down to user-tier on next call automatically (the role check is evaluated against the live rolesSnapshot, not the key creation snapshot).

Limitations / honest caveats

  • No SSE push. The endpoint is request/response only. We don't emit notifications/tools/list_changed when the catalog evolves; hosts re-probe on connect, which is enough for the catalog churn we actually have (occasional new tools, never within a session).
  • Plunk credit cost when ATRIUM_EMAIL_BCC is set. If a tool fires an email trigger (e.g. agent grants someone a role → ROLE_GRANTED notification), the BCC mechanism doubles the Plunk send count. Intended for the test phase only.
  • An unscoped API key inherits its creator's roles via rolesSnapshot on AtriumUser, with a legacy super-admin fallback when that snapshot is empty (users who haven't signed in since the column was added). Scoped keys have landed and are a different model: they carry no roles at all, and /api/mcp rejects them outright, because a catalog tiered by "no roles" would be meaningless. The legacy fallback is slated for removal, not something to build on. See API keys.
  • tools/list_changed notification isn't emitted; if you add a new tool, restart Claude Desktop to pick it up.
  • Read-only-mode flag isn't exposed — the catalog is what determines mutate availability. If you want a hard read-only key for an integration, mint it from a regular-user account; the user-tier catalog has no destructive tools (only request_app_access, which routes through admin approval).