Claude Code, Cursor und Codex anbinden
Der MCP-Server (mcp/server.py) unterstützt zwei Transporte, gewählt über
die Umgebungsvariable COMPASS_MCP_TRANSPORT auf dem Server-Prozess
(Standard stdio). Welcher es ist, ändert, woher der Schlüssel kommt — das
falsch zu machen bringt entweder ein verwirrendes 401 oder, im alten
abgelösten Verhalten, still die Daten eines anderen Mandanten.
stdio — Claude Code und Cursor, lokal
Der Client startet mcp/server.py selbst als Unterprozess. Der Schlüssel
lebt in der Prozess-Umgebung, die man ihm mitgibt — es gibt keinen
separaten Aufrufer, dessen Kopfzeile man lesen könnte, deshalb sind
COMPASS_KEY_RO/COMPASS_KEY_RW in diesem Modus die einzige Quelle.
Claude Code (.mcp.json) und Cursor (.cursor/mcp.json) nutzen dieselbe
Form:
{
"mcpServers": {
"compass": {
"command": "/pfad/zu/mcp/.venv/bin/python",
"args": ["/pfad/zu/mcp/server.py"],
"env": {
"COMPASS_CORE_URL": "http://localhost:18080",
"COMPASS_KEY_RO": "<Lese-Schlüssel>",
"COMPASS_KEY_RW": "<Schreib-Schlüssel>"
}
}
}
}
COMPASS_CORE_URL— Basis-URL des Kerns, ohne/api/v1(Standardhttp://localhost:18080).COMPASS_KEY_RO— für lesende Werkzeuge; fehlt er, fallen lesende Werkzeuge aufCOMPASS_KEY_RWzurück.COMPASS_KEY_RW— für schreibende Werkzeuge; fehlt er, wird ein Schreib-Aufruf clientseitig verweigert statt ohne Schlüssel abgesetzt.
Codex
Codex CLI liest MCP-Server aus ~/.codex/config.toml, als Tabelle
[mcp_servers.<name>] mit derselben command/args/env-Form:
[mcp_servers.compass]
command = "/pfad/zu/mcp/.venv/bin/python"
args = ["/pfad/zu/mcp/server.py"]
[mcp_servers.compass.env]
COMPASS_CORE_URL = "http://localhost:18080"
COMPASS_KEY_RO = "<Lese-Schlüssel>"
COMPASS_KEY_RW = "<Schreib-Schlüssel>"
streamable-http — ein laufender Container
Auf https://<host>/mcp zeigen (lokal http://localhost:18082/mcp). In
diesem Modus läuft der ausgerollte MCP, und er nimmt zwei Zugangsmittel —
genau eines je Anfrage, nie beide zusammen.
Der erste Weg: OAuth, ohne Schlüssel
Ein Client, der OAuth spricht, braucht keinen Schlüssel in seiner Konfiguration. Bei Claude Code sind es zwei Schritte:
claude mcp add --transport http compass https://<host>/mcp
Danach im Chat /mcp → „Authenticate": der Browser öffnet die
Zustimmungsseite, ihr meldet euch an und gebt den Zugriff frei. Bis dahin
sagt claude mcp list „Needs authentication" — das ist kein Fehler, sondern
der Zustand vor der Zustimmung.
Die Sitzung hängt dann an dem Menschen, der zugestimmt hat, mit dessen
Rolle und Mandant. Ist auf dem Server kein Signierschlüssel für die Tür
gesetzt (COMPASS_MCP_OAUTH_SIGNING_KEY), antworten die OAuth-Pfade mit
503 und nennen die fehlende Variable — sie fallen nicht still auf offenen
Zugriff zurück. Der Schlüsselweg ist davon unberührt.
Der zweite Weg: Schlüssel als Kopfzeile
Mit Schlüssel statt OAuth, wenn der Agent als eine bestimmte Persona arbeiten soll (ihr Brief, ihre Grenzen) oder ohne Browser läuft — etwa auf einem Server. Ein Schlüssel kann an eine Persona gebunden sein, eine OAuth-Sitzung nie: sie gehört dem angemeldeten Menschen.
{
"mcpServers": {
"compass": {
"url": "http://localhost:18082/mcp",
"transport": "http",
"headers": { "X-Compass-Key": "<Schlüssel dieses Mandanten>" }
}
}
}
Auf diesem Weg ist der headers-Block Pflicht, nicht optional — wer
weder Schlüssel noch OAuth-Zugangsband mitbringt, kommt nicht durch, und wer
beides zugleich schickt, wird mit „Zugangsmittel sind mehrdeutig" abgewiesen.
Liegt eine echte eingehende HTTP-Anfrage vor, liest der Server den
Schlüssel ausschließlich aus deren eigener X-Compass-Key-Kopfzeile —
kein Rückfall auf einen im Prozess gesetzten Schlüssel, selbst wenn einer
zufällig gesetzt ist. Fehlt die Kopfzeile, wird der Aufruf verweigert,
bevor der Kern überhaupt kontaktiert wird:
{"status": 401, "detail": "X-Compass-Key fehlt in der Anfrage — kein Ruecksprung auf den Prozess-Schluessel."}
Das ist wichtig, weil derselbe Schlüssel beides bestimmt: welchen
Mandanten man sieht und was man darf (ro/rw/bruecke — siehe
Konzepte) — dieselbe Kopfzeile gilt für jedes Werkzeug,
lesend oder schreibend; der Kern entscheidet serverseitig, ob sie reicht.
Ohne Schlüssel verbinden — was ihr dabei seht
Clients, die OAuth sprechen, brauchen keinen Schlüssel in der Konfiguration. Sie schicken euch stattdessen einmalig in den Browser, und dort entscheidet ihr selbst.
Was auf der Seite steht:
- Wer fragt. Der Name, unter dem sich der Client angemeldet hat. Erkennt ihr ihn nicht, lehnt ab.
- Als wer ihr angemeldet seid. Eure Adresse, so wie COMPASS sie kennt.
- Mit welchem Projekt. Gehört ihr zu mehreren, ist die Wahl Pflicht und es ist nichts vorausgewählt — sonst klickt man die falsche Antwort durch. Gehört ihr zu genau einem, wird gar nicht gefragt.
- Was der Client darf, in ganzen Sätzen: lesen, eintragen, und was ausdrücklich nicht geht — Schlüssel sehen, Mitglieder ändern, etwas löschen. Ist euer Zugang nur lesend, steht das Eintragen dort unter „Nicht", mit dem Grund.
Dann zwei Knöpfe: Erlauben oder Ablehnen. Lehnt ihr ab, entsteht nichts, und der Client bleibt unverbunden.
Geht etwas schief, sagt die Seite welches von diesen vier Dingen es war, statt nur „Fehler":
| Was dasteht | Was es heißt |
|---|---|
| Anmeldung abgelaufen | Das Fenster stand zu lange offen. Nichts ist passiert — in eurem Client noch einmal auf „Verbinden". |
| Falsches Projekt | Das Konto, mit dem ihr angemeldet seid, hat keinen Zugriff auf dieses Projekt. Mit einem anderen Konto anmelden, oder oben ein anderes Projekt wählen. |
| Unbekannter Assistent | Das anfragende Programm konnte sich nicht ausweisen. COMPASS verbindet es nicht. Habt ihr es selbst eingerichtet, nehmt den Weg mit Schlüssel. |
| COMPASS antwortet gerade nicht | Es wurde nichts gespeichert und nichts verbunden. Noch einmal versuchen. |
Die Seite spricht Deutsch oder Englisch, je nachdem, was euer Browser verlangt; im Zweifel Deutsch.
Nach dem Verbinden wählt ihr im Chat die Persona, mit
persona_waehlen. Erst damit steht fest, als wer der Assistent schreibt
und was er sehen darf. Ohne Wahl bleibt alles so, wie es ohne sie war.
Welcher Transport für welchen Client
| Client | Typische Einrichtung | Transport | Schlüsselquelle |
|---|---|---|---|
| Claude Code, lokal | startet server.py selbst |
stdio |
Prozess-Umgebung |
| Cursor, lokal | startet server.py selbst |
stdio |
Prozess-Umgebung |
| Codex, lokal | startet server.py selbst |
stdio |
Prozess-Umgebung |
| Claude Code, gegen den ausgerollten Server | claude mcp add --transport http |
streamable-http |
OAuth, kein Schlüssel |
| Beliebiger Client, gegen einen geteilten/ausgerollten Server | verbindet über HTTP | streamable-http |
Kopfzeile je Anfrage oder OAuth |
Verbindung prüfen
Nach dem Verbinden ist ein lesendes Werkzeug ohne Abhängigkeit von vorhandenen Daten die günstigste Probe:
persona_list()
oder, aus der Shell, der entsprechende REST-Aufruf:
curl -sS "$COMPASS_URL/personas" -H "X-Compass-Key: $COMPASS_KEY"
Ein 401 hier bedeutet: Schlüssel oder Kopfzeile passen nicht zum
gewählten Transport — zuerst die Tabelle oben prüfen, bevor man annimmt,
der Server selbst sei nicht erreichbar.