docs(rfc): MCP-Server für KI-Agenten — RFC 001, Plan-Knoten #ai.mcp, D93 (festgelegt, nichts gebaut)

Ein Node-Paket, das die headless-Module des Frontends unverändert
importiert (kein zweiter Parser, D14), den Plan als Ressource und Tools
anbietet (Baum, Warnungen, günstigster Pfad, Knoten-Verben) und
Änderungen als Zeilen-Diff mit Basisversion schreibt (D76); Leitplanken:
keine beschädigte Notation, kein [^] von einem Agenten. Geteiltes Dokument
und Git zugleich: nach jedem Agenten-Zug committet der Server über
pull-doc --git-commit (D88). Entscheidungen der Multiple-Choice-Runde in
RFC §11 und D93.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-09-02 09:25:02 +02:00
co-authored by Claude Fable 5
parent aa6ca85bdf
commit 1e9cc93cb9
6 changed files with 704 additions and 0 deletions
+1
View File
@@ -219,6 +219,7 @@ anderen Domain läge das Web-Verzeichnis in `…/subs-ssl/<name>/`.)
- `docs/DECISIONS.md` — Design-Entscheidungen mit Begründung - `docs/DECISIONS.md` — Design-Entscheidungen mit Begründung
- `docs/ROADMAP.md` — Mermaid-Plugin, Taiga-Integration, Tenzu - `docs/ROADMAP.md` — Mermaid-Plugin, Taiga-Integration, Tenzu
- `docs/TASKS.md` — offene Aufgaben (Checkboxen) - `docs/TASKS.md` — offene Aufgaben (Checkboxen)
- `docs/rfc/` — Vorschläge, die mehrere Teile des Repos berühren und vor dem Bauen abgewogen werden (001: ein MCP-Server für KI-Agenten)
- `docs/brand/BRAND.md` — Logo, Wortbild, Anwendungsregeln - `docs/brand/BRAND.md` — Logo, Wortbild, Anwendungsregeln
- `docs/design/` — Design-Herleitung der Marke - `docs/design/` — Design-Herleitung der Marke
- `CLAUDE.md` — Projektkontext für Claude Code - `CLAUDE.md` — Projektkontext für Claude Code
+1
View File
@@ -212,6 +212,7 @@ directory would be `…/subs-ssl/<name>/`.)
- `docs/DECISIONS.md` — design decisions with rationale - `docs/DECISIONS.md` — design decisions with rationale
- `docs/ROADMAP.md` — Mermaid plugin, Taiga integration, Tenzu - `docs/ROADMAP.md` — Mermaid plugin, Taiga integration, Tenzu
- `docs/TASKS.md` — open tasks (checkboxes) - `docs/TASKS.md` — open tasks (checkboxes)
- `docs/rfc/` — proposals that span several parts of the repo, weighed before they are built (001: an MCP server for AI agents)
- `docs/brand/BRAND.md` — logo, wordmark, usage rules - `docs/brand/BRAND.md` — logo, wordmark, usage rules
- `docs/design/` — design derivation of the brand - `docs/design/` — design derivation of the brand
- `CLAUDE.md` — project context for Claude Code - `CLAUDE.md` — project context for Claude Code
+4
View File
@@ -17,6 +17,10 @@ the git history of `docs/examples/werkbaum.werkbaum`. A day can therefore carry
a link without having a note (someone forgot to write one) — but never the a link without having a note (someone forgot to write one) — but never the
reverse. reverse.
## 2026-09-02
- An MCP server for AI agents recorded as planned: a Node package that reuses the one JS parser, hands the plan to Claude Code and other agents as resource and tools, writes changes as conflict-safe line diffs, and mirrors the shared document into git after every agent write — spelled out in an RFC before it is built
## 2026-08-28 ## 2026-08-28
- Folding or unfolding a node keeps it in place on screen now: the scroll position follows the layout shift, so the clicked node no longer jumps away - Folding or unfolding a node keeps it in place on screen now: the scroll position follows the layout shift, so the clicked node no longer jumps away
+65
View File
@@ -8673,3 +8673,68 @@ sagen weiterhin, wo sich einer lohnt. Und ein Doppelklick öffnet und
schließt in einem Zug — hinnehmbar, auf Knoten doppelklickt niemand schließt in einem Zug — hinnehmbar, auf Knoten doppelklickt niemand
absichtlich. absichtlich.
## D93 — MCP-Server für KI-Agenten: als RFC vorgelegt, nichts gebaut
Gewünscht: ein Plan für ein MCP-Modul, mit dem KI-Agenten außerhalb des
Editors (Claude Code, IDE-Agenten, Desktop-Assistenten) einen Werkbaum-Plan
lesen, befragen und ändern können. Der Plan liegt als **RFC** in
`docs/rfc/001-mcp-server.md` — die erste Datei dieser Art; ein RFC ist die
Form für etwas, das über mehrere Teile des Repos reicht und Alternativen
abwägt, bevor eine Zeile Code entsteht (die Taiga-Anlage in D91 hat
dasselbe als Nachtragskette getan). Im mitgelieferten Plan steht es als
`#ai.mcp` mit Teilpaketen.
**Was das RFC vorschlägt, in einem Satz:** Ein Node-Paket `mcp/`, das die
headless-Module des Frontends (`parser.js`, `model.js`, `live.js`)
**unverändert importiert** — kein zweiter Parser, D14 —, den Plan als
Ressource anbietet, mit Tools den Baum, die Warnungen und den günstigsten
Pfad liefert und Änderungen als Zeilen-Diff mit Basisversion und Prüfsumme
schreibt (D76), mit zwei Leitplanken: keine beschädigte Notation, kein
`[^]` von einem Agenten (D30).
**Schon entschieden, weil es aus vorhandenen Entscheidungen folgt:**
- **Kein Server im Kotlin-Backend** (`#ai.mcp.kotlin` als `[-]`): Jedes
nützliche Tool braucht den Parser, und ein Kotlin-Parser ist genau die
zweite Grammatik, die D14 verbietet.
- **Kein Modell im Server, kein API-Schlüssel:** Das Modell sitzt im Host;
`#ai.key` und `#ai.dialog` bleiben eigene Knoten. Der MCP-Server ist die
**Gegenrichtung** zu `#ai.dialog`, nicht sein Ersatz.
- **Kein Geheimnis über MCP:** Master-Passwort (D76-Nachtrag 6) und
Taiga-Token (D91) laufen nie durch ein Transkript — deshalb weder die
Dokumentenliste noch Taiga-Tools in der ersten Fassung.
- **Kein Backend-Umbau:** Die vorhandene REST-API genügt.
- **Geteiltes Dokument und Git zugleich** (Nutzer-Wunsch, mitten in der
Ausarbeitung): Der Agent arbeitet auf dem Server-Dokument, und nach
jedem seiner Schreibvorgänge committet der Server den Serverstand über
das vorhandene `pull-doc --git-commit` (D88) in eine Spiegel-Datei —
der Server ist die Quelle, Git das Archiv (`#ai.mcp.mirror`). Die
Umkehrung, Git als Quelle, bleibt bei `#col.git`.
**Entschieden in einer Multiple-Choice-Runde** (RFC §11, jede Frage mit
der Option „zunächst offen halten“ — keine wurde gewählt):
- **`@modelcontextprotocol/sdk` darf herein** — die erste
Laufzeit-Abhängigkeit des Repos, isoliert im MCP-Paket; der gebündelte
Editor bleibt abhängigkeitsfrei (D11/D19/D20). Die Rückfrage, die
CLAUDE.md verlangt, ist damit gestellt und beantwortet.
- **Nur Struktur-Warnungen blockieren** einen Agenten-Schreibvorgang
(`mixedGate`, `unknownStatus`, `descStray`, `duplicateId`); inhaltliche
Widersprüche (`sizeConflict`, `assigneeOverload`, `unknownDep`, …)
werden geschrieben und gemeldet. Vorgeschlagen war die strenge Fassung
(jede neue Warnung blockiert, D59-Linie); der Nutzer hat entschieden,
dass ein Agent in Zwischenschritten arbeiten darf und ein Mensch die
bernsteinfarbenen Marken sieht — wie bei einer eigenen Änderung.
- **Der Anzeigename ist der Host-Name aus dem Handshake** („Claude Code“),
Rückfall „Agent“.
- **Knoten-Verben kommen dazu** (`add_node`, `move_node`, `set_size`,
`remove_node`) — nicht nur `apply_ops`. Die Text→Text-Regeln dafür
liegen als `frontend/src/edit.js` neben `setFoldMark` und
`expandShortIds`, headless und getestet; das Paket verdrahtet nur.
Damit kennt weiterhin genau eine Stelle das Zeilenformat.
- **Die Baum-Serialisierung bleibt im MCP-Paket**, **verteilt wird nur
aus dem Repo** (`.mcp.json`, kein npm).
Offen bleibt der Transport über Streamable HTTP (nach `#col.live.owner`)
und die Form der Urheber-Angabe im `pull-doc`-Commit. Die
Umsetzungsreihenfolge steht im RFC; gebaut ist nichts.
+71
View File
@@ -225,6 +225,16 @@
- [?] #ai.dialog: Edit the plan in a dialogue with the AI (L) - [?] #ai.dialog: Edit the plan in a dialogue with the AI (L)
- [?] #ai.key: Bring your own API key, no server in between (M) - [?] #ai.key: Bring your own API key, no server in between (M)
- [?] #ai.ground: llms.md grounds the model in the notation (S) - [?] #ai.ground: llms.md grounds the model in the notation (S)
- [ ] #ai.mcp: An MCP server hands the plan to outside agents (XL) :#not.llms %% docs/rfc/001-mcp-server.md
- [ ] #ai.mcp.node: A Node package that reuses the one JS parser (S) %% no second parser, D14
- [ ] #ai.mcp.read: Resources: the agent guide, a file, a server document (S)
- [ ] #ai.mcp.inspect: Tools that answer what the diagram answers (M) %% tree, warnings, cheapest path
- [ ] #ai.mcp.write: Writes go out as line diffs and are refused on conflict (M) :#col.live.diff
- [ ] #ai.mcp.guard: Guard rails: no broken notation, no [^] from an agent (S)
- [ ] #ai.mcp.verbs: Node verbs: add, move, size, remove — the rules live in the frontend (M) :#ai.mcp.write
- [ ] #ai.mcp.mirror: The shared document mirrors into git after every agent write (S) :#col.git.pull
+ [?] #ai.mcp.http: A remote transport over streamable HTTP (M)
- [-] #ai.mcp.kotlin: A server inside the backend (L) %% would need a Kotlin parser, D14
--- ---
#wb #wb
@@ -1285,3 +1295,64 @@
The system prompt is essentially llms.md (D43): the agent guide already The system prompt is essentially llms.md (D43): the agent guide already
explains the notation for reading and writing, so the model needs no explains the notation for reading and writing, so the model needs no
training — just the document and the request. training — just the document and the request.
#ai.mcp
The other direction of the AI integration: not an LLM inside the editor,
but the plan handed to agents that live outside it — Claude Code, IDE
agents, desktop assistants — over the Model Context Protocol. Specified in
docs/rfc/001-mcp-server.md before anything is built.
#ai.mcp.node
The server is a small Node package that imports the frontend's headless
modules (parser, model, live diff) as they are. That keeps D14 intact: the
one JS parser stays the only parser, and agents get the same warnings,
effective statuses and cheapest path the diagram shows.
#ai.mcp.read
What an agent can read: llms.md as the notation guide, a local .werkbaum
file, and a shared document on a Werkbaum backend by its URL — the same
three sources a person has.
#ai.mcp.inspect
Tools that return the parsed tree with ids, statuses (own and effective),
sizes (given and assumed), tags and dependencies, the warnings the editor
would show, and the cheapest path with its stations and per-person load —
so the agent reasons on the model instead of re-implementing the SPEC.
#ai.mcp.write
Edits leave the server as the same line diffs the editor sends: against a
base version with checksum, refused on a real conflict, visible to every
open editor with "changed by" naming the agent. A local file is written
only if it is unchanged since it was read.
#ai.mcp.guard
Guard rails on every write: the result must not break the notation (no new
mixedGate, unknownStatus, descStray or duplicateId), and an agent never
writes [^] — "in production" is a deploy's statement (D30), not a model's.
Content warnings like a size conflict are written and reported, so a human
sees them as the amber mark. Both rails can be lifted per call, never silently.
#ai.mcp.verbs
Convenience for the model: add a node under a parent, move a subtree, set a
size, remove or discard a node — by id, not by line index, which an agent
gets wrong. The text-to-text rules live in frontend/src/edit.js beside the
fold and short-id helpers, so exactly one place keeps knowing the line format.
#ai.mcp.mirror
Both at once: the agent works on the shared document, where the people
are, and the same plan stays under git control. After every agent write
the server runs pull-doc --git-commit for a configured mirror file, naming
the agent in the commit — the server is the source, git the archive. The
reverse (git as the source) is a different node, #col.git.
#ai.mcp.http
Streamable HTTP as a second transport, so a hosted agent can reach the
server without a local process. Optional: stdio covers the local agents
first, and remote access raises the authentication question the backend
has not answered yet (#col.live.owner).
#ai.mcp.kotlin
Building the server into the Spring backend would put it next to the
documents — but every useful tool needs the parser, and a Kotlin parser is
exactly the second grammar D14 forbids. Discarded in favour of the Node
package.
+562
View File
@@ -0,0 +1,562 @@
# RFC 001 — MCP-Server: Werkbaum für KI-Agenten außerhalb des Editors
| | |
|---|---|
| Status | **Abgestimmt** (2026-09-02, Entscheidungen in §11 und D93) — nichts gebaut |
| Plan-Knoten | `#ai.mcp` in `docs/examples/werkbaum.werkbaum` |
| Entscheidung | D93 in `docs/DECISIONS.md` (Verweis auf dieses RFC) |
| Berührt | `frontend/src/*` (headless-Module, lesend), neues Paket `mcp/`, `tools/pull-doc` (Aufrufer), README, `.mcp.json` |
| Berührt nicht | Notation (SPEC), `llms.md`, Backend-Code, Deploy-Skripte |
## 1. Zusammenfassung
Werkbaum bekommt einen **MCP-Server** (Model Context Protocol): ein kleines
Node-Paket, das KI-Agenten außerhalb des Editors — Claude Code, IDE-Agenten,
Desktop-Assistenten — einen Werkbaum-Plan als **Ressource** (Text) und als
**Werkzeuge** (Baum, Warnungen, günstigster Pfad, Schreiben) anbietet.
Der Server **parst nicht selbst**: Er importiert die headless-Module des
Frontends (`parser.js`, `model.js`, `live.js`) unverändert. Damit bleibt D14
gewahrt — es gibt weiterhin genau einen Parser — und ein Agent bekommt
wörtlich dieselben Warnungen, effektiven Status und Stationen, die das
Diagramm zeigt.
Geschrieben wird über denselben Weg wie aus dem Editor: als **Zeilen-Diff
gegen eine Basisversion mit Prüfsumme** (D76). Fremde Änderungen werden nie
überschrieben, ein Konflikt kommt als Fehler zurück, und in jedem offenen
Editor steht „geändert von: *Claude Code*“. Zwei Leitplanken gelten auf
jedem Schreibvorgang: **keine beschädigte Notation** (Struktur-Warnungen
blockieren, inhaltliche werden gemeldet), **kein `[^]` von einem Agenten**.
Der Server arbeitet bevorzugt auf dem **geteilten Dokument** — dort sitzen
die Menschen —, und derselbe Plan kann zugleich **unter Git-Kontrolle**
stehen: Nach jedem Agenten-Schreibvorgang committet der Server den
Serverstand über das vorhandene `tools/pull-doc --git-commit` (D88) in eine
Spiegel-Datei. Der Server ist die Quelle, Git das Archiv (§5.7).
Das ist die **Gegenrichtung** zu `#ai.dialog`: Dort käme ein Modell in den
Editor, hier geht der Plan zu Agenten, die schon irgendwo laufen. Beides
teilt sich den Boden (`llms.md`, D43) und die Module; keines ersetzt das
andere.
## 2. Motivation
Heute kann ein Agent mit einem Werkbaum-Plan dreierlei tun — und alles
davon nur halb:
1. **Eine `.werkbaum`-Datei lesen und schreiben.** Das geht mit Claude Code
bereits, `llms.md` erklärt die Notation (D43). Aber der Agent muss die
**Semantik** selbst nachrechnen: Ist der Plan warnungsfrei? Welcher
Knoten ist effektiv wie weit (§4)? Was ist die nächste Station auf dem
günstigsten Pfad (§9)? Für all das gibt es im Frontend geprüfte
Funktionen — der Agent bekommt sie nicht zu sehen und rät stattdessen.
Beim Größen-Konflikt (D62) oder der Closure-Rechnung (D42) ist Raten
nachweislich falsch.
2. **Ein Server-Dokument (`?live=`) anfassen.** Nur per `curl` gegen die
REST-API — mit Volltext-`PUT`, also ohne das Zeilen-Diff-Protokoll (D76).
Ein Agent, der so schreibt, überschreibt, was ein Mensch in der
Zwischenzeit getippt hat; genau der Verlust, gegen den D89 vier Netze
gespannt hat.
3. **Den Plan im Editor ändern lassen**`#ai.dialog` ist eine Idee ohne
Entwurf.
MCP ist inzwischen die Verkehrssprache dafür: Claude Code, Claude Desktop,
Cursor, IntelliJ-Agenten und die Anthropic-API selbst (MCP-Connector)
sprechen es. Ein Server, der Werkbaum darüber anbietet, macht Punkt 1 und 2
auf einen Schlag richtig — und zwar für **alle** diese Hosts, nicht nur für
einen.
## 3. Ziele und Nicht-Ziele
**Ziele**
- Ein Agent kann einen Plan **lesen** (Datei oder Server-Dokument) und den
Notationsleitfaden dazu bekommen.
- Ein Agent kann den Plan **befragen**, ohne die SPEC nachzuimplementieren:
Baum mit allem, was das Diagramm weiß; Warnungen; günstigster Pfad samt
Stationen und Personen-Last.
- Ein Agent kann den Plan **ändern**, ohne fremde Arbeit zu überschreiben,
und jede Änderung ist im Editor als Agenten-Änderung erkennbar.
- Kein zweiter Parser, keine zweite Semantik (D14).
- Kein Geheimnis läuft durch den Server, das er nicht braucht.
**Nicht-Ziele (Abgrenzung — wird nicht gebaut, siehe §9)**
- **Kein LLM im Server.** Der Server ruft kein Modell auf und kennt keinen
API-Schlüssel. Das Modell sitzt im Host; `#ai.key` bleibt ein eigener
Knoten.
- **Keine neue Notation.** Nichts an SPEC oder `llms.md` ändert sich.
- **Kein Backend-Umbau.** Die vorhandene REST-API genügt (§7.3).
- **Kein Taiga über MCP** in der ersten Fassung (§9).
- **Keine Rechteverwaltung** — der Server erbt das Zugriffsmodell des
Backends (unerratbare UUID, D76) und erfindet keines dazu.
- **Kein Ersatz für `#ai.dialog`** und kein Chat im Editor.
## 4. Begriffe (soweit hier nötig)
MCP trennt **Hosts** (die Anwendung mit dem Modell: Claude Code, Desktop,
IDE) von **Servern** (Prozesse, die Fähigkeiten anbieten). Ein Server
bietet drei Dinge an:
- **Resources** — lesbare Inhalte mit URI, vom Host in den Kontext geholt
(„zeig mir den Plan“).
- **Tools** — aufrufbare Funktionen mit JSON-Schema, die das Modell selbst
wählt („prüfe den Plan“, „setze den Status“).
- **Prompts** — vorgefertigte Anweisungen, die der Nutzer auswählt
(„zerlege diesen Knoten“).
Transporte: **stdio** (der Host startet den Server als Kindprozess — der
Normalfall für lokale Werkzeuge) und **Streamable HTTP** (ein
Netzwerk-Endpunkt; hat 2025 das ältere SSE-Verfahren abgelöst). Die
Protokollrevision wird vom SDK getragen; dieses RFC legt keine fest.
## 5. Vorschlag
### 5.1 Architektur
```
Host (Claude Code, IDE, Desktop) Werkbaum
┌──────────────┐ stdio / JSON-RPC ┌──────────────────────────────┐
│ Modell │◄────────────────────►│ mcp/server.js │
│ + Werkzeuge │ │ ├─ resources (Text, Guide) │
└──────────────┘ │ ├─ tools (inspect, │
│ │ write) │
│ └─ stores │
│ ├─ file (fs) │
│ └─ live (REST, D76) │
│ importiert unverändert: │
│ frontend/src/parser.js │
│ frontend/src/model.js │
│ frontend/src/live.js │
└──────────────┬───────────────┘
│ HTTP
┌──────────────▼───────────────┐
│ Backend /api/v1/documents/… │
└──────────────────────────────┘
```
Ein Paket `mcp/` neben `frontend/` und `backend/`, eigenes `package.json`,
ES-Module. Es hat **keine** Kopie von Werkbaum-Logik; alles Entscheidbare
kommt aus `frontend/src`. Was das Paket selbst beisteuert, ist Verdrahtung:
MCP-Rahmen, zwei Ablagen, JSON-Serialisierung des Baums und die
Leitplanken.
### 5.2 Ablagen
Ein Plan wird über eine **Quelle** adressiert — dieselben drei, die ein
Mensch hat, minus `?sourceUrl=` (nur lesend, kommt bei Bedarf dazu):
| Quelle | Adressform | Lesen | Schreiben |
|---|---|---|---|
| Datei | absoluter Pfad oder relativ zum Arbeitsverzeichnis | `fs` | atomar (Temp-Datei + `rename`); **nur, wenn die Datei seit dem Lesen unverändert ist** (Prüfsumme) |
| Server-Dokument | `https://…/api/v1/documents/<uuid>` oder der geteilte `?live=`-Link (ausgepackt wie bei `pull-doc`, D88) | `GET` | `PATCH /content` mit Zeilen-Diff, `baseVersion`, `checksum` (D76) |
| Leitfaden | `werkbaum://guide` | `frontend/public/llms.md` | — |
Der Dateizugriff ist auf **erlaubte Wurzeln** begrenzt (Startparameter
`--root`, Voreinstellung: das Arbeitsverzeichnis des Hosts). Ein Agent, der
einen Pfad außerhalb nennt, bekommt einen Fehler — nicht, weil ihm misstraut
wird, sondern weil der Plan-Text Fremddaten ist (§8).
### 5.3 Resources
| URI | Inhalt | MIME |
|---|---|---|
| `werkbaum://guide` | `llms.md` — der Notationsleitfaden (D43) | `text/markdown` |
| `werkbaum://file/<pfad>` | Notationstext der Datei | `text/plain` |
| `werkbaum://doc/<url-encoded dokument-url>` | Notationstext des Server-Dokuments, mit `version` als Metadatum | `text/plain` |
Resources sind bewusst **nur der Text**: Er ist das führende Format (D14),
und ein Agent, der ihn im Kontext hat, kann mit `llms.md` schon alles
lesen. Die Semantik kommt aus den Tools.
### 5.4 Tools
Wenige Verben, sprechend benannt, alle mit `source` als erstem Parameter
(Pfad oder URL):
| Tool | Eingabe | Ausgabe | Nutzt |
|---|---|---|---|
| `werkbaum_read` | `source` | Text, `version`/Prüfsumme, Zeilenzahl | Ablage |
| `werkbaum_inspect` | `source`, optional `id` (Teilbaum), `include: [tree, warnings, path]` | JSON: Baum je Knoten mit `id`, `label`, `line`, `gate`, `optional`, `status`, `effectiveStatus`, `size`, `assumedSize`, `tags`, `deps`, `desc`, `taigaSlug`, `ticketRef`; Warnungen als `{type, line, …}` **plus** englischer Klartext; Pfad als Liste der nötigen Knoten, Stationen in Dokumentreihenfolge, `assigneeLoads`, `overloadedAssignee`, `exact` (D42) | `parse`, `effectiveStatus`, `computeCheapPlan`, `assigneeLoads`, `taigaSlugs`, `warningText` |
| `werkbaum_set_status` | `source`, `id`, `status` (Code der §4-Tabelle), `base` | neuer Stand (Version, Prüfsumme), Warnungs-Delta | `setStatusBox` (parser.js), Ablage |
| `werkbaum_apply_ops` | `source`, `base`, `ops[]` (`insert`/`replace`/`delete` mit `index`, `count`, `lines`**dasselbe Schema wie `LineOperation` der API**) | wie oben | `applyOps` (live.js), Ablage |
| `werkbaum_write` | `source`, `base`, `text` (Volltext) | wie oben | `computeOps` (live.js) → Diff → Ablage |
| `werkbaum_add_node` | `source`, `base`, `parent` (ID), `line` (die Knotenzeile ohne Einrückung, z. B. `- [ ] #auth.token: Token prüfen (S)`), optional `after` (ID eines Geschwisters) | wie oben | `addNodeLine` (edit.js) |
| `werkbaum_move_node` | `source`, `base`, `id`, `parent` (neue Eltern-ID), optional `after` | wie oben; der Teilbaum samt Beschreibungs- und Fortsetzungszeilen wandert mit, Einrückung wird angepasst | `moveSubtree` (edit.js) |
| `werkbaum_set_size` | `source`, `base`, `id`, `size` (`XS``XXL` oder `null` zum Entfernen) | wie oben | `setSizeToken` (edit.js) |
| `werkbaum_remove_node` | `source`, `base`, `id`, `discard: true\|false` | wie oben — `discard` setzt `[-]` (die Notation für „bewusst nicht“, §4), `false` löscht den Teilbaum samt seinen Blöcken | `setStatusBox` bzw. `removeSubtree` (edit.js) |
Die **Knoten-Verben** (Nutzer-Entscheidung, §11) sind Bequemlichkeit für
das Modell: `apply_ops` könnte jedes davon ausdrücken, aber ein Agent, der
Zeilenindizes ausrechnen muss, verrechnet sich — ein Verb mit ID ist die
robustere Schnittstelle. Damit dabei **keine zweite Stelle mit
Zeilenformat-Wissen** entsteht, liegen die Text→Text-Regeln als
`frontend/src/edit.js` neben `setStatusBox`/`setFoldMark` (parser.js) und
`appendToken` (taiga.js) — headless, getestet, und später auch für
`#ai.dialog` da (§7.1). Das MCP-Paket verdrahtet sie nur.
**`base` ist Pflicht bei jedem Schreiben** — `{version, checksum}` vom
letzten Lesen. Ohne `base` wird nicht geschrieben. Das ist die
Idempotenz- und Konfliktregel aus D76, unverändert übernommen.
**Warnungen tragen Typ und Text.** Der Typ (`sizeConflict`, `unknownDep`,
…) ist für den Agenten das Verlässliche; der englische Text kommt dazu,
weil das Modell ihn ohne Nachschlagen versteht. Die Texte sind die
**Editor-Texte** (`warningText`, D33-Nachtrag) in Englisch — nicht
nacherzählt (§7.1).
### 5.5 Schreib-Leitplanken
Auf jedem der drei Schreib-Tools, vor dem Schreiben:
1. **Keine beschädigte Notation.** Der Ergebnis-Text wird geparst; trägt
er **Struktur-Warnungen**, die der Ausgangstext nicht hatte —
`mixedGate`, `unknownStatus`, `descStray`, `duplicateId` —, wird
**nicht geschrieben**, und die Antwort nennt sie. Das sind die
Warnungen, bei denen der Text als Notation kaputt ist: eine Zeile, die
der Parser anders liest, als sie gemeint war. Schalter
`allowWarnings: true` je Aufruf erlaubt es ausdrücklich. **Inhaltliche
Widersprüche** — `sizeConflict`, `assigneeOverload`, `xorConflict`,
`unknownDep`, `unknownDesc` — werden **geschrieben und gemeldet**: Ein
Agent arbeitet in Zwischenschritten (das Ziel einer Abhängigkeit
entsteht oft erst im nächsten Zug), und im Diagramm sieht ein Mensch sie
als bernsteinfarbene Marke, genau wie bei einer eigenen Änderung
(Nutzer-Entscheidung, §11; die strengere Fassung „jede neue Warnung
blockiert“ war der Vorschlag). Verschwindende Warnungen sind immer
erlaubt, und jede Antwort trägt das vollständige Warnungs-Delta.
2. **Kein `[^]` von einem Agenten.** `in Produktion` ist die Aussage eines
Deploys (D30), nicht die eines Modells. `set_status` lehnt `prod` ab,
`apply_ops`/`write` lehnen ein Ergebnis ab, in dem ein Knoten neu `[^]`
trägt. Schalter `allowProd: true` je Aufruf — mit derselben Begründung:
ausdrücklich, nie still.
3. **Konflikt ist ein Fehler, keine Entscheidung.** Bei `409` (Server) oder
geänderter Datei kommt der **aktuelle** Text mit zurück; der Agent — und
im Zweifel der Mensch hinter ihm — entscheidet. Der Server rebased nichts
selbst über das hinaus, was das Backend ohnehin tut (D76: nicht
überlappende Ops verschiebt der Server; das bleibt).
Alle drei sind reine Verdrahtung um vorhandene Regeln; der Parser selbst
bleibt fehlertolerant, wie SPEC §4 es verlangt.
### 5.6 Identität und Sichtbarkeit
Jeder Schreibvorgang an ein Server-Dokument geht mit `clientId`
`mcp-<zufall>` (je Serverprozess, sessionStorage-Äquivalent: je Prozess
eine laufende `seq`, D76-Nachtrag 7) und einem `displayName`, der den
**Host nennt**: den Namen, den der Host im MCP-Handshake mitschickt
(`clientInfo.name`, etwa „Claude Code“), mit Rückfall **„Agent“**, wenn er
keinen nennt; `--name` übersteuert beides (Nutzer-Entscheidung, §11). In
jedem offenen Editor steht damit in der Historie „geändert von: Claude
Code“ (D86) — der Mensch sieht, welches Werkzeug es war, und kann es über
die Meilensteine zurückholen. Der Name bleibt eine Behauptung, kein
Nachweis — dieselbe Einordnung wie beim Anzeigenamen eines Menschen (D86).
### 5.7 Geteiltes Dokument **und** Git — beides zugleich
Die Frage, an der die Ablage-Wahl hängt: Ein Agent soll auf dem
**geteilten** Dokument arbeiten (dort sitzen die Menschen, dort greift das
Konfliktprotokoll), und derselbe Plan soll **unter Git-Kontrolle** stehen
(Diff, Blame, Review, ein Netz außerhalb des Servers — D89). Das sind zwei
Wahrheiten für einen Text, und die Antwort ist nicht „eine davon“, sondern
eine **Richtung**: Der Server ist die Quelle, Git ist das Archiv.
**Der Weg dafür existiert schon:** `tools/pull-doc --git-commit` (D88,
`#col.git.pull`) holt ein Server-Dokument in ein Worktree und committet es
datiert, nur bei Änderung, mit Titel und Server-Version in der Nachricht.
Aus dem Cron heraus archiviert sich ein Plan damit selbst. Der MCP-Server
setzt genau darauf auf:
- **Spiegel-Datei (`--mirror <datei>`):** Ist für ein Dokument eine
Spiegel-Datei konfiguriert, ruft der Server nach **jedem erfolgreichen
Agenten-Schreibvorgang** `pull-doc --git-commit` dafür auf. Die
Commit-Nachricht nennt zusätzlich den Urheber („via Agent: Claude Code“)
— die Git-Historie kann Agenten-Änderungen dann so auseinanderhalten, wie
es die Server-Historie über `displayName` tut (D86). Menschliche
Änderungen dazwischen fängt weiterhin der Cron; wer keinen hat, bekommt
sie spätestens mit dem nächsten Agenten-Zug ins Git, denn `pull-doc`
committet den **ganzen** Serverstand, nicht nur die Agenten-Zeilen.
- **Die Spiegel-Datei ist für den Agenten lesbar, aber nicht der
Schreibweg.** Ein Agent in Claude Code sieht die Datei im Repo und könnte
sie direkt editieren — beim nächsten `pull-doc` wäre das überschrieben.
Deshalb: `werkbaum_write`/`apply_ops`/`set_status` auf eine Datei, die
als Spiegel eines Dokuments konfiguriert ist, werden **umgeleitet** an
das Dokument (der Server kennt die Zuordnung) und die Antwort sagt es.
Ein Agent, der die Datei mit seinen eigenen Werkzeugen (Editor, `sed`)
anfasst, ist außerhalb des MCP-Servers — dagegen hilft nur die
D88-Regel, die schon da ist: Eine schmutzige Spiegel-Datei wird beim
nächsten Commit **trotzdem** vom Serverstand überschrieben, und der
Commit macht die Abweichung im Diff sichtbar statt sie zu verstecken.
- **Nicht gebaut wird die Umkehrung** — Git als Quelle und der Server als
Spiegel (`#col.git.pr` für Pläne, die sich wöchentlich ändern, oder
`#col.git.auto`, bei dem das Backend selbst committet). Beides sind
eigene Knoten mit eigenen Fragen (Konfliktmarker mitten in der Notation,
ROADMAP „Gemeinsam an einem Diagramm arbeiten“); der MCP-Server soll sie
weder vorwegnehmen noch ausschließen. Kommt `#col.git.auto`, wird
`--mirror` schlicht überflüssig — die Schnittstelle des Servers ändert
sich dadurch nicht.
Im Plan steht das als `#ai.mcp.mirror` mit Abhängigkeit auf
`#col.git.pull`.
### 5.8 Prompts (optional, Phase 6)
Zwei vorgefertigte Anweisungen, die ein Host dem Nutzer anbietet:
- `werkbaum/review` — „Prüfe diesen Plan: Warnungen, Größen, fehlende
Zerlegung ab M, unklare Zuständigkeiten“ (nutzt `inspect`, schreibt
nichts).
- `werkbaum/decompose` — „Zerlege Knoten `#id` in Teilpakete mit Größen“
(endet in `apply_ops`, mit den Leitplanken).
Beides sind Texte, keine Logik; sie leben im Paket und nicht in `llms.md`
(das bleibt Notation, D43).
### 5.9 Konfiguration und Start
```
node mcp/server.js [--root <dir>]… [--server <backend-basis>] [--name <anzeige>]
[--mirror <dokument-url>=<datei>]…
```
- `--root`: erlaubte Dateiwurzeln (mehrfach). Ohne Angabe: `cwd`.
- `--server`: Voreinstellung für relative Dokument-Angaben (`<uuid>` statt
voller URL). Ohne Angabe muss die URL vollständig sein.
- `--name`: `displayName` für Server-Schreibvorgänge.
- `--mirror`: Spiegel-Datei je Dokument (§5.7); die Datei muss in einem
git-Worktree unter einer erlaubten Wurzel liegen — `pull-doc` prüft das
ohnehin.
Für Claude Code:
```bash
claude mcp add werkbaum -- node /pfad/zu/werkbaum/mcp/server.js --root .
```
Im Repository selbst liegt eine `.mcp.json`, damit Claude Code hier den
Server von selbst hat — der Plan `docs/examples/werkbaum.werkbaum` ist damit
das erste Dokument, an dem der Server benutzt wird (Dogfooding, dieselbe
Logik wie D27).
## 6. Alternativen
### A — Server im Kotlin-Backend (Spring AI MCP)
*Pro:* ein Deployment, Streamable HTTP frei Haus, sitzt direkt an den
Dokumenten.
*Contra:* Jedes nützliche Tool braucht den Parser. Entweder entsteht ein
Kotlin-Parser — **genau die zweite Grammatik, die D14 verbietet** — oder
der Server bietet nur Dokument-CRUD an, und das kann ein Agent per REST
schon heute. Dazu die erste Spring-AI-Abhängigkeit im Backend.
**Verworfen** (`#ai.mcp.kotlin` als `[-]` im Plan).
### B — Node-Paket, das die Frontend-Module importiert (Vorschlag)
*Pro:* ein Parser, eine Semantik; die Module sind headless und getestet
(D54-Nachtrag 3 ist die Hausregel dafür); stdio ist der Transport, den alle
lokalen Hosts können; kein Backend-Umbau.
*Contra:* ein zweites Node-Paket im Repo; die Dependency-Frage (§7.4);
Remote-Zugriff erst mit Phase 6.
**Gewählt.**
### C — Nichts bauen: Agenten lesen `llms.md` und editieren Dateien
*Pro:* funktioniert heute mit Claude Code.
*Contra:* keine Semantik (der Agent rät Warnungen, Pfad, effektiven
Status), keine Server-Dokumente außer per `curl`, Schreiben an geteilte
Dokumente ohne Konfliktschutz. Das ist der Ausgangszustand aus §2.
**Bleibt als Rückfall bestehen** — der Server ersetzt `llms.md` nicht.
### D — OpenAPI der REST-API den Agenten geben
*Pro:* null neuer Code, die Spec existiert.
*Contra:* dieselbe Semantik-Lücke wie C, und die API spricht Volltext-`PUT`
oder Zeilen-Ops mit Prüfsumme — ein Modell, das die Prüfsumme selbst
rechnen soll, ist eine Fehlerquelle mit Ansage.
**Verworfen.**
### E — `#ai.dialog` zuerst (Modell im Editor)
*Pro:* der Nutzer bleibt im Werkzeug.
*Contra:* andere Frage (welches Modell, wessen Schlüssel, welche UI); bringt
den Plan nicht zu den Agenten, die Menschen heute schon benutzen.
**Nicht konkurrierend** — B liefert die Module, die E später auch braucht
(JSON-Baum, Leitplanken).
## 7. Impact auf den bestehenden Code
Grundsatz: **Das Frontend wird gelesen, nicht umgebaut.** Wo doch etwas zu
ändern ist, ist es eine Verschiebung ohne Verhaltensänderung.
### 7.1 `frontend/src/` — Änderungen
| Datei | Was | Warum |
|---|---|---|
| `app.js` → neu `i18n.js` | Das `I18N`-Objekt (ab `app.js:3287`) in ein headless-Modul verschieben; `app.js` importiert es. | Der Server braucht `warningText(w, t)` mit **englischem** `t`. Die Texte leben heute im UI-Modul, das DOM voraussetzt. Eine Kopie im MCP-Paket wäre die Drift, vor der D33-Nachtrag warnt. Reine Verschiebung; der Vite-Bundle bleibt gleich. |
| `warnings.js` | unverändert | importiert nur `esc` aus `render.js`; `render.js` ist headless bis zum Aufruf. |
| `parser.js` | unverändert | `parse`, `setStatusBox`, `STATUS_BY_CODE`, `SIZE_RANK` sind exportiert. |
| `model.js` | unverändert | `effectiveStatus`, `computeCheapPlan`, `assigneeLoads`, `overloadedAssignee`, `taigaSlugs`, `assumedSize` sind exportiert. |
| `live.js` | `checksum()` läuft in Node unverändert (`globalThis.crypto.subtle` gibt es ab Node 20). Prüfen, sonst Rückfall auf `node:crypto` **im MCP-Paket**, nicht in `live.js`. | Die Prüfsumme ist Pflichtfeld des Patches (D76). |
| `taiga.js` | `ticketRefOf`, `collectTicketRefs` nur lesend für `inspect` (`ticketRef`, `taigaSlug` je Knoten); `appendToken` für `set_size` | kein Taiga-Zugriff, nur die Auskunft, was im Text steht. |
| **neu** `edit.js` | Text→Text-Regeln der Knoten-Verben: `addNodeLine(text, parentId, line, afterId)`, `moveSubtree(text, id, parentId, afterId)`, `setSizeToken(text, id, size)`, `removeSubtree(text, id)`. Zeilenweise, zeichengenau, mit Tests wie `setFoldMark`/`expandShortIds`. Kennt Einrückung (§2), Fortsetzungs- und Beschreibungszeilen (§1: die wandern mit ihrem Knoten) und lässt Kommentare stehen. | Die eine Stelle für „Zeile eines Knotens umbauen“; `app.js` benutzt sie zunächst nicht, `#ai.dialog` später schon. Bewusst **nicht** in `parser.js`: Der wird sonst zum Sammelbecken. |
Was **nicht** ins Frontend zurückfließt: die JSON-Serialisierung des Baums
(`mcp/inspect.js`) — sie ist eine Sicht für Agenten, keine Editor-Logik
(Nutzer-Entscheidung, §11). Sollte `#ai.dialog` sie später brauchen,
wandert sie dann.
### 7.2 Neues Paket `mcp/`
```
mcp/
package.json name: werkbaum-mcp, type: module, bin: server.js
server.js MCP-Rahmen: Resources, Tools, Prompts registrieren; stdio
stores/file.js lesen, Prüfsumme, atomar schreiben, Wurzel-Prüfung
stores/live.js GET / PATCH gegen /api/v1/documents (D76), clientId/seq
inspect.js Baum → JSON, Warnungen → {type, line, text}, Pfad → Stationen
guard.js Leitplanken (§5.5): Struktur-Warnungen, [^]-Sperre
verbs.js Knoten-Verben → Aufrufe von frontend/src/edit.js, dann Ablage
tests/*.test.js Vitest gegen einen In-Memory-Transport des SDK; Fixtures = SPEC §10 und der mitgelieferte Plan
```
Die Regeln in `inspect.js` und `guard.js` sind Text→JSON bzw. Text→Text und
damit headless testbar — die Hausregel aus D54-Nachtrag 3 gilt auch hier;
`server.js` verdrahtet nur.
### 7.3 Backend
**Keine Änderung nötig.** Genutzt werden `GET /documents/{id}`,
`PATCH /documents/{id}/content` und `GET /info` — alle vorhanden. Zwei
Dinge sind zu **prüfen**, nicht zu bauen:
- Der Server sendet `displayName` — steht in der Historie (D86), gemessen
gegen ein lokales Backend.
- `clientId`-Präfix `mcp-`: Das Backend behandelt es wie jeden Client; ein
späterer Filter („nur menschliche Änderungen zeigen“) wäre ein eigener
Wunsch.
Perspektivisch berührt: `#col.live.owner`. Sobald Verwaltungs-Aktionen an
ein Owner-Passwort gebunden sind, braucht der MCP-Server einen Weg, es zu
übergeben — **nie** als Tool-Parameter (Prozessliste, Transkript), sondern
als Umgebungsvariable des Serverprozesses. Das ist heute keine Arbeit, nur
eine Vormerkung.
### 7.4 Abhängigkeit: `@modelcontextprotocol/sdk`
CLAUDE.md: keine neuen Laufzeit-Abhängigkeiten ohne Rückfrage. Das ist eine.
- **Für:** Das offizielle SDK trägt Protokollrevision, Capability-Handshake,
Schema-Validierung und beide Transporte; eine Handschrift des JSON-RPC
wäre ~200 Zeilen, die bei jeder Revision nachgezogen werden müssten.
- **Dagegen:** Es ist die erste Laufzeit-Abhängigkeit im Repo überhaupt.
- **Einordnung:** Sie liegt im **MCP-Paket**, nicht im Frontend — der
gebündelte Editor bleibt abhängigkeitsfrei (D11/D19/D20). Dieselbe
Grenze, die D76 für Spring Security gezogen hat: die Abhängigkeit dort,
wo sie den Kern nicht berührt.
**Entschieden (Nutzer, §11): SDK** — als bewusste Ausnahme in D93
festgehalten. Version wird beim Bauen gepinnt (`package-lock.json`
eingecheckt, wie im Frontend).
### 7.5 Übriges
| Ort | Änderung |
|---|---|
| `.mcp.json` (Repo-Wurzel) | Server-Eintrag für Claude Code (Dogfooding) |
| `README.md` / `README.de.md` | Abschnitt „Für KI-Agenten: der MCP-Server“ — Installation, die drei Quellen, die Leitplanken |
| `docs/CHANGELOG.md` | je Phase eine Zeile |
| `.github/workflows/*` | `npm --prefix mcp test` neben dem Frontend-Test; kein Deploy (lokales Werkzeug) |
| `tools/pull-doc` | **unverändert im Verhalten**; ein neuer Schalter `--author <text>` für den Urheber in der Commit-Nachricht (§5.7) — der Schalter kommt ins Skript, nicht in den Aufrufer (D77-Nachtrag) |
| `scripts/deploy-*.sh`, `tools/remote` | **unverändert** — nichts davon läuft auf dem Server |
| `frontend/public/llms.md` | **unverändert** (Notation, D43); ein Verweis auf den Server gehört in `llms.txt` (Wegweiser, D43-Nachtrag 2) |
## 8. Sicherheit
- **Plan-Text ist Fremddaten.** Beschreibungen (`---`-Teil) können
Anweisungen enthalten („ignoriere die Leitplanken“). Der Server führt
nichts aus, was im Text steht; Resources sind als Inhalt, nie als
Anweisung deklariert. Der Host trägt die Prompt-Injection-Frage — der
Server macht sie nur nicht schlimmer: keine Shell, kein Netz außer den
zwei konfigurierten Zielen (Dateiwurzeln, Backend).
- **Kein Geheimnis im Transkript.** Master-Passwort (D76-Nachtrag 6) geht
**nie** über MCP — `GET /documents` (die Liste) wird deshalb nicht
angeboten; der Agent bekommt Dokument-URLs vom Menschen. Ein späteres
Owner-Passwort kommt als Umgebungsvariable (§7.3). Taiga-Token ebenso
(§9).
- **Dateizugriff nur unter erlaubten Wurzeln**; Pfade werden aufgelöst
(`realpath`) und gegen die Wurzeln geprüft — Symlinks nach draußen
zählen als draußen.
- **Schreiben nur mit `base`**; kein Volltext-`PUT`. Was der Editor an
Netzen hat (Konflikt-Band, Rettungs-Sicherung, D89), hat der Agent in
Form des zurückgelieferten aktuellen Textes — er verliert nichts, er
muss neu ansetzen.
## 9. Abgrenzung — was ausdrücklich nicht gebaut wird
| Nicht gebaut | Warum | Wo es hingehört |
|---|---|---|
| Modellaufrufe, API-Schlüssel, Provider-Wahl | Der Host hat das Modell; ein Server mit eigenem Schlüssel wäre `#ai.key` unter falschem Namen | `#ai.key`, `#ai.dialog` |
| Taiga-Tools (Ticket anlegen, Status schreiben) | Braucht das Taiga-Token — das lebt im Browser (D91) und dürfte nicht durch ein Transkript; außerdem eine eigene Entscheidung, ob ein Modell Tickets anlegen darf | eigener Nachtrag, wenn gewünscht: über den vorhandenen Proxy, Token aus der Umgebung |
| Dokumentenliste (`GET /documents`) | Master-Passwort | — (der Mensch nennt die URL) |
| Anlegen/Löschen/Umbenennen von Server-Dokumenten | Verwaltung; wartet auf `#col.live.owner` | nach dem Owner-Passwort |
| Falten (`fold`/`unfold`) als Verb | Faltmarken sind Darstellung für Menschen (§9); ein Agent liest den ganzen Text ohnehin | — |
| Änderungsfeed / Long Polling | Ein Agent arbeitet in Zügen, nicht live; `read` vor jedem Schreiben genügt | — |
| Git als **Quelle** (Server folgt dem Repo) | Umkehrung von §5.7; Konfliktmarker mitten in der Notation, eigene Fragen | `#col.git.pr`, `#col.git.auto` |
| `?sourceUrl=`-Quellen | nur lesend; der Agent kann die URL selbst holen | Nachtrag bei Bedarf |
| Streamable HTTP | Phase 6, hängt an der Authentifizierungsfrage | `#ai.mcp.http` |
| Ein Kotlin-Server | D14 | `#ai.mcp.kotlin` (`[-]`) |
## 10. Umsetzungsreihenfolge
Jede Phase ist für sich abgeschlossen und bekommt ihre CHANGELOG-Zeile;
Plan-Knoten gehen beim Mergen auf `[x]`.
1. **Lesen** (`#ai.mcp.node`, `#ai.mcp.read`): Paket, stdio, Resources
für Guide und Datei, `werkbaum_read`. Nachweis: Claude Code liest den
mitgelieferten Plan über die `.mcp.json`. Vorher: SDK-Entscheidung
(§7.4).
2. **Befragen** (`#ai.mcp.inspect`): `i18n.js`-Verschiebung im Frontend,
`werkbaum_inspect` mit Baum, Warnungen, Pfad. Nachweis: Für den
mitgelieferten Plan liefert das Tool dieselbe Stationszahl und dieselben
Warnungen wie der Editor (Vergleich gegen die Zahlen, die die
DECISIONS-Einträge ohnehin nennen).
3. **Schreiben** (`#ai.mcp.write`, `#ai.mcp.guard`): Server-Ablage mit
`PATCH /content`, Datei-Ablage atomar, die drei Basis-Schreib-Tools, die
Leitplanken. Nachweis gegen ein lokales Backend: Agenten-Änderung
erscheint im offenen Editor mit „Claude Code“ in der Historie; ein
Konflikt (Mensch tippt dieselbe Zeile) kommt als Fehler mit aktuellem
Text zurück, nichts wird überschrieben; ein `[^]` wird abgelehnt; ein
`mixedGate` wird abgelehnt, ein `sizeConflict` geschrieben und gemeldet.
4. **Knoten-Verben** (`#ai.mcp.verbs`): `frontend/src/edit.js` mit Tests
(Gegenprobe per Mutation wie bei `setFoldMark`), dann die vier Verben
im Paket. Nachweis: Ein `move_node` nimmt Beschreibungsblock und
Fortsetzungszeilen mit und lässt den Rest des Texts zeichengenau stehen
(Vergleich der übrigen Zeilen vorher/nachher).
5. **Spiegel ins Git** (`#ai.mcp.mirror`): `--mirror`, Aufruf von
`pull-doc --git-commit` nach jedem Agenten-Schreibvorgang, Umleitung
von Schreibzugriffen auf die Spiegel-Datei. Nachweis in einem
Wegwerf-Worktree: ein Agenten-Zug ergibt genau einen Commit mit
Serverstand und Urheber; ein unveränderter Zug keinen; eine von Hand
verschmutzte Spiegel-Datei wird vom nächsten Commit überschrieben und
die Abweichung steht im Diff.
6. **Prompts und HTTP** (`#ai.mcp.http`, optional): die zwei Prompts;
Streamable HTTP erst nach `#col.live.owner`.
## 11. Entscheidungen (Multiple-Choice-Runde, 2026-09-02)
| Frage | Entschieden | Anmerkung |
|---|---|---|
| SDK-Abhängigkeit `@modelcontextprotocol/sdk` (§7.4) | **Ja, SDK** | erste Laufzeit-Abhängigkeit, isoliert im MCP-Paket; D93 |
| Strenge der Warnungs-Leitplanke (§5.5) | **Nur Struktur-Warnungen blockieren** | `mixedGate`, `unknownStatus`, `descStray`, `duplicateId`; alles Inhaltliche wird geschrieben und gemeldet. Vorgeschlagen war die strenge Fassung — der Nutzer hat die Zwischenschritte eines Agenten höher gewichtet |
| `displayName` (§5.6) | **Host-Name aus dem Handshake** | Rückfall „Agent“, `--name` übersteuert |
| Geteiltes Dokument und Git (§5.7) | **Server committet je Agenten-Zug** | über `pull-doc --git-commit`; Cron für menschliche Änderungen dazwischen |
| Ort der Baum-Serialisierung (§7.1) | **Im MCP-Paket** | wandert, wenn `#ai.dialog` sie braucht |
| Schreib-Tools (§5.4) | **Zusätzlich Knoten-Verben** | `add_node`, `move_node`, `set_size`, `remove_node`; Regeln in `frontend/src/edit.js`, nicht im Paket |
| Verteilung | **Nur aus dem Repo** | `.mcp.json`; npm erst auf Nachfrage von außen |
**Weiterhin offen** (stellt sich erst beim Bauen oder danach):
1. **Streamable HTTP** — wartet auf `#col.live.owner`; bis dahin stdio.
2. **`--author`-Schalter in `pull-doc`** — Form der Urheber-Angabe in der
Commit-Nachricht (Trailer `Co-Authored-By:` oder Freitext im Betreff);
entscheidet sich beim Bauen von Phase 5.
3. **Prompts** (§5.8) — ob zwei genügen und wie sie heißen; Phase 6.