Zum Inhalt springen

Claude und eigene MCP-Clients anbinden

NomOS ist ein governter MCP-Server. Claude Desktop, Claude Code oder ein eigener Client erhalten über OAuth dieselben belegten, raum-isolierten Antworten wie die Oberfläche.

Wer einen MCP-Client betreibt (Claude Desktop, Claude Code oder eine eigene Integration), kann NomOS direkt anbinden. NomOS stellt sich als MCP-Server bereit. Jede Antwort trägt Beleg, Owner und Gültigkeit und ist auf die Räume beschränkt, in denen die anfragende Person Mitglied ist.

Der Zugang läuft über dieselbe Governance wie das Web-UI: OPA-Zutritt pro Raum, harte Regel-Checks und ein Beleg für jede Frage, jede Prüfung und jeden Antrag. Es gibt keinen Seiteneingang am Regelwerk vorbei.

Externe Clients melden sich per OAuth (Authorization-Code mit PKCE/S256) gegen den Keycloak-Realm «nimbus» an. Der Ablauf ist standardisiert (RFC 9728) und funktioniert ohne vorab erstellten Token:

  1. Der Client ruft die Discovery ab: GET auf «/.well-known/oauth-protected-resource» (HTTP 200, JSON unten).
  2. Aus «authorization_servers» folgt der Client zum Keycloak-Realm «nimbus».
  3. Login + PKCE (S256): der Nutzer meldet sich bei Keycloak an, der Client tauscht den Code gegen einen Access-Token.
  4. Der Client ruft die MCP-Tools mit dem Header «Authorization: Bearer …» auf.

Discovery: GET https://<ihre-installation>/.well-known/oauth-protected-resource

{"resource":"https://<ihre-installation>/api/mcp",
"authorization_servers":["https://<ihre-installation>/auth/realms/nimbus"],
"scopes_supported":["openid","profile","email"],
"bearer_methods_supported":["header"]}

Ohne Token antwortet der Server mit einer 401-Challenge und einem «WWW-Authenticate»-Header. Dessen «resource_metadata» zeigt auf die Discovery. So findet ein konformer Client den Auth-Server selbst.

Die Registrierung allein bestätigt noch keine funktionierende Verbindung. Übernimm die angezeigte MCP-Adresse unverändert in den Client und prüfe sie mit einer echten Anfrage. Fehlt die öffentliche Konfiguration, muss die Plattformadministration zuerst die MCP-Adresse korrigieren.

401: POST https://<ihre-installation>/api/mcp

www-authenticate: Bearer error="invalid_token",
error_description="Authentication required",
resource_metadata="https://<ihre-installation>/.well-known/oauth-protected-resource/api/mcp"

Keycloak (Realm nimbus): https://<ihre-installation>/auth/realms/nimbus , PKCE S256, authorization_code.

In Claude (Web oder Desktop) unter «Settings → Connectors» einen «Custom Connector» anlegen und die URL «https://<ihre-installation>/api/mcp» eintragen. Die OAuth Client ID unter «Erweiterte Einstellungen» ist optional: Ohne sie registriert sich Claude selbst, und beim ersten Verbinden entscheidet dein Zustimmungsdialog. Mit einer festen ID nutzt Claude immer denselben Client, entweder die vorregistrierte ID «claude-desktop» oder (empfohlen, mit Audit-Trail und Widerruf) die Client-ID eines im Agenten-Bereich angelegten Per-User-Agenten. Das Geheimnis-Feld bleibt leer, denn der Client ist öffentlich und nutzt PKCE. Danach öffnet sich der Keycloak-Login.

Hinweis: Bei manchen Desktop-Versionen bleibt die Connector-Anmeldung nicht gespeichert. Fehlen die Werkzeuge nach einem Neustart, den Connector einmal neu verbinden.

Verbindungsdauer: Eine Verbindung hält bis zu 30 Tage ohne Nutzung, jede Nutzung verlängert sie. Erst danach musst du einmal neu verbinden.

Für Claude Code die MCP-URL mit «claude mcp add –transport http» eintragen. Claude Code meldet sich per OAuth an: Browser-Login, dann der Zustimmungsdialog. Ein Token oder Skript braucht es nicht.

Terminal

claude mcp add --transport http ainomos "https://<ihre-installation>/api/mcp"

Danach in der Claude-Code-Sitzung «/mcp» aufrufen und die Anmeldung für «ainomos» starten. Ist der Server verbunden, eine erste Frage stellen, etwa nach den eigenen Räumen: Claude ruft dann «list_rooms» auf.

Alternativ, wenn im Agenten-Bereich schon ein Per-User-Agent angelegt ist: Sein Detailbereich zeigt unter «Konfigurationsdatei» den fertigen Block mit gepinnter Client-ID. Claude Code schreibt die ID als «oauth.clientId». Cursors «auth.CLIENT_ID» wirkt dort nicht: Der Client registriert sich dann bei jedem Start neu und fragt erneut nach Zustimmung.

Hermes Agent (Nous Research) führt OAuth 2.1 mit PKCE selbst aus und verbindet sich deshalb in denselben drei Schritten wie Claude Code: kein Token zum Kopieren, kein Header zum Bearbeiten. Ab Hermes 0.20.4; die Befehle stammen aus «hermes mcp –help».

  1. Server hinzufügen: den Befehl im Terminal ausführen. Hermes schreibt den Eintrag nach «~/.hermes/config.yaml», registriert sich beim Authorization Server (RFC 7591) und öffnet den Browser-Login. Alternativ den Konfigurationsblock einfügen; er pinnt den vorregistrierten Client «hermes-agent» (ADR-038), statt einen neuen zu registrieren.

    hermes mcp add ainomos --url "https://<ihre-installation>/api/mcp" --auth oauth
  2. Anmelden: Der Keycloak-Login öffnet sich im Browser. Hat sich Hermes mit dem Befehl aus Schritt 1 selbst registriert, folgt der Zustimmungsdialog; mit dem vorregistrierten Client «hermes-agent» entfällt er. Öffnet sich kein Browser oder läuft der Login ab, «hermes mcp login ainomos» in einem frischen Terminal ausführen; es wartet fünf Minuten. Hermes speichert den Token in «~/.hermes/mcp-tokens/» und erneuert ihn selbst.

    hermes mcp login ainomos
  3. Prüfen: «hermes mcp test ainomos» listet die Werkzeuge; im Chat tragen sie alle das Präfix «mcp_ainomos_», zum Beispiel «mcp_ainomos_list_rooms».

    hermes mcp test ainomos

Der Konfigurationsblock, wenn du eine Datei dem Befehl vorziehst:

~/.hermes/config.yaml

mcp_servers:
ainomos:
url: "https://<ihre-installation>/api/mcp"
auth: oauth
oauth:
client_id: "hermes-agent"

Gegen diese Installation verifiziert: Discovery («/.well-known/oauth-protected-resource»), die 401-Challenge, die Keycloak-Metadaten («registration_endpoint» vorhanden, kein Client-ID-Metadata-Dokument, Hermes registriert sich also per RFC 7591) und der vorregistrierte Client «hermes-agent» mit Loopback-Redirects. Von dir zu bestätigen: der Browser-Rundlauf aus einem laufenden Hermes (Login, Zustimmung, «hermes mcp test»). Gegenüber Claude Code fehlt Hermes nur der Connector-Dialog, darum ist Schritt 1 ein Befehl oder eine Datei.

Skill, Härtung und die Werkzeuge, die Hermes zuerst aufrufen soll, stehen auf der Hermes-Hilfeseite:Hermes-Agent an NomOS anbinden

Devin for Terminal (der Befehl devin) verbindet sich wie Claude Code per OAuth mit PKCE, seine Konfigurationsdatei verwendet aber eigene Feldnamen: url, transport und oauthClientId. Die Form von Claude Code mit type und einem oauth-Block wirkt dort nicht. Trage die Client-ID deines Agenten aus dem Agentenbereich fest ein; ein Secret braucht es nicht. Live verifiziert gegen diese Installation am 24.09.2026.

  1. Lege die Datei an und ersetze den Platzhalter durch die Client-ID deines Agenten:

    ~/.config/devin/mcp_config.json

    {
    "mcpServers": {
    "ainomos": {
    "url": "https://<ihre-installation>/api/mcp",
    "transport": "http",
    "oauthClientId": "<client-id-of-your-agent>"
    }
    }
    }
  2. Melde dich an. Der Browser öffnet den Login: Wähle das Konto, dessen Räume Devin sehen soll.

    devin mcp login ainomos
  3. Prüfe, dass der Server verbunden ist und seine Werkzeuge geladen sind. Starte eine Devin-Sitzung neu, die vor dem Login gestartet wurde:

    devin mcp list

Devin Desktop, der Editor, liest ~/.codeium/windsurf/mcp_config.json und nennt die Adresse serverUrl. Seine Dokumentation zeigt kein Feld für eine fest eingetragene OAuth-Client-ID; Devin würde sich deshalb selbst registrieren, und die Verbindung wartet auf die Freigabe im Agentenbereich. Diese Variante ist gegen diese Installation nicht verifiziert; bitte bestätige sie, bevor du dich darauf verlässt.

Ein Agent erreicht diesen Server nur über die Werkzeuge dieser Verbindung. Er darf sich kein Token selbst holen, zum Beispiel per curl mit fremden Zugangsdaten: Das umgeht deine Anmeldung, und die Aufrufe werden unter dem falschen Konto protokolliert.

Die Werkzeuge folgen dem Lebenszyklus eines Agenten: orientieren und entdecken, lesen, prüfen, beitragen, nachvollziehen. Die Katalog-Werkzeuge lesen nur. Beitragen heisst immer vorschlagen; Menschen entscheiden und übernehmen.

list_rooms()

Listet die Räume, in denen die anfragende Person Mitglied ist, mit einem kompakten Themen-Digest aus aktuellen governten Entscheiden. Entwürfe und abgelöste Entscheide zählen nicht; abgelehnte Vorschläge bleiben als bindende Negativ-Governance sichtbar. «hidden» bedeutet, dass der Entscheid-Digest für diese Identität nicht verfügbar ist. Es heisst nicht, dass der Raum ungeregelt ist.

search_brain_catalog(q?, room?, origin?, genre?, publisher?)

Durchsucht sichtbare Brain-Store-Angebote nach Freitext, Zielraum, Herkunft, Genre oder Publisher. Mit Zielraum liefert jedes Angebot seine aktuelle Beziehung und den nächsten zulässigen Schritt. Die Suche ist read-only.

get_brain_offer(offer_id, room?)

Liest ein sichtbares Angebot mit Publisher, Version, Datenklasse, Lizenz, typisierten Inhaltszählungen und repräsentativen Titeln. Private Brains bleiben unauffindbar; Decision-Inhalte folgen der Decision-Sichtbarkeit.

list_brain_relationships(room)

Zeigt für einen eigenen Raum direkte, geerbte, verfügbare und approval- oder acquisition-bezogene Zustände samt next_action. Das Werkzeug abonniert, beantragt und genehmigt nichts.

ask_brain(room, question)

Liefert eine governte Antwort mit Belegen und einer «run_id», auf demselben Pfad wie im Web-UI (Retrieval, Regel-Check, Beleg). «room» akzeptiert den Slug oder den Anzeigenamen eines eigenen Mitglieds-Raums. Für Folgefragen die «conversation_id» aus der vorherigen Antwort mitgeben: Der Verlauf fliesst als Kontext ein, und die Antwort liefert die «conversation_id» mit. Belege gelten weiterhin je Antwort.

list_decisions(room, status?, type?, mine?)

Geltende Entscheide strukturiert lesen (Schlüssel, Titel, Typ, Status), deterministisch und ohne Sprachmodell; mit mine=true nur die eigenen Vorschläge.

get_decision(room, key)

Voll-Detail eines Entscheids (Inhalt, Beziehungen, Herkunft). Beantwortet auch die Frage «Was wurde aus meinem Vorschlag?».

get_decision_neighbours(room, key)

Zeigt die typisierten Nachbarn eines Entscheids, einen Schritt weit: Nachbar-Titel und Art (kind), Relationstyp, Richtung, Effekt, Kanten-Status (active steuert, proposed ist inert), ob die Abruf-Priorität gepflegt ist, das Typ-Gewicht des antwortenden Raums, Abruf-Priorität und Provenienz. Raum-begrenzt, nur lesend.

get_passage(room, knowledge_object_id, version?)

Liest die Textstelle eines Wissenseintrags über denselben autorisierten Leser wie die Oberfläche: Titel, Textstelle, Quelldokument, Version, Gültigkeit, Owner und ob es Wissen (beratend) oder ein geltender Entscheid (bindend) ist. knowledge_object_id ist die ID aus einem ask_brain-Zitat; ein GOV:-Schlüssel führt zum Record des Entscheids. Dieselben Gates wie der Leser der Oberfläche: Raum-Mitgliedschaft, die Wissens-Sicht und bei einem Entscheid-Record zusätzlich die Entscheid-Sicht; alles andere heisst «nicht gefunden», ohne Titel. Der Leser führt keine Versionshistorie: er liefert die aktuelle Textstelle und nennt version_served neben version_requested, statt einen älteren Text zu rekonstruieren.

validate_action(room, action, content=&quot;&quot;, tool=&quot;&quot;)

Der Laufzeit-PDP für Agenten: legt eine geplante Aktion vor und erhält ein Urteil («allow», «caution», «require_approval» oder «block»), die geltenden bindenden Regeln, eine als Beratung markierte KI-Einschätzung und eine «evidence_id». Nennt die Aktion ein Werkzeug, «tool» mitgeben: Dann prüft das Urteil auch die Werkzeug-Regeln des Raums und nennt bei «require_approval» den Freigabe-Weg. «room» akzeptiert Slug oder Anzeigename.

request_brain_access(offer_id, room)

Beantragt als Owner des Zielraums den Zugriff auf ein geschütztes Corp Brain. Der Antrag schreibt einen Beleg, genehmigt nichts und erstellt kein Abo; ein bezeichneter menschlicher Publisher-Approver entscheidet im Posteingang.

propose_decision(room, type, title, context_md, options_md, recommendation_md)

Erfasst eine im Dialog anstehende Entscheidung als Entwurf im Raum (ADR, BDR oder SDR), mit Kontext, Optionen und Empfehlung. Der Entwurf wird nie automatisch bindend: Review und Freigabe macht der zuständige Entscheider in der Bibliothek; der Vorschlagende wird als Herkunft vermerkt. Pro Raum kann jede vorschlagende Identität nur eine begrenzte Zahl offener Entwürfe halten; die Raumdetails zeigen den Deckel und ob er vom Raum oder von der Installation stammt.

request_exception(room, policy_key, justification, scope?)

Läuft eine Aktion gegen eine Leitplanke, eine befristete Ausnahme beantragen. Genehmigen kann nur ein Entscheider des Raums (Raum-Owner, Governance-Owner oder Mandanten-Admin) in der Oberfläche; die Ausnahme gilt standardmässig 14 Tage. Harte Regeln haben keinen Ausnahmepfad.

propose_knowledge(room, title, body_md, summary, source_note)

Erarbeitetes Wissen als Entwurf ins Import-Review einreichen. Ins Gedächtnis gelangt es erst nach menschlicher Übernahme.

list_knowledge_proposals(room)

Zeigt die eigenen Wissens-Einreichungen im Raum über alle Status (Entwurf, übernommen, abgelehnt) mit Ziel-Raum. So sieht der Agent, was aus einem «propose_knowledge» wurde. Nur lesend, ohne Sprachmodell.

get_evidence(run_id)

Das vollständige Beleg-Bundle zu einer run_id abrufen, mit denselben Nachweisen, die auch die Oberfläche zeigt.

get_answer_relations(run_id)

Liest die Voraussetzungen und Widersprüche einer Antwort über ihre run_id: jede vom Governance-Block nachgezogene depends_on-Voraussetzung (samt dem ziehenden Entscheid) und jedes conflicts_with-Paar, als Daten statt als Prosa. Read-only, kein Sprachmodell; dieselbe Reichweite wie get_evidence.

Bewusst nicht vorhanden: Werkzeuge für Publisher-Entscheide, Brain-Abos, Administration oder Löschen. Maschinen lesen oder beantragen, Menschen entscheiden.

Der Raumzugriff wird über OPA anhand der Mitgliedschaft entschieden. Ein Aufruf auf einen fremden Raum scheitert mit demselben Entscheid wie im UI. Fragen, Prüfungen und Wissens-Vorschläge durchlaufen harte Checks: SEC-01 blockt Secrets. DAT-03 hält eine Antwort mit Personendaten in Räumen der gesperrten Datenklasse zurück und markiert Personendaten in geprüften Aktionen als «caution». Jede Frage, jede Prüfung und jeder Antrag erzeugt einen Beleg; reine Lesewerkzeuge schreiben keinen. Budget-Gates begrenzen den Verbrauch.

Der Ansatz ist bewusst kooperativ und belegt: Ein Agent erfährt, welche Regel greift und warum. So kann er seine Aktion anpassen, bevor er handelt.

  • 401 (Bearer, «invalid_token»): Meist ist der Token abgelaufen. Den OAuth-Login wiederholen (in Claude Code «/mcp», in Hermes «hermes mcp login ainomos») oder bei einem NHI-Agenten ein neues Token holen. Hält der Fehler trotz frischem Token an, ist eher die Installation falsch konfiguriert (Issuer/JWKS) als der Token schuld → Admin prüfen lassen.
  • 403 auf einen Raum: Der OPA-Entscheid verweigert den Zutritt. Erst die eigene Raum-Mitgliedschaft prüfen («list_rooms()» zeigt die erlaubten Räume). Meldet der Fehler «is closed to agents», ist der Raum für Agenten geschlossen und nur im Browser erreichbar; das ist eine Einstellung des Raums. Steht der Raum in «list_rooms()» und der Zugriff bleibt ohne diesen Hinweis verweigert, passen die Policy-Daten serverseitig nicht zusammen (Fehlkonfiguration) → dem Admin melden, statt weiter an der eigenen Berechtigung zu suchen.
  • Den «WWW-Authenticate»-Header und «resource_metadata» lesen. Ein konformer Client folgt ihnen automatisch zum Auth-Server.
  • Die Token-Claims prüfen («iss», «exp», «email»): falscher Issuer oder abgelaufenes «exp» sind die häufigsten Ursachen.
  • «Client not found» auf der Keycloak-Seite: die OAuth Client ID ist dem Realm unbekannt. Entweder die App sich selbst registrieren lassen (Self-Service: keine Client ID, der Zustimmungsdialog ist das Tor) oder eine existierende Client ID setzen: «claude-desktop», «cursor», «hermes-agent» oder die ID eines Agenten aus dem Agenten-Bereich. Eine erfundene oder vertippte ID scheitert immer so.