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
|
`talkd`, siehe unten), `mute`, `unmute`, `status`, `cancel`. Die übrigen
|
||||||
Befehle sind Stubs und melden `not implemented yet` (Exit-Code ≠ 0).
|
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
|
`talkd` besitzt die Ansage-Queue und spricht seriell ab — genau eine Ansage
|
||||||
gleichzeitig, Reihenfolge `now` > `high` > `normal` > `low` (FIFO je
|
gleichzeitig, Reihenfolge `now` > `high` > `normal` > `low` (FIFO je
|
||||||
@@ -43,9 +43,12 @@ config `[server] port`, Env `TALK_PORT` (schlagen den Default).
|
|||||||
| Endpunkt | Wirkung |
|
| 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 |
|
| `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 /cancel` | bricht die laufende Ansage ab + leert die Queue → `200 {"cancelled":n}` |
|
||||||
| `POST /mute` / `POST /unmute` | persistenter Mute-State → `200 {"muted":bool}` |
|
| `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
|
- **CLI-Verhalten:** `talk say` (und `talk "Text"`) erkennt den Daemon via
|
||||||
`GET /status`. Läuft er: enqueue + sofortige Rückkehr (non-blocking,
|
`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,
|
erlaubt), nächster slot …` und `ruhezeit: 18:00-08:00 (modus discard,
|
||||||
aktiv bis 08:00)`.
|
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)
|
### Provider-Kette (Task 03)
|
||||||
|
|
||||||
Ohne Pin läuft die Vier-Stufen-Fallback-Kette
|
Ohne Pin läuft die Vier-Stufen-Fallback-Kette
|
||||||
|
|||||||
Reference in new issue
Block a user