Worum es geht
Modelle kannst du im Hub von Hand pflegen. Du kannst sie aber auch von einem KI-Client ändern lassen, zum Beispiel von Claude. Dafür hat der Hub einen MCP-Server. MCP (Model Context Protocol) ist ein Standard, über den ein KI-Client Werkzeuge (Tools) eines Servers aufruft.
Der KI-Client schreibt dabei nicht einfach drauflos. Jede Änderung läuft durch dieselbe Schleuse: beschreiben, prüfen, planen, von dir freigeben lassen, einmal anwenden.
Drei Begriffe kommen auf dieser Seite immer wieder vor:
- bindet alle folgenden Aufrufe an ein CDMS
- gehört dem Benutzer, der ihn anlegt
- gilt 30 Minuten
- beschreibt, was es geben soll
- symbolische
refs statt IDs - ergänzt nur; ändern und löschen nur über
migrations
- der eingefrorene Plan
- wird freigegeben und dann angewendet
Die Werkzeuge
Der MCP-Server bietet 15 Tools. Die meisten lesen nur.
| Tool | macht | ändert etwas? |
|---|---|---|
cdms_list_targets | listet die CDMS-Systeme, die du sehen darfst | nein |
cdms_get_target | Metadaten eines CDMS | nein |
cdms_get_capabilities | erlaubte Feld-, Beziehungs-, Regel-, Modell- und Endpunkttypen, Namensregeln, Systemfelder | nein |
cdms_get_structure | Ordner mit IDs und Pfaden | nein |
cdms_get_schema | das vollständige aktuelle Schema | nein |
cdms_get_element | ein einzelnes Element mit seinem Blueprint-Schlüssel | nein |
cdms_create_operation_context | eröffnet eine Änderung an einem CDMS | nein, öffnet nur die Klammer |
cdms_get_operation_context | Status und Ziel einer Änderung | nein |
cdms_validate_blueprint | prüft einen Blueprint | nein |
cdms_plan_changes | prüft und berechnet, was sich ändern würde | nein |
cdms_create_change_set | friert den Plan als Change Set ein | nein |
cdms_approve_change_set | gibt das Change Set frei | nur den Status |
cdms_apply_change_set | schreibt die Änderung in den Hub | ja |
cdms_get_change_set_status | Status und Ergebnisse | nein |
cdms_rollback_change_set | nimmt ein angewendetes Change Set zurück | ja |
Ein CDMS ist im Hub ein Modul. Seine ID ist die cdmsId, mit der alle Tools arbeiten. Dieselben Informationen gibt es auch als lesbare MCP-Ressourcen unter cdms://targets/{cdmsId}/….
Der Weg einer Änderung
-
KI-ClientLesenWelche CDMS gibt es, was ist erlaubt, wie sieht das Schema heute aus?
-
MCP-ServerKlammer öffnenDarf der Benutzer dieses CDMS sehen?↳ nein
CDMS_TARGET_NOT_FOUND -
MCP-ServerValidierenIst der Blueprint gültig, passen Ziel und Klammer zusammen?↳ nein Liste aller Fehler und Warnungen, Blueprint korrigieren
-
MCP-ServerPlanenWas wird angelegt, was bleibt, was ist destruktiv?↳ nein
BLUEPRINT_INVALID -
BenutzerFreigebenHast du den Plan gesehen und ausdrücklich zugestimmt? Bei destruktiven Änderungen: Hast du sie gesondert bestätigt, und hast du die Rolle dafür?↳ nein Change Set bleibt in
PENDING_APPROVALliegen, bei destruktiven Änderungen mitDESTRUCTIVE_CHANGE_BLOCKED -
MCP-ServerAnwendenIst es freigegeben, unverändert, gerade als einziges auf diesem CDMS unterwegs, und ist das Schema noch so wie beim Planen?↳ nein
CHANGE_SET_NOT_APPROVED,BLUEPRINT_INVALID,DESTRUCTIVE_CHANGE_BLOCKEDoderSCHEMA_VERSION_CONFLICT, nichts geschrieben - Das Modell im Hub ist geändert
So sieht das als Gespräch zwischen den Beteiligten aus:
sequenceDiagram
actor B as Benutzer
participant K as KI-Client
participant M as MCP-Server (Hub)
participant DB as Hub-Datenbank
K->>M: cdms_list_targets, cdms_get_capabilities, cdms_get_schema
M-->>K: Ziele, Regeln, Ist-Schema
K->>M: cdms_create_operation_context(cdmsId)
M-->>K: operationId (30 min gültig)
K->>M: cdms_validate_blueprint(operationId, blueprint)
M-->>K: valid, errors, warnings
K->>M: cdms_plan_changes(operationId, blueprint)
M-->>K: planId, summary, changes, destructiveChanges
K->>M: cdms_create_change_set(operationId, planId)
M-->>K: changeSetId, PENDING_APPROVAL
K->>B: zeigt den Plan
B-->>K: "Ja, anwenden"
K->>M: cdms_approve_change_set(operationId, changeSetId, confirmDestructive)
M-->>K: APPROVED
K->>M: cdms_apply_change_set(operationId, changeSetId, idempotencyKey)
M->>DB: Ist-Schema neu lesen und vergleichen
M->>DB: alle Änderungen in einer Transaktion
M-->>K: APPLIED + Ergebnis je Element
Der Blueprint
Der Blueprint ist ein JSON-Dokument. Es beschreibt Ordner, Enums, Modelle, Felder, Beziehungen und Zugriffsfilter so, wie sie nach der Änderung da sein sollen.
{
"operationId": "4c2b…",
"blueprint": {
"blueprintVersion": "1",
"target": { "cdmsId": "a7f5…", "expectedCdmsName": "CVC Backend" },
"folders": [
{ "ref": "folder.crm", "name": "CRM",
"existingFolderId": "2a7f…" } ← vorhandener Ordner
],
"enums": [
{ "ref": "enum.status", "folderRef": "folder.crm",
"name": "CUSTOMER_STATUS",
"values": [ { "key": "ACTIVE" }, { "key": "BLOCKED" } ] }
],
"models": [
{ "ref": "model.customer", "folderRef": "folder.crm",
"name": "Customer", ← gibt es schon
"fields": [
{ "ref": "field.customer.status", "name": "status",
"type": "ENUM", "enumRef": "enum.status",
"required": true }
] }
]
}
}{
"valid": true,
"errors": [],
"warnings": [
{ "code": "BLUEPRINT_INVALID", "element": "model.customer",
"message": "Das Modell '/crm/Customer' existiert bereits … wird wiederverwendet …" }
],
"models": [ { "ref": "model.customer", "existingId": "9b1…" } ], …
}Vier Regeln solltest du kennen:
- Symbolische Namen statt IDs. Elemente verweisen über ihre
refaufeinander, zum BeispielenumRef: "enum.status". Die IDs vergibt der Hub. - Ergänzen, nie still löschen. Was schon existiert, erkennt der Hub an seinem Pfad (
/crm/Customer) und verwendet es wieder. Was im Blueprint fehlt, bleibt unverändert. Ändern oder Löschen von Vorhandenem geht nur ausdrücklich über den Abschnittmigrations. - Was du benutzt, deklarierst du mit. Willst du ein vorhandenes Modell oder Enum referenzieren, steht es im Blueprint, sonst kommt
BLUEPRINT_REFERENCE_NOT_FOUND. Für vorhandene Ordner gibst duexistingFolderIdan. - Eine Beziehung ist ein Feldpaar. Beide Seiten stehen als
RELATION-Feld im Blueprint und zeigen überinverseFieldRefaufeinander, siehe Beide Seiten einer Beziehung. - Keine Umgebung angeben. Ein CDMS hat keine Umgebung: Sein Modell gilt in Entwicklung, Test und Produktion gleich, die Umgebungen unterscheiden sich nur in ihren Daten.
target.expectedEnvironmentist deshalb ein Fehler.
Der Hub prüft streng: Ein unbekannter JSON-Schlüssel ist schon ein Fehler. Inhaltliche Befunde sammelt er aber vollständig, damit der KI-Client alles in einem Durchgang korrigieren kann.
| Ergebnis | Beispiele |
|---|---|
Fehler (valid: false) | Name verletzt die Namensregel oder ist reserviert (id, _createdOn …), ref doppelt, Feld gibt es schon im Basismodell, Gegenseite einer Beziehung fehlt, unbekannter Feld- oder Regeltyp, Zyklus in der Vererbung, target.expectedEnvironment gesetzt |
Warnung (valid: true) | Element existiert schon und wird wiederverwendet, ein abweichender Wert an einem vorhandenen Element wird nicht übernommen, Angaben wie displayName werden ignoriert, leerer Blueprint |
Ausdrückliche Änderungen: migrations
Alles, was Vorhandenes verändert oder entfernt, steht als eigener Eintrag unter migrations:
| Art | Beispiele |
|---|---|
| umbauen | RENAME, MOVE, SET_OPTIONS, SET_FIELD_TYPE, SET_RULE |
| entfernen | REMOVE_RULE, REMOVE_RECURSION, REMOVE_ENDPOINT, REMOVE_ROLE, REMOVE_ENUM_VALUE |
| löschen | DELETE_FIELD, DELETE_MODEL, DELETE_ENUM, DELETE_FOLDER |
Jede Migration gilt im Plan als destruktiv. Ebenso destruktiv sind ein kaskadierendes Löschen über eine Beziehung, ein entfernter Zugriffsfilter und ein Modell, das auditiert wird. Ein Change Set mit destruktiven Änderungen gibst du nur mit einer eigenen Rolle frei und nur, wenn du diese Änderungen gesondert bestätigst, siehe weiter unten „Destruktive Änderungen freigeben“.
Der Plan
cdms_plan_changes validiert noch einmal und vergleicht dann den Blueprint mit dem Ist-Schema. Heraus kommt ein Plan:
{ "operationId": "4c2b…", "blueprint": { … wie oben … } }{
"planId": "af72…",
"summary": { "createCount": 2, "updateCount": 0, "deleteCount": 0,
"unchangedCount": 2, "destructiveChangeCount": 0 },
"changes": [
{ "type": "UNCHANGED", "elementType": "folder", "path": "/crm" },
{ "type": "CREATE", "elementType": "enum", "path": "/crm/CUSTOMER_STATUS" },
{ "type": "UNCHANGED", "elementType": "model", "path": "/crm/Customer" },
{ "type": "CREATE", "elementType": "field", "path": "/crm/Customer#status" }
],
"destructiveChanges": [],
"blueprintHash": "269a…"
}Jede Zeile in changes hat einen Typ: CREATE, UPDATE, DELETE oder UNCHANGED. Unter destructiveChanges stehen im Klartext alle Änderungen, die etwas zerstören können. Genau diesen Plan zeigt der KI-Client dir, bevor du freigibst. Ein Plan lebt 30 Minuten.
Das Change Set und seine Zustände
cdms_create_change_set nimmt nur eine planId, keinen neuen Blueprint. Was freigegeben wird, ist also genau das, was geplant wurde.
stateDiagram-v2
[*] --> PENDING_APPROVAL: cdms_create_change_set
PENDING_APPROVAL --> APPROVED: cdms_approve_change_set
APPROVED --> APPLIED: cdms_apply_change_set, Erfolg
APPROVED --> FAILED: cdms_apply_change_set, Fehler
APPLIED --> ROLLED_BACK: cdms_rollback_change_set
FAILED --> [*]
ROLLED_BACK --> [*]
APPLIED --> [*]
| Zustand | heißt | wie weiter |
|---|---|---|
PENDING_APPROVAL | geplant, wartet auf dich | freigeben, oder einfach liegen lassen |
APPROVED | freigegeben | anwenden |
APPLIED | im Hub geschrieben | fertig, oder zurücknehmen |
FAILED | Anwenden ist gescheitert, nichts wurde geschrieben | neu planen |
ROLLED_BACK | zurückgenommen | neu planen |
FAILED und ROLLED_BACK sind Endzustände. Ein Change Set wird nie ein zweites Mal versucht. Ein Werkzeug zum Verwerfen gibt es nicht: Ein Change Set, das du nicht freigibst, bleibt in PENDING_APPROVAL und wird nie angewendet.
Destruktive Änderungen freigeben
Stehen im Plan Einträge unter destructiveChanges, reicht ein einfaches „Ja“ nicht:
| Rolle für destruktive Änderungen | confirmDestructive | Was passiert |
|---|---|---|
| ja | true | APPROVED |
| ja | fehlt oder false | DESTRUCTIVE_CHANGE_BLOCKED (destructive-change-not-confirmed), bleibt PENDING_APPROVAL |
| nein | egal | DESTRUCTIVE_CHANGE_BLOCKED (destructive-change-requires-role), bleibt PENDING_APPROVAL |
Der KI-Client zeigt dir die destruktiven Änderungen einzeln und fragt gesondert danach. Nur wenn du genau diesen Teil bestätigst, sendet er confirmDestructive: true. Die Rolle vergibt dein Administrator; welche es ist, legt der Betreiber des Hubs fest. Beim Anwenden prüft der Server die Rolle noch einmal. Wurde sie dir inzwischen entzogen, wird nicht geschrieben.
Ein Change Set ohne destruktive Änderungen braucht weder die Rolle noch die Bestätigung.
Genau einmal anwenden
-
1KI-Client→MCP-Serverruft
cdms_apply_change_setmitoperationId,changeSetIdund einemidempotencyKey -
2MCP-ServerIst das Change Set
APPROVED? -
3MCP-Serverschon
APPLIEDoderROLLED_BACK→CHANGE_SET_ALREADY_APPLIED; noch nicht freigegeben →CHANGE_SET_NOT_APPROVED -
4MCP-ServerWurde derselbe
idempotencyKeyschon für dieses Change Set benutzt?Dann kommt das frühere Ergebnis zurück. Nichts wird doppelt geschrieben. Das gilt nur für dich: Wer einen fremden Schlüssel wiederholt, bekommtOPERATION_CONTEXT_NOT_FOUND. -
5MCP-ServerLäuft auf diesem CDMS gerade ein anderes Anwenden oder Zurücknehmen?
-
6MCP-Serverja →
SCHEMA_VERSION_CONFLICT(cdms-change-in-progress), nichts geschrieben. Später erneut versuchen. -
7MCP-ServerPasst der eingefrorene Blueprint noch zu seinem
blueprintHash? Hat das Change Set destruktive Änderungen, hast du noch die Rolle dafür? -
8MCP-Servernein →
BLUEPRINT_INVALIDbzw.DESTRUCTIVE_CHANGE_BLOCKED, nichts geschrieben -
9MCP-Server→Datenbankliest das Ist-Schema neu und vergleicht es mit dem Stand beim Planen
-
10MCP-ServerBasis geändert →
SCHEMA_VERSION_CONFLICT, nichts geschrieben -
11MCP-Server→Datenbankschreibt alle Änderungen in der Reihenfolge des Plans, in einer TransaktionGeschrieben wird über die normalen System-Layer des Hubs, also mit Rechteprüfung und Hooks wie bei jedem anderen Schreibzugriff.
-
12MCP-Serverprüft das Ergebnis: jedes Element genau einmal da, jede
refaufgelöst -
13MCP-Serverirgendein Fehler → alles wird verworfen, Status
FAILED -
14MCP-Server→KI-ClientCommit, Status
APPLIED, Ergebnis je Element (CREATED,REUSED, …)
Wenn sich die Basis inzwischen geändert hat
Zwischen Planen und Anwenden kann jemand anders das Modell im Hub geändert haben. Deshalb liest der Server vor dem Schreiben das Schema neu ein:
| Was hat sich seit dem Planen geändert? | Was passiert |
|---|---|
| nichts | wird angewendet |
| ein geplantes neues Element existiert inzwischen | SCHEMA_VERSION_CONFLICT, neu planen |
| ein wiederverwendetes Element ist verschwunden | SCHEMA_VERSION_CONFLICT, neu planen |
| ein alter Wert stimmt nicht mehr (Länge, Default, Auditierung …) | SCHEMA_VERSION_CONFLICT, neu planen |
| das Ziel einer Migration ist geändert oder weg | SCHEMA_VERSION_CONFLICT, neu planen |
Zurücknehmen
cdms_rollback_change_set geht nur aus APPLIED. Der Rollback macht in umgekehrter Reihenfolge rückgängig, was das Change Set angelegt oder geändert hat. Elemente, die nur wiederverwendet wurden (REUSED), bleiben unberührt. Auch der Rollback läuft in einer Transaktion, und wie beim Anwenden läuft auf einem CDMS immer nur einer. Die Rolle für destruktive Änderungen braucht er nicht: Er nimmt nur zurück, was dasselbe Change Set getan hat.
Ein Change Set, das ein Modell (DELETE_MODEL) oder ein Enum (DELETE_ENUM) gelöscht hat, lässt sich nicht zurücknehmen. Der Server lehnt den Rollback dann von vornherein ab.
Der Operation Context: ein Ziel, eine halbe Stunde
-
MCP-ServerKontext bekanntGibt es die
operationId, ist sie jünger als 30 Minuten, und hast du sie angelegt?↳ neinOPERATION_CONTEXT_NOT_FOUND -
MCP-Servergleiches ZielZeigt der Aufruf, auch
target.cdmsIdim Blueprint, auf dasselbe CDMS?↳ neinCDMS_TARGET_MISMATCH -
MCP-Servergleiche KlammerGehören Plan und Change Set zu dieser
operationId?↳ nein Fehler, nichts passiert - Der Aufruf wirkt auf genau das CDMS, das beim Öffnen gewählt wurde
Die operationId ist kein Schlüssel, den man weitergeben kann. Kennt jemand anders sie, etwa aus einem geteilten Chatverlauf, kann er damit nichts freigeben, anwenden oder zurücknehmen: Für ihn gibt es diesen Kontext nicht.
Der Kontext gilt 30 Minuten ab dem Anlegen. Freigeben, Anwenden und Zurücknehmen brauchen ihn. Die ganze Kette vom Öffnen bis zum Anwenden muss also in dieser Zeit laufen. Ist die Zeit um, öffnest du eine neue Klammer und planst neu.
Beim Öffnen akzeptiert der Server nur die UUID des CDMS. Die lesenden Tools nehmen auch den genauen Modulnamen. Mehrere Treffer ergeben CDMS_TARGET_AMBIGUOUS, es gibt keine ungefähre Suche.
Den KI-Client anbinden
Dein Zugang
Die Zugangsdaten bekommst du von uns bei der Registrierung bzw. mit dem Vertragsabschluss:
| Angabe | Bedeutung |
|---|---|
| Basis-URL | wohin der KI-Client verbindet, z. B. https://<host>/api/mcp |
| Client-ID | die Kennung deines Zugangs. Sie ist öffentlich und darf ins Repository |
| Callback-Port | der lokale Port, auf dem der KI-Client nach dem Login die Antwort entgegennimmt |
Ein Client-Secret gibt es nicht. Der Zugang ist ein öffentlicher OAuth-Client und wird mit PKCE abgesichert: Der KI-Client erzeugt für jede Anmeldung einen Einmal-Schlüssel, den nur er kennt. Ein Werkzeug auf deinem Rechner könnte ein Secret ohnehin nicht geheim halten.
Was du sehen und ändern darfst, entscheidet dein Benutzerkonto, nicht der Zugang. Ein CDMS, auf das du keine Rechte hast, taucht in cdms_list_targets nicht auf.
Claude Code
claude mcp add --transport http --client-id <ihre-client-id> --callback-port <port> \
codamai-cdms https://<host>/api/mcp
--transport httpsteht vor Name und URL. Ohne ihn legt Claude Code einen lokalen stdio-Server an.--client-idund--callback-portsind Pflicht. Ohne sie versucht Claude Code, sich selbst als Client zu registrieren, und das lehnt der Hub ab.- Kein
--client-secret, siehe oben. - Mit
--scope projectlandet der Eintrag in der.mcp.jsondes Projekts und gilt für das ganze Team, mit--scope userfür alle deine Projekte.
Danach meldest du dich in einer Claude-Code-Sitzung an:
-
1Entwickler→KI-Clienttippt
/mcpund wählt beicodamai-cdmsAuthenticate -
2KI-Client→Browseröffnet die Anmeldeseite
-
3Benutzer→Keycloakmeldet sich mit seinem Konto an
-
4Keycloak→KI-Clientschickt den Code an
http://localhost:<port>/callback -
5KI-Client→Keycloaktauscht den Code gegen ein Token und speichert es lokal
-
6KI-Client→MCP-ServerServer ist
connected, die Tools stehen bereit
Im selben Menü /mcp siehst du die Tools des Servers und kannst dich wieder abmelden, etwa wenn du den Benutzer wechselst oder sich deine Rechte geändert haben. Verwalten kannst du den Eintrag auch von der Kommandozeile:
claude mcp list # alle Server mit Verbindungsstatus
claude mcp get codamai-cdms # Details zu einem Server
claude mcp remove codamai-cdms # Server entfernen
Statt claude mcp add kannst du den Server auch direkt in die .mcp.json im Projekt schreiben:
{
"mcpServers": {
"codamai-cdms": {
"type": "http",
"url": "https://<host>/api/mcp",
"oauth": { "clientId": "<ihre-client-id>", "callbackPort": <port> }
}
}
}
Trage keinen Authorization-Header ein. Das Token holt sich der Client selbst über die Anmeldung, und ein fester Header wäre ein eingecheckter Zugangsschlüssel.
Weniger Rückfragen, aber nicht beim Schreiben
Claude Code fragt vor jedem Tool-Aufruf nach. Die lesenden Tools kannst du in .claude/settings.json dauerhaft erlauben. Die Namen haben das Schema mcp__<servername>__<toolname>:
{
"permissions": {
"allow": [
"mcp__codamai-cdms__cdms_list_targets",
"mcp__codamai-cdms__cdms_get_target",
"mcp__codamai-cdms__cdms_get_capabilities",
"mcp__codamai-cdms__cdms_get_structure",
"mcp__codamai-cdms__cdms_get_schema"
]
}
}
cdms_approve_change_set, cdms_apply_change_set und cdms_rollback_change_set gehören nie in diese Liste. Die Rückfrage vor diesen Aufrufen ist deine Freigabe.
Andere KI-Clients
Wann: empfohlen
claude mcp add wie oben, mit Client-ID und Callback-Port.
Ergebnis: Anmeldung per /mcp
Wann: der Client beherrscht Streamable HTTP und OAuth
Transport Streamable HTTP auf https://<host>/api/mcp. Die Anmeldung findet der Client selbst: Auf den ersten Aufruf ohne Token antwortet der Server mit 401 und nennt im Header WWW-Authenticate das Discovery-Dokument (/.well-known/oauth-protected-resource/api/mcp). Als client_id trägst du deine Client-ID ein, als Redirect-URI http://localhost:<port>/callback mit deinem Callback-Port.
Wann: der Client kann sich nur selbst registrieren
Manche Clients, etwa benutzerdefinierte Connectoren in Claude Desktop, melden sich nur über eine dynamische Client-Registrierung an. Die bietet der Hub nicht an. Nutze für solche Fälle Claude Code oder einen Client, dem du Client-ID und Callback-Port mitgeben kannst.
Rechte
Alles, was der MCP-Server liest und schreibt, läuft über dieselben Rechteprüfungen wie die REST-API. Ein CDMS, das du nicht sehen darfst, gibt es für dich nicht: Die Antwort ist CDMS_TARGET_NOT_FOUND, siehe Unsichtbar ist 404.
Und danach?
Das Anwenden ändert nur die Modelle im Hub. Deine Anwendung merkt davon erst etwas beim nächsten Build:
-
1MCP-Server→HubChange Set
APPLIED: Modell im Hub geändert -
2Build→Hubholt beim nächsten Build die Metadaten neu
-
3Generatorerzeugt den Code aus dem neuen Modell
-
4CDMS→Datenbankpasst beim Start das Datenbankschema an, je nach MigrationsmodusErgebnis: Die Anwendung kennt das neue Feld
Wie der Build die Metadaten holt, steht unter Codegenerierung im Build.