Dein MCP-Server sollte nicht mit einem geteilten API-Schlüssel laufen

ai-agents

Stell dir einen Beitrag auf deinem Feedback-Board vor, der über Nacht in die Spalte Declined gewandert ist. Laut Verlauf war es das Projekt.

Niemand im Team erinnert sich daran. Es war der Assistent von irgendwem, der mit dem MCP-Schlüssel läuft, den alle von derselben Einstellungsseite kopiert haben. Wessen Assistent es war, findest du erst nach einer Runde Nachrichten heraus.

So funktionieren die meisten MCP-Server heute, weil sie das Modell der API-Schlüssel übernehmen. In Kookee habe ich es gerade durch persönliche Tokens ersetzt. Hier steht, warum der geteilte Schlüssel nicht trägt, was ich stattdessen gebaut habe und welche Details sich als wichtig herausgestellt haben.

Ein geteilter Schlüssel ist eine Gewohnheit aus der API-Welt

Ein API-Schlüssel identifiziert eine Anwendung. Eine App, ein Schlüssel, und der Schlüssel kann nur, was die App braucht, bei der öffentlichen API von Kookee also veröffentlichte Inhalte lesen. Für Anwendungen passt dieses Modell.

Ein MCP-Server verbindet etwas anderes: den Assistenten einer Person, der in ihrem Auftrag handelt und Dinge ändert. Gib ihm einen Projektschlüssel, und drei Dinge gehen gleichzeitig kaputt.

Niemand war es. Jede Änderung wird dem Projekt zugeschrieben. Bekommt ein Beitrag eine neue Kategorie, eine neue Zuständigkeit oder eine Antwort, siehst du nicht, wessen Assistent das war oder ob es ein Mensch selbst getan hat.

Jeder darf alles. Ein Schlüssel heißt eine Zugriffsstufe. Wer seinen Assistenten nur Feedback zusammenfassen lassen wollte, hält einen Schlüssel in der Hand, der veröffentlichen kann.

Einer einzelnen Person kannst du ihn nicht entziehen. Verlässt jemand das Team, sperrst du seinen Assistenten nur aus, indem du den Schlüssel rotierst, und legst damit das Setup aller anderen lahm. In der Praxis rotiert ihn deshalb niemand.

Und ein Schlüssel, den alle kopieren, landet überall: in Dotfiles, in einer committeten .mcp.json, auf einem Screenshot in einem Support-Thread.

Was ich gebaut habe: ein Token pro Person und Projekt

Jedes Mitglied erstellt jetzt auf der MCP-Server-Seite des Projekts sein eigenes Token. Das Token handelt als dieses Mitglied, mit seiner Rolle und seinen Rechten, die bei jeder Anfrage geprüft werden, und jede Änderung, die es vornimmt, wird unter dem Namen des Mitglieds festgehalten.

Dialog „MCP-Token erstellen“: ein Tokenname und die Wahl zwischen „Nur lesen“, standardmäßig ausgewählt, und „Lesen und schreiben“

„Nur lesen“ ist voreingestellt. Schreibzugriff kostet einen bewussten zweiten Klick.

Sechs Details, die darüber entscheiden, ob es funktioniert

1. Nur lesen heißt: Die Schreib-Tools gibt es gar nicht

Die naheliegende Umsetzung weist ein Schreib-Tool erst ab, wenn es aufgerufen wird. Damit wird es aber trotzdem angeboten: Das Modell sieht update_feedback in seiner Tool-Liste, plant eine ganze Triage darum herum und scheitert im letzten Schritt an einem Fehler, den es dann zu umgehen versucht.

Deshalb registriert eine Sitzung mit Nur-Lese-Zugriff die Schreib-Tools gar nicht erst, und ihre Server-Anweisungen lassen jeden Schreib-Workflow weg und erklären stattdessen, wie man Schreibzugriff bekommt. In groben Zügen:

TypeScript
registerReadTools(server, auth);
if (auth.token.canWrite) {
  registerWriteTools(server, auth);
}

Der Assistent kann dem Nutzer dann klar sagen, dass sein Token nur lesen darf, statt zu improvisieren.

2. Prüfe bei jeder Anfrage die Person hinter dem Token

Ein Token überdauert den Grund, aus dem es ausgestellt wurde. Die Prüfung endet deshalb nicht bei „der Hash stimmt“: Sie lädt das Mitglied hinter dem Token und prüft, ob sein Konto aktiv ist, ob das Projekt nicht gesperrt ist und ob die Person noch Mitglied ist.

TypeScript
if (!plain.startsWith('kkm_')) return fail('not_mcp_token');
const token = await findTokenByHash(sha256(plain));
if (!token) return fail('not_found');
if (!token.user.isActive) return fail('inactive');
if (token.project.bannedAt) return fail('banned');
if (!(await isMember(token.projectId, token.userId))) return fail('not_member');

Wird ein Mitglied entfernt oder gesperrt, werden auch seine Tokens gelöscht, in derselben Transaktion wie die Mitgliedschaft. Die Prüfung bei jeder Anfrage sperrt die Person ohnehin schon aus. Das Löschen der Zeilen sorgt dafür, dass die Token-Liste stimmt.

3. Speichere einen Hash, zeig das Token einmal, gib ihm ein Präfix

Gespeichert wird nur ein SHA-256-Hash des Tokens. Den Klartext zeigt einmalig der Dialog, in dem es erstellt wird, danach zeigt die Liste nur noch die letzten vier Zeichen.

Das Präfix erfüllt zwei Aufgaben. kkm_ unterscheidet sich vom kk_ eines API-Schlüssels, sodass ein Secret-Scanner oder ein Mensch erkennt, welche Art geleakt ist. Außerdem kann der Server so einen nützlichen Fehler zurückgeben: Ein API-Schlüssel, der an den MCP-Endpunkt geht, bekommt MCP_PERSONAL_TOKEN_REQUIRED, und die Person weiß genau, was zu tun ist. Jeder andere Fehler bekommt ein einheitliches MCP_TOKEN_INVALID, damit niemand durch Ausprobieren herausfindet, warum ein Token abgelehnt wurde. Der genaue Grund landet nur im Server-Log.

4. Zeig die Zuordnung dort, wo Menschen hinschauen

Im Audit-Log geht Verantwortung unter, weil dort niemand nachsieht. Kookee schreibt für jede Änderung über MCP einen Eintrag, aber das Entscheidende ist sichtbar: Jeder Schreibzugriff speichert die ID des Tokens beim Aktivitätseintrag oder Kommentar, den er erzeugt, und das Dashboard markiert diese mit über MCP.

Ein Feedback-Beitrag, den ein Assistent nach Planned verschoben, zugewiesen und beantwortet hat, jede Änderung mit „über MCP“ markiert

Das Verlaufs-Tool, das der Assistent liest, meldet dasselbe Kennzeichen. Die Frage „Wer hat das nach Planned verschoben, und war es ein Assistent?“ hat also von beiden Seiten eine Antwort. Die öffentliche Kommentar-API entfernt es: Besucher sehen eine Antwort vom Team, nie eine Token-ID.

5. Begrenze Schreibzugriffe pro Person und Tool

Ein außer Kontrolle geratener Agent, der eine fehlschlagende Änderung immer wieder versucht, schafft Hunderte Aufrufe pro Minute. Ein Schreiblimit pro Projekt würde die Teammitglieder aussperren, die nichts falsch gemacht haben, und ein Limit pro Token umgeht man, indem man ein zweites Token erstellt.

Deshalb sind Schreib-Tools pro Mitglied und pro Tool begrenzt: 60 Aufrufe pro Minute für update_feedback und separat 60 für create_comment. Das projektweite Zeitfenster für MCP ist außerdem von dem der öffentlichen API getrennt, sodass ein Agent mit vielen parallelen Aufrufen nicht das Rate Limit der Website auslöst, die deine Kunden sehen.

6. Halte die geteilte Konfiguration frei von Secrets

Ein Team will eine gemeinsame .mcp.json im Repository, damit alle dasselbe Setup haben. Mit persönlichen Tokens darf darin keins stehen. Stattdessen liest die Datei das Token aus der Umgebung der jeweiligen Person:

JSON
{
  "mcpServers": {
    "kookee": {
      "type": "http",
      "url": "https://api.kookee.dev/api/mcp",
      "headers": { "Authorization": "Bearer ${KOOKEE_MCP_TOKEN}" }
    }
  }
}

Jedes Teammitglied setzt KOOKEE_MCP_TOKEN auf sein eigenes Token. Der Befehl, den das Dashboard für Claude Code anzeigt, macht dasselbe mit $KOOKEE_MCP_TOKEN, außer direkt nachdem du ein Token erstellt hast: Dann setzt er dieses Token für dich ein.

Wenn du einen MCP-Server betreibst

  • Stell Zugangsdaten pro Person und pro Workspace aus. Ein MCP-Client ist der Agent einer Person, keine Anwendung.

  • Mach Nur-Lesen zur Voreinstellung und registriere keine Tools, die ein Token nicht nutzen darf.

  • Prüfe die Person hinter dem Token bei jeder Anfrage neu: Konto, Mitgliedschaft, Sperre.

  • Speichere Hashes, zeig Tokens nur einmal und gib ihnen ein eigenes Präfix.

  • Unterscheide „falsche Art von Zugangsdaten“ von „ungültig“ und verrate nicht mehr als das.

  • Speichere das Token bei jeder Änderung und zeig es dort, wo Menschen den Verlauf lesen.

  • Begrenze Schreibzugriffe pro Person und pro Tool, getrennt von deiner öffentlichen API.

  • Halte geteilte Client-Konfigurationen mit Umgebungsvariablen frei von Secrets.


Der MCP-Server von Kookee arbeitet jetzt mit persönlichen Tokens, und Assistenten können dein Feedback-Board lesen und sortieren. Die Einrichtung beschreibt der Artikel Inhalte mit einem KI-Assistenten verwalten (MCP), die neuen Tools der Artikel Feedback mit einem KI-Assistenten sichten.