docs(rfc): MCP-Server nur im Backend, kein Node als Laufzeit — dritte Runde; Hausregel: neue Technologie braucht ausdrückliche Zustimmung
Nutzer-FYI: Node.js einzuführen ist eine Dimension schwerer als eine Backend-Abhängigkeit und muss dem Entwickler deutlich gemacht werden — als Regel in CLAUDE.md. Aus den nachgefragten Beweggründen folgt: kein lokaler Prozess. Der MCP-Server lebt im Backend (Streamable HTTP, Bearer-Token), führt die Frontend-Module per GraalJS aus, jedes Werkzeug hat eine text- und eine document-Form; das Node-Paket ist verworfen, seine Tool-Schicht lebt als frontend/src/inspect.js, guard.js, edit.js. Backend-Abhängigkeiten GraalJS + Spring-AI-Starter zugestimmt; der GraalJS-Spike steht vor allem anderen. RFC neu geschrieben (§12 hält die drei Runden fest), Plan-Teilbaum #ai.mcp neu, D93-Nachtrag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
62807b91a1
commit
1f186eddce
@@ -36,6 +36,13 @@ Integrations-Backend.
|
|||||||
`I18N`-Objekt (frontend/index.html) anlegen, dann in alle Sprachen
|
`I18N`-Objekt (frontend/index.html) anlegen, dann in alle Sprachen
|
||||||
übersetzen.
|
übersetzen.
|
||||||
- Keine neuen Laufzeit-Abhängigkeiten ohne Rückfrage (gilt in beiden Teilen).
|
- Keine neuen Laufzeit-Abhängigkeiten ohne Rückfrage (gilt in beiden Teilen).
|
||||||
|
- **Eine neue Technologie ist eine Dimension schwerer als eine neue
|
||||||
|
Abhängigkeit** — Node.js als *Laufzeit* (auf dem Server oder als eigenes
|
||||||
|
Paket; bisher ist es nur Build-Werkzeug), eine weitere Sprache, ein
|
||||||
|
weiteres Laufzeitsystem. Das wird dem Entwickler **deutlich gemacht** und
|
||||||
|
seine **Zustimmung eingeholt**, als eigene benannte Entscheidung mit den
|
||||||
|
Alternativen innerhalb der vorhandenen Technologien (JVM, Vanilla JS im
|
||||||
|
Browser) — nie als Nebensatz eines Vorschlags (D93-Nachtrag).
|
||||||
- Der Notationstext ist das führende Datenformat; kein Teil erfindet ein
|
- Der Notationstext ist das führende Datenformat; kein Teil erfindet ein
|
||||||
eigenes Speicherformat für die Struktur.
|
eigenes Speicherformat für die Struktur.
|
||||||
- Im mitgelieferten Plan `docs/examples/werkbaum.werkbaum` bekommt **jeder**
|
- Im mitgelieferten Plan `docs/examples/werkbaum.werkbaum` bekommt **jeder**
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ reverse.
|
|||||||
|
|
||||||
## 2026-09-02
|
## 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
|
- An MCP server for AI agents recorded as planned: it lives in the backend, runs the one JS parser through GraalJS, hands plans to Claude Code and other agents as resources and tools over streamable HTTP, and writes changes as conflict-safe line diffs — no new runtime, spelled out in an RFC before it is built
|
||||||
|
|
||||||
## 2026-08-28
|
## 2026-08-28
|
||||||
|
|
||||||
|
|||||||
+54
-2
@@ -8765,6 +8765,58 @@ auch wenn sie erst spät gebaut werden — RFC §5.7 und §5.10):
|
|||||||
keinen Git-Spiegel; eine echte Teilmenge der lokalen Fassung mit
|
keinen Git-Spiegel; eine echte Teilmenge der lokalen Fassung mit
|
||||||
denselben Verben.
|
denselben Verben.
|
||||||
|
|
||||||
Offen bleiben nur noch Messungen (GraalJS-Spike, SSE durch den Apache)
|
**Dritte Runde — Node ist eine Technologie, keine Abhängigkeit; der
|
||||||
und die Prompts. Die Umsetzungsreihenfolge steht im RFC; gebaut ist nichts.
|
Entwurf kippt (2026-09-02, später am Tag).** Nutzer-FYI, als wichtig
|
||||||
|
notiert: *„Bevor komplette neue Technologien eingeführt werden, hier z. B.
|
||||||
|
Node.js, sollte dies dem menschlichen Entwickler deutlich gemacht werden
|
||||||
|
und dessen Zustimmung eingeholt werden. Node.js einzuführen ist eine
|
||||||
|
Dimension schlimmer als eine neue Dependency für den Kotlin/Spring-Boot
|
||||||
|
Backend."* Das RFC hatte Node — bisher im Repo reines Build-Werkzeug
|
||||||
|
(Vite) — als **Laufzeit** vorgeschlagen, lokal als Paket und auf dem
|
||||||
|
Server als Dienst, und dabei nur die SDK-Abhängigkeit abgefragt; die
|
||||||
|
eigentliche Frage war nie sichtbar gestellt. Die Regel steht jetzt in
|
||||||
|
CLAUDE.md (Querschnitts-Konventionen): Eine neue Laufzeit oder Sprache wird
|
||||||
|
als eigene, benannte Entscheidung mit den Alternativen innerhalb der
|
||||||
|
vorhandenen Technologien vorgelegt — nie als Nebensatz.
|
||||||
|
|
||||||
|
Nachgeholt in zwei Schritten — erst die **Beweggründe** (Nutzer: „die
|
||||||
|
Frage aufsplitten und mich mehr nach Beweggründen fragen, daraus die
|
||||||
|
eigentliche Entscheidung ableiten"), dann die Entscheidung:
|
||||||
|
|
||||||
|
- **Beweggründe:** Entwickler arbeiten parallel an geteilten Plänen und
|
||||||
|
setzen agentische KI ein — Pläne im Dialog erstellen, Feature-Fortschritt
|
||||||
|
tracken; der Agent läuft meist lokal (OpenCode, Claude Code, Codex),
|
||||||
|
später auch im Frontend. Pläne liegen **beides**: als Datei im Repo und
|
||||||
|
als Server-Dokument. Der Text eines lokalen Plans **darf** zur Analyse
|
||||||
|
an den eigenen Server. Die Startzeit eines lokalen Prozesses ist egal.
|
||||||
|
- **Daraus folgt: kein lokaler Prozess, keine neue Laufzeit.** Der
|
||||||
|
MCP-Server lebt **nur im Backend** (Streamable HTTP, Bearer-Token per
|
||||||
|
Spring Security), die Werkbaum-Logik läuft dort per GraalJS aus den
|
||||||
|
unveränderten Frontend-Modulen, beim Bauen zu einem ES-Modul gebündelt —
|
||||||
|
Node bleibt Build-Werkzeug. Jedes Werkzeug hat zwei Formen: `text` (der
|
||||||
|
Agent hat die Datei selbst gelesen, bekommt den neuen Text zurück und
|
||||||
|
schreibt ihn selbst; die Leitplanken sind dort **Befund**) und `document`
|
||||||
|
(UUID eines Dokuments dieses Backends; Zeilen-Diff, Konfliktschutz,
|
||||||
|
Leitplanken als **Sperre**). `document` ist bewusst eine UUID, keine URL
|
||||||
|
— kein Fremdserver, keine SSRF-Frage. Der Git-Spiegel ist `pull-doc`,
|
||||||
|
vom Cron oder vom Agenten selbst aufgerufen; ein Spiegel am Dienst
|
||||||
|
entfällt.
|
||||||
|
- **Verworfen:** das Node-Paket (`#ai.mcp.node` `[-]`); als Rückfall
|
||||||
|
benannt, nicht gewählt: das Backend-Jar im stdio-Modus beim Entwickler
|
||||||
|
(keine neue Laufzeit, aber ohne Anlass, sobald der Text zum Server darf).
|
||||||
|
- **Backend-Abhängigkeiten, Rückfrage gestellt und beantwortet:** GraalJS
|
||||||
|
(`org.graalvm.polyglot`) und der **Spring-AI-Starter**
|
||||||
|
(`spring-ai-starter-mcp-server-webmvc`); Boot-4-Verträglichkeit ist beim
|
||||||
|
Spike zu prüfen, Rückfall das MCP-Java-SDK direkt.
|
||||||
|
- **Die Runde-1-Entscheidungen zum Node-Paket sind überholt** (SDK,
|
||||||
|
Serialisierung im Paket, npm); die Tool-Schicht lebt als
|
||||||
|
`frontend/src/inspect.js`, `guard.js`, `edit.js` — dort, wo `#ai.dialog`
|
||||||
|
sie ohnehin braucht.
|
||||||
|
|
||||||
|
**Vor allem anderen steht jetzt der Spike** (`#ai.mcp.spike`): Läuft das
|
||||||
|
Bündel in GraalJS mit denselben Zahlen wie Vitest, was kostet ein Kontext
|
||||||
|
auf dem knappen Host (D76-Nachtrag 3), reicht der Apache den SSE-Strom
|
||||||
|
ungepuffert durch. Fällt er durch, ist die Antwort nicht Node, sondern eine
|
||||||
|
neue Frage an den Entwickler. Offen sonst nur die Prompts. Gebaut ist
|
||||||
|
nichts.
|
||||||
|
|
||||||
|
|||||||
@@ -225,16 +225,18 @@
|
|||||||
- [?] #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: An MCP server in the backend 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.spike: GraalJS runs the one JS parser inside the JVM — measured before anything is built (S) %% memory, same numbers as vitest, SSE through Apache
|
||||||
- [ ] #ai.mcp.read: Resources: the agent guide, a file, a server document (S)
|
- [ ] #ai.mcp.core: The tool layer as headless frontend modules, bundled into the jar (M) :#ai.mcp.spike
|
||||||
- [ ] #ai.mcp.inspect: Tools that answer what the diagram answers (M) %% tree, warnings, cheapest path
|
- [ ] #ai.mcp.read: Resources: the agent guide and the server documents (S)
|
||||||
- [ ] #ai.mcp.write: Writes go out as line diffs and are refused on conflict (M) :#col.live.diff
|
- [ ] #ai.mcp.inspect: Tools that answer what the diagram answers, for a text or a document (M) %% tree, warnings, cheapest path
|
||||||
- [ ] #ai.mcp.guard: Guard rails: no broken notation, no [^] from an agent (S)
|
- [ ] #ai.mcp.write: Writes to documents go out as line diffs and are refused on conflict (M) :#col.live.diff
|
||||||
- [ ] #ai.mcp.verbs: Node verbs: add, move, size, remove — the rules live in the frontend (M) :#ai.mcp.write
|
- [ ] #ai.mcp.guard: Guard rails: no broken notation, no [^] — a block on documents, a finding on text (S)
|
||||||
- [ ] #ai.mcp.mirror: The shared document mirrors into git after every agent write (S) :#col.git.pull
|
- [ ] #ai.mcp.verbs: Node verbs: add, move, size, remove — text to text, and on documents (M) :#ai.mcp.write
|
||||||
+ [?] #ai.mcp.http: Streamable HTTP, served by the backend running the JS modules (M) :#col.live.owner %% GraalJS spike first
|
- [ ] #ai.mcp.auth: A static bearer token via Spring Security; OAuth once there is an IdP (S)
|
||||||
- [-] #ai.mcp.kotlin: A server inside the backend with its own Kotlin parser (L) %% the second grammar D14 forbids
|
- [ ] #ai.mcp.mirror: pull-doc names who changed which version — run by cron or by the agent (S) :#col.git.pull
|
||||||
|
- [-] #ai.mcp.node: A local Node package as stdio server (L) %% a new runtime, and the text may go to the own server instead — D93
|
||||||
|
- [-] #ai.mcp.kotlin: A parser of its own in Kotlin (L) %% the second grammar D14 forbids
|
||||||
|
|
||||||
---
|
---
|
||||||
#wb
|
#wb
|
||||||
@@ -1298,39 +1300,50 @@
|
|||||||
|
|
||||||
#ai.mcp
|
#ai.mcp
|
||||||
The other direction of the AI integration: not an LLM inside the editor,
|
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
|
but the plan handed to agents that live outside it — Claude Code,
|
||||||
agents, desktop assistants — over the Model Context Protocol. Specified in
|
OpenCode, Codex, IDE agents — over the Model Context Protocol, served by
|
||||||
|
the backend over streamable HTTP. No local process, no new runtime: a
|
||||||
|
URL and a token in the host's configuration. Specified in
|
||||||
docs/rfc/001-mcp-server.md before anything is built.
|
docs/rfc/001-mcp-server.md before anything is built.
|
||||||
|
|
||||||
#ai.mcp.node
|
#ai.mcp.spike
|
||||||
The server is a small Node package that imports the frontend's headless
|
The whole design rests on the JVM running the frontend's JS modules
|
||||||
modules (parser, model, live diff) as they are. That keeps D14 intact: the
|
through GraalJS. Before a line is built, a spike measures it: the bundle
|
||||||
one JS parser stays the only parser, and agents get the same warnings,
|
parses the shipped plan with the same numbers vitest reports, a context
|
||||||
effective statuses and cheapest path the diagram shows.
|
costs a known amount of memory on the tight production host, and Apache
|
||||||
|
passes the SSE stream of a long tool call unbuffered.
|
||||||
|
|
||||||
|
#ai.mcp.core
|
||||||
|
The tool layer — tree to JSON, guard rails, node edits — lives as headless
|
||||||
|
modules in frontend/src, tested with vitest, and is bundled at build time
|
||||||
|
into one ES module inside the jar. That keeps D14 intact: the one JS
|
||||||
|
parser stays the only parser, and Node stays what it is here, a build tool.
|
||||||
|
|
||||||
#ai.mcp.read
|
#ai.mcp.read
|
||||||
What an agent can read: llms.md as the notation guide, a local .werkbaum
|
What an agent can read: llms.md as the notation guide, and the documents
|
||||||
file, and a shared document on a Werkbaum backend by its URL — the same
|
of this backend by UUID — with title, version and checksum. A local file
|
||||||
three sources a person has.
|
the agent reads itself and hands the text to the tools.
|
||||||
|
|
||||||
#ai.mcp.inspect
|
#ai.mcp.inspect
|
||||||
Tools that return the parsed tree with ids, statuses (own and effective),
|
Tools that return the parsed tree with ids, statuses (own and effective),
|
||||||
sizes (given and assumed), tags and dependencies, the warnings the editor
|
sizes (given and assumed), tags and dependencies, the warnings the editor
|
||||||
would show, and the cheapest path with its stations and per-person load —
|
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.
|
for a text the agent passes in or a document it names — so the agent
|
||||||
|
reasons on the model instead of re-implementing the SPEC.
|
||||||
|
|
||||||
#ai.mcp.write
|
#ai.mcp.write
|
||||||
Edits leave the server as the same line diffs the editor sends: against a
|
Edits to a document leave the server as the same line diffs the editor
|
||||||
base version with checksum, refused on a real conflict, visible to every
|
sends: against a base version with checksum, refused on a real conflict,
|
||||||
open editor with "changed by" naming the agent. A local file is written
|
visible to every open editor with "changed by" naming the agent's host.
|
||||||
only if it is unchanged since it was read.
|
For a text, the tool returns the new text and the agent writes the file.
|
||||||
|
|
||||||
#ai.mcp.guard
|
#ai.mcp.guard
|
||||||
Guard rails on every write: the result must not break the notation (no new
|
Guard rails on every write: the result must not break the notation (no new
|
||||||
mixedGate, unknownStatus, descStray or duplicateId), and an agent never
|
mixedGate, unknownStatus, descStray or duplicateId), and an agent never
|
||||||
writes [^] — "in production" is a deploy's statement (D30), not a model's.
|
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
|
On a document that blocks the write; on a text it is a finding the agent
|
||||||
sees them as the amber mark. Both rails can be lifted per call, never silently.
|
sees. 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.
|
||||||
|
|
||||||
#ai.mcp.verbs
|
#ai.mcp.verbs
|
||||||
Convenience for the model: add a node under a parent, move a subtree, set a
|
Convenience for the model: add a node under a parent, move a subtree, set a
|
||||||
@@ -1338,24 +1351,26 @@
|
|||||||
gets wrong. The text-to-text rules live in frontend/src/edit.js beside the
|
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.
|
fold and short-id helpers, so exactly one place keeps knowing the line format.
|
||||||
|
|
||||||
|
#ai.mcp.auth
|
||||||
|
One static bearer token per installation, checked by Spring Security like
|
||||||
|
the master password; without it the endpoint is closed, not open. OAuth
|
||||||
|
2.1, which MCP foresees for HTTP, waits for an identity provider.
|
||||||
|
|
||||||
#ai.mcp.mirror
|
#ai.mcp.mirror
|
||||||
Both at once: the agent works on the shared document, where the people
|
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
|
are, and the same plan stays under git control. pull-doc --git-commit
|
||||||
the server runs pull-doc --git-commit for a configured mirror file, naming
|
archives the server state — run by a cron, or by the agent itself right
|
||||||
the agent in the commit — the server is the source, git the archive. The
|
after its change — and with --with-history the commit names who changed
|
||||||
reverse (git as the source) is a different node, #col.git.
|
which version, as trailers, so blame never credits the agent with a
|
||||||
|
human's lines. The server is the source, git the archive.
|
||||||
|
|
||||||
#ai.mcp.http
|
#ai.mcp.node
|
||||||
Streamable HTTP as a second transport, so a hosted agent can reach the
|
A local Node package as stdio server was the first draft: files and the
|
||||||
server without a local process. Served by the Spring backend, which runs
|
git mirror right at the process. Discarded because it would make Node a
|
||||||
the same JS modules through GraalJS — Kotlin is transport and access
|
runtime — a new technology, one dimension heavier than a dependency — and
|
||||||
(a static bearer token via Spring Security), the logic stays in one place.
|
the motives did not need it: the text may go to the own server instead.
|
||||||
A spike must first prove the modules run there and what they cost in
|
|
||||||
memory; if it fails, the Node package serves HTTP as its own service.
|
|
||||||
Documents only: no file store, no git mirror over the network.
|
|
||||||
|
|
||||||
#ai.mcp.kotlin
|
#ai.mcp.kotlin
|
||||||
Building the server into the Spring backend with a parser of its own would
|
A parser of its own in Kotlin would put the logic next to the documents —
|
||||||
put it next to the documents — but a Kotlin parser is exactly the second
|
but it is exactly the second grammar D14 forbids. Discarded; the backend
|
||||||
grammar D14 forbids. Discarded; the backend may host the transport
|
runs the JS parser instead of rebuilding it.
|
||||||
(#ai.mcp.http), never a second grammar.
|
|
||||||
|
|||||||
+448
-521
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user