stack: MCP submit-Tool mit Upload Review Gate und Quarantäne-Schreibpfad (schliesst #32)
Files changed: - .gitignore - AGENTS.md - CHANGES.md - INSTALL-MCP.md - README.md - VERSION - docs/why-gates-are-code.md - instructions/gates.md - instructions/ingest-queue.md - instructions/mcp-read-server.md - instructions/wiki-ingest/SKILL.md - raw/CONTRACT.md - tools/CONTRACT.md - tools/chemenu/cli.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/upload_cmd.py - tools/chemenu/config.py - tools/chemenu/mcp/server.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_mcp_server.py - tools/chemenu/tests/test_upload.py - tools/chemenu/tests/test_upload_cmd.py - tools/chemenu/upload.py
This commit is contained in:
+71
-1
@@ -35,7 +35,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
|
||||
|
||||
---
|
||||
|
||||
## 5.0.0-beta.10 - 2026-09-11 - tools/CONTRACT.md: raw accept Doku auf Datums-Shard und Capture-Felder nachgezogen (schliesst #89)
|
||||
## 5.0.0-beta.11 - 2026-09-11 - MCP submit-Tool: Quarantäne-Schreibpfad mit Upload Review Gate (schliesst #32)
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -63,6 +63,7 @@ dev-checkout concern - readable here, never shipped as something to parse.
|
||||
- CLAUDE.md-Importkette entdrifted, Modellwahl nach docs/ verschoben (schliesst #81)
|
||||
- Telemetrie-Default nach Installationsform, Byte-Deckel und Session-Retention
|
||||
- tools/CONTRACT.md: raw accept Doku auf Datums-Shard und Capture-Felder nachgezogen (schliesst #89)
|
||||
- MCP submit-Tool: Quarantäne-Schreibpfad mit Upload Review Gate (schliesst #32)
|
||||
<!-- /wikitool:bumps -->
|
||||
|
||||
|
||||
@@ -1107,6 +1108,75 @@ vorbeigeschrieben worden - `raw/CONTRACT.md` selbst war korrekt, `docs verify`s
|
||||
prüft nur Kommandonamen gegen die Tabelle, nicht deren Prosa gegen den Code. Reine
|
||||
Doku-Korrektur, kein Verhalten geändert. Schließt #89.
|
||||
|
||||
Der MCP-Server bekommt ein sechstes, optionales Tool: `submit`, ein Schreibpfad für Dokumente
|
||||
von einem Aufrufer, der nicht dieses Terminal ist. #19s Eigenschaft "kein Tool schreibt" galt
|
||||
strukturell - nichts unter `chemenu.commands` war importierbar - und diese Formulierung wird mit
|
||||
einem echten Schreibpfad falsch. Die tragfähige Ersatzformulierung ist eine **Positiv-Liste**
|
||||
statt einer Abwesenheit: der Serverprozess darf in genau ein Verzeichnis schreiben,
|
||||
`mcp-upload/`, erzwungen durch eine einzige Funktion (`chemenu.upload._write_atomic_within`),
|
||||
die jeden aufgelösten Zielpfad gegen dieses eine Verzeichnis prüft - Tests decken `..`,
|
||||
absolute Pfade und einen Symlink, der aus dem Verzeichnis hinausführt. Die alte
|
||||
Abwesenheitseigenschaft bleibt daneben unverändert bestehen: die Reviewer-Kommandos
|
||||
(`upload accept`/`upload reject`) liegen unter `chemenu.commands` und sind vom Server aus nicht
|
||||
erreichbar.
|
||||
|
||||
**Zwei Stufen vor `raw/`, zwei verschiedene Grenzen.** `mcp-upload/<id>/` hält Material, das
|
||||
niemand geprüft hat; `wikitool upload accept <id> --confirm <token>` befördert es nach
|
||||
`incoming/`, wo es sich nicht mehr von einer lokal abgelegten Datei unterscheidet und
|
||||
`wiki-ingest` Schritt 1 unverändert greift. Ohne Token verweigert `upload accept` mit **Exit
|
||||
42** - der vierte Gate des Stacks, **Upload Review Gate**, gleiche Form wie der
|
||||
Mass-Update-Gate: ein Token, der Id, Dateiname, Größe, Sha256 und Einreicher digestet, wird
|
||||
also ungültig, sobald sich das Manifest ändert. `wikitool upload reject <id> --reason "<warum>"`
|
||||
braucht keinen Gate - Ablehnen braucht keine Freigabe, nur Annehmen tut das - und löscht das
|
||||
Material, behält aber Grund und Sha256 im append-only `mcp-upload/ledger.jsonl`.
|
||||
|
||||
**Die Einreicher-Identität kommt ausschließlich aus einem HTTP-Header, nie aus einem
|
||||
Tool-Argument.** `identity_header` (Default `X-Forwarded-User`) in `.wikitool-upload.json`
|
||||
nennt den Header; fehlt er auf der Anfrage, wird ohne jeden Schreibvorgang verweigert - eine
|
||||
unzurechenbare Einreichung ist damit unmöglich, nicht nur unerwünscht. Das Manifest hält neben
|
||||
`submitter` auch `submitter_source` (den Headernamen), damit der Datensatz sagt, worauf die
|
||||
Behauptung ruht, statt sie als Tatsache zu führen. Die Middleware muss den Header selbst setzen
|
||||
und eine vom Client mitgeschickte Kopie verwerfen - eine Deployment-Pflicht, dokumentiert in
|
||||
`INSTALL-MCP.md` Schritt 5 und `instructions/ingest-queue.md`, die der Prozess selbst nicht
|
||||
erzwingen kann.
|
||||
|
||||
**`.wikitool-upload.json` ist ein struktureller Opt-in, nicht bloß eine Konfiguration.** Fehlt
|
||||
die Datei, wird das `submit`-Tool gar nicht erst registriert - anders als bei
|
||||
`.wikitool-remotes.json`, wo Abwesenheit "unbeschränkt" heißt, heißt sie hier "der Schreibpfad
|
||||
existiert nicht". Eine defekte Datei ist ein Startfehler des Servers (`ValidationError` beim
|
||||
Aufbau) und ein `FAIL` in `wikitool doctor`s neuem `upload-intake`-Check - nie "keine
|
||||
Beschränkung". Weitere Schutzschichten in `chemenu/upload.py`: eine Größenprüfung auf der
|
||||
base64-Länge *vor* dem Dekodieren (mit einem Toleranzband von 2 Bytes für Padding, damit sie
|
||||
keine an der Grenze liegende, legitime Einreichung fälschlich ablehnt - der Nachdekodier-Check
|
||||
bleibt die exakte Durchsetzung), eine Endungs-Positivliste, ein rollierendes
|
||||
24-Stunden-Kontingent pro Einreicher (Anzahl und Bytes, aus dem Ledger berechnet, nie aus
|
||||
Verweigerungen), und eine Ablehnung doppelter Inhalte, solange die erste Einreichung noch
|
||||
wartet - unter Nennung der wartenden Id.
|
||||
|
||||
**`incoming/` bleibt komplett unberührt als zweite, unabhängige Grenze.** Ein Unterverzeichnis
|
||||
dort wird seit #67 toleriert und ignoriert, sodass eine Fremdeinreichung darunter still
|
||||
promotierbar gewesen wäre - deshalb ein eigenes Top-Level-Verzeichnis `mcp-upload/`, gitignored,
|
||||
ohne `.gitkeep` (die Schreibprimitive legt es selbst an). `docs_verify`s Ignore-Kanarien prüfen
|
||||
jetzt auch `mcp-upload/probe.pdf` und `.wikitool-upload.json`.
|
||||
|
||||
**MINOR, kein neuer Boundary-Crossing:** additiv in beide Richtungen geprüft - eine bestehende
|
||||
Instanz kopiert die neue Maschinerie über sich und bekommt ein neues, standardmäßig
|
||||
unregistriertes Tool, ohne Hand-Arbeit oder Migration; kein `.wikitool-kb.json`-Feld, kein
|
||||
Type-Spec, kein umbenanntes Kommando oder Flag, kein geändertes Dateiformat. Die alte Version
|
||||
zurücklegen verliert nichts - der Schreibpfad existiert dort schlicht nicht. Der Kandidat trägt
|
||||
seine `--breaking`-Zeile bereits aus einem früheren Bump (Confidence-Entfernung); diese Änderung
|
||||
fügt keine neue hinzu.
|
||||
|
||||
Geändert: `tools/chemenu/upload.py` (neu), `tools/chemenu/commands/upload_cmd.py` (neu),
|
||||
`tools/chemenu/tests/test_upload.py` (neu), `tools/chemenu/tests/test_upload_cmd.py` (neu),
|
||||
`tools/chemenu/mcp/server.py`, `tools/chemenu/tests/test_mcp_server.py`,
|
||||
`tools/chemenu/commands/doctor.py`, `tools/chemenu/tests/test_doctor.py`,
|
||||
`tools/chemenu/commands/docs_verify.py`, `tools/chemenu/cli.py`, `tools/chemenu/config.py`,
|
||||
`.gitignore`, `tools/CONTRACT.md`, `AGENTS.md`, `instructions/gates.md`,
|
||||
`instructions/mcp-read-server.md`, `instructions/wiki-ingest/SKILL.md`,
|
||||
`instructions/ingest-queue.md` (neu), `raw/CONTRACT.md`, `docs/why-gates-are-code.md`,
|
||||
`INSTALL-MCP.md`, `README.md`. Schließt #32.
|
||||
|
||||
---
|
||||
|
||||
## 4.7.4 - 2026-09-04 - bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet
|
||||
|
||||
Reference in New Issue
Block a user