docs(readme): CORS-Konfiguration und MCP-Endpoint mit curl-Handshake dokumentiert
This commit is contained in:
1 parent
150c374bdc
commit
30bd535400
1 file changed
+61
-2
@@ -33,7 +33,7 @@ mit der kompletten Provider-Kette und Daemon-Anbindung (non-blocking über
|
||||
`talkd`, siehe unten), `mute`, `unmute`, `status`, `cancel`. Die übrigen
|
||||
Befehle sind Stubs und melden `not implemented yet` (Exit-Code ≠ 0).
|
||||
|
||||
## Daemon talkd (Task 04 + 05)
|
||||
## Daemon talkd (Task 04–06)
|
||||
|
||||
`talkd` besitzt die Ansage-Queue und spricht seriell ab — genau eine Ansage
|
||||
gleichzeitig, Reihenfolge `now` > `high` > `normal` > `low` (FIFO je
|
||||
@@ -43,9 +43,12 @@ config `[server] port`, Env `TALK_PORT` (schlagen den Default).
|
||||
| Endpunkt | Wirkung |
|
||||
| --- | --- |
|
||||
| `POST /speak` | `{text, priority?, provider?, voice?, lang?, now?}` → `202 {"id":"…"}`; bei Mute: `202 {"id":"","muted":true}`; bei Gesetz-Verwurf: `202 {"id":"","discarded":"rate_limit"\|"quiet_hours"}` (+ `"toast":true` im Toast-Modus) — verworfen, dokumentiertes Verhalten |
|
||||
| `GET /status` | `{"version", "queue", "queue_ids", "current", "muted", "provider", "rate_limit": {"interval", "allow_high_burst", "next_slot"}, "quiet_hours": {"window", "active", "mode", "until"}}` |
|
||||
| `OPTIONS /speak` | CORS-Preflight → `204` mit Freigabe-Headern, wenn die `Origin` in `server.allowed_origins` steht (siehe unten); sonst `204` ohne CORS-Header |
|
||||
| `GET /status` | `{"version", "queue", "queue_ids", "current", "muted", "provider", "rate_limit": {"interval", "allow_high_burst", "next_slot"}, "quiet_hours": {"window", "active", "mode", "until"}, "mcp": true, "cors": [origins]}` |
|
||||
| `POST /cancel` | bricht die laufende Ansage ab + leert die Queue → `200 {"cancelled":n}` |
|
||||
| `POST /mute` / `POST /unmute` | persistenter Mute-State → `200 {"muted":bool}` |
|
||||
| `POST /mcp` | MCP (Streamable HTTP): JSON-RPC `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call` — ein Tool `speak` (siehe unten) |
|
||||
| `GET /mcp` | `405` — talkd bietet keine server-initiierten Streams an |
|
||||
|
||||
- **CLI-Verhalten:** `talk say` (und `talk "Text"`) erkennt den Daemon via
|
||||
`GET /status`. Läuft er: enqueue + sofortige Rückkehr (non-blocking,
|
||||
@@ -105,6 +108,62 @@ quiet_mode = "discard" # discard | toast (TALK_QUIET_MODE)
|
||||
erlaubt), nächster slot …` und `ruhezeit: 18:00-08:00 (modus discard,
|
||||
aktiv bis 08:00)`.
|
||||
|
||||
### CORS-Tür + MCP-Endpoint (Task 06)
|
||||
|
||||
**CORS** gilt bewusst nur für `POST /speak` — genau die eine Tür, die eine
|
||||
spätere Browser-Anbindung (z. B. LibreChat) braucht; Steuer-Endpunkte
|
||||
bleiben browserseitig unerreicht. Die Erlaubnis ist eine exakte Origin-
|
||||
Liste; `"*"` ist erlaubt, aber nicht Default; **leere Liste (Default) =
|
||||
gar keine CORS-Header = der Browser blockt**:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
allowed_origins = ["https://chat.az-gruppe.com"] # Env TALK_ALLOWED_ORIGINS
|
||||
```
|
||||
|
||||
- `OPTIONS /speak` (Preflight) von gelisteter Origin → `204` mit
|
||||
`Access-Control-Allow-Origin` (die Origin bzw. `*`), `-Methods: POST,
|
||||
OPTIONS`, `-Headers: Content-Type`, `Max-Age: 600`.
|
||||
- Echter `POST /speak` mit gelisteter `Origin` trägt denselben ACAO-Header.
|
||||
- Ungelistete Origins bekommen saubere `204`/`202` **ohne** CORS-Header —
|
||||
die Blockade macht der Browser.
|
||||
|
||||
**MCP** (`POST /mcp`, Streamable HTTP): schlanke eigene Implementierung
|
||||
des JSON-RPC-Subsets ohne SDK — `initialize` (angefragte
|
||||
`protocolVersion` wird geechot; Ergebnis `protocolVersion`,
|
||||
`capabilities: {tools:{}}`, `serverInfo`), `notifications/initialized`
|
||||
(Notification → `202` ohne Body), `ping`, `tools/list`,
|
||||
`tools/call`. Der Server ist zustandslos (keine Session-IDs) und pusht
|
||||
nichts (daher `GET /mcp` → `405`). Ein Tool:
|
||||
|
||||
```json
|
||||
{"name": "speak",
|
||||
"inputSchema": {"type": "object",
|
||||
"properties": {"text": {"type": "string"},
|
||||
"priority": {"enum": ["low","normal","high"]},
|
||||
"provider": {"type": "string"},
|
||||
"lang": {"enum": ["de","en"]},
|
||||
"now": {"type": "boolean"}},
|
||||
"required": ["text"]}}
|
||||
```
|
||||
|
||||
`tools/call` fährt exakt denselben Aufnahme-Pfad wie `POST /speak`
|
||||
(inklusive Mute, Rate-Limit, Ruhezeiten). Ergebnis ist immer Text-Content:
|
||||
`"queued: <id>"` bei Aufnahme; `muted`/`discarded: <grund>` mit Erklärung
|
||||
— bewusst `isError: false`, denn ein Verwurf ist das korrekte
|
||||
dokumentierte Ergebnis, kein Protokollfehler. Validierungsfehler
|
||||
(fehlender Text, ungültige Priorität, unbekanntes Tool) sind dagegen
|
||||
JSON-RPC-Fehler (`-32602`). Handshake als curl-Sequenz:
|
||||
|
||||
```sh
|
||||
curl -s http://127.0.0.1:7800/mcp -H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
|
||||
curl -s http://127.0.0.1:7800/mcp -H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
|
||||
curl -s http://127.0.0.1:7800/mcp -H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"speak","arguments":{"text":"Build fertig"}}}'
|
||||
```
|
||||
|
||||
### Provider-Kette (Task 03)
|
||||
|
||||
Ohne Pin läuft die Vier-Stufen-Fallback-Kette
|
||||
|
||||
Reference in new issue
Block a user