Skip to main content
Docs navigation

API keys & the bridge

Every agent you connect authenticates with one API key. This page is that key's whole life. For picking a client and pasting its config, see Connect a CLI agent.

emisar console · LLM agents
Keys grouped by the member who owns them — status, last call, expiry, and per-agent rotate and revoke.

What an MCP key is#

An MCP key is the credential an LLM agent presents to reach emisar's tool API. It rides as Authorization: Bearer emk-… on every call to the MCP endpoint. Keys start with emk- followed by a short public identifier; the secret itself is hashed the moment it's created and is never stored or shown again.

  • Shown once. The full secret appears exactly once, at creation (and again at rotation). Copy it into the client's config then — emisar keeps only the hash, so a lost key is replaced, never recovered.
  • Identity, not authority. A key is an identity, an optional expiry, and audit attribution — nothing more.
  • One key per connection. Each connected client gets its own key, so you can see and revoke agents one at a time. Cloud clients that connect over OAuth follow the same idea.

A key has no scope of its own#

An MCP key carries no scope of its own — there is no per-key runner list or permission dial to set, and none to forget. What a connected agent can do is decided by two things that live on the member who minted the key, not on the key:

  • Which runners it can reach. Exactly the runners the member who minted it can reach. A member is scoped to runner groups on the Team page; the key inherits that scope, and emisar resolves it from that member on every call, not once at mint time.
  • What it may run there. Your account's policies and the approvals they require — the same gate a human dispatch passes. A key authenticates as a fixed api_client role: it can discover the catalog, dispatch actions, and read run state, but it can't approve its own gated requests or edit the policy that gates them. The member's own role — owner, admin, operator — does not transfer; an owner's key and an operator's key have the same capability.

So scoping the person scopes their agents. To limit what an agent can touch, narrow the member who owns its key on the Team page — you don't tune keys one by one. Remove that member's access entirely and every key they issued stops resolving to any runner, so an unbound key is never usable.

Minting a key#

Keys are minted from the LLM agents page in the console — the same place you connect and revoke agents. Pick the client you're connecting and emisar mints a key for it on the spot; the secret drops into a pre-filled config snippet you copy into that client. Cloud clients like Claude.ai and ChatGPT get no pasted key at all — they connect over OAuth and mint their backing key only after you approve the connection.

  • Minted on demand, per client. A manual key is minted only when you open that client's snippet (or approve its installer), not eagerly — so the agents list stays the real set of connections, not a drawer of unused secrets.
  • Operator and above. Connecting an agent — minting a key — needs an operator, admin, or owner role. A viewer can see the agents list but can't mint. Rotating and revoking are admin or owner.

Rotating a key#

Rotating swaps a key's secret without downtime. From an agent's row, Rotate mints a successor that inherits the old key's name, scope, and kind but carries a new secret and a fresh expiry. Both keys work through the overlap: the old one keeps authenticating until the successor's first successful call, which retires the old key automatically. Update the client's config with the new secret any time in that window — nothing breaks while you do.

  • Copy the new secret now. Like the original, it's shown once. If you lose it before the client swaps, rotate again.
  • The old key is still yours to kill. If you'd rather not wait out the overlap, revoke the old key by hand — rotation doesn't take that away.
  • Expiring keys rotate themselves. A key inside its last 7 days auto-rotates through the bridge: the bridge prepares a successor, proposes it, and promotes it only after emisar confirms the exact new key — so a long-running local agent crosses its own expiry untouched. This needs the bridge's credential directory to be writable and persistent; OAuth and hand-supplied bearer tokens don't use it.

Revoking a key#

Revoke is the kill switch, and it's terminal. The moment you confirm, the key is dead and the connected client gets a 401 on its very next call. Reach for it when a key leaks, an agent misbehaves, or a connection is finished. It asks you to type the agent's name first, because it can't be undone.

A key that suddenly returns 401.
Start on the Agents page. If it expired, the bridge may have already rotated it — look for a live successor. If it was revoked, that's terminal: reconnect the client to mint a new key. If the whole bridge is below the supported version, it fails at connect rather than per call — update it (below).

Audit-export tokens are a different kind#

Not every token emisar issues is an MCP key. An audit-export token is a separate kind, minted from the Audit page, that does one thing: read your account's events from the /api/audit stream. It can't list runners, discover the catalog, or run an action — present it to the MCP endpoint and emisar rejects it as the wrong key kind. An MCP key is the mirror image: it drives tools but can't read the audit stream. Keeping them distinct means a log-shipping credential sitting in your SIEM config can never be turned into one that dispatches actions. The export setup is in The audit trail & SIEM export.

The emisar-mcp bridge#

Local LLM clients — Claude Desktop, Cursor, Claude Code, the Gemini and Codex CLIs — speak MCP over stdio, not HTTP. emisar-mcp is the small bridge that connects them: it forwards each JSON-RPC frame from the client's stdin to POST /api/mcp/rpc on the control plane and writes the response back to stdout, carrying your key in the Authorization header.

  • The bridge is deliberately thin. It owns transport correctness only — line framing, request-id correlation, response validation. Every tool, its input schema, and its result come from the control plane; the bridge doesn't interpret them. The one exception is signed dispatch, where your Ed25519 signing key stays on your machine and never reaches emisar. New capabilities land in the control plane, so the bridge rarely has to change.
  • Cloud clients skip it. Claude.ai and ChatGPT connect to /api/mcp/rpc directly over OAuth — no binary to install, nothing to keep updated. That's why a cloud agent shows no bridge version.
  • It won't leak your key. The bridge refuses to send your Bearer key over cleartext http:// to a non-loopback host, which would put the key on the wire in plaintext. Use https; a loopback dev endpoint is the only exception.

Keeping the bridge supported#

emisar supports the emisar-mcp bridge from a declared minimum version (0.3.0 today) upward. The bridge reports its version on every request, so both the console and the control plane know whether a given agent's bridge is current, merely outdated, or below the floor.

  • Below the minimum reads "unsupported." On the Agents page, a key whose bridge is below the floor shows a rose unsupported status in place of its usual activity, so a version-blocked client reads as blocked, not merely quiet. When the control plane enforces the floor, it also refuses that bridge at connect with an error naming the minimum and pointing here, so your LLM surfaces "update the bridge" rather than a cryptic failure. A version emisar can't parse — or a cloud client with no bridge at all — is never blocked.
  • Outdated but usable keeps working. A bridge above the minimum but behind the recommended version runs normally; the console shows an amber version chip as a nudge, nothing more.
  • Updating is re-running the installer. It replaces the binary in place — your client configs and key are untouched.
shell
# check the installed bridge version
$ emisar-mcp --version

# update to the latest — replaces /usr/local/bin/emisar-mcp in place
$ curl -sSL https://emisar.dev/install-mcp.sh | sudo bash