Metadata-Version: 2.4
Name: fyos-hub
Version: 0.1.0
Summary: Der MCP-Hub von FYOS - For Your Own Sake: ein Server, viele Plugins, Apps mit Oberflaeche.
Author: sirfyyn and FYOS CONTRIBUTORS
License-Expression: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: mcp==2.1.1
Requires-Dist: pydantic>=2
Requires-Dist: pyyaml>=6
Requires-Dist: uvicorn[standard]>=0.30
Requires-Dist: starlette>=0.40
Requires-Dist: httpx>=0.27
Requires-Dist: pyjwt[crypto]>=2.8
Requires-Dist: redis>=5.0
Provides-Extra: suche
Requires-Dist: qdrant-client>=1.10; extra == "suche"
Requires-Dist: numpy>=1.26; extra == "suche"
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Requires-Dist: numpy>=1.26; extra == "test"
Requires-Dist: qdrant-client>=1.10; extra == "test"
Requires-Dist: fakeredis>=2.20; extra == "test"
Dynamic: license-file

# FYOS-Hub

Der Hub von FYOS (For Your Own Sake): ein MCP-Server, der viele Plugins unter einer Adresse anbietet.
Ein Plugin liefert Werkzeuge und, wenn es will, eine Oberfläche (`ui://`-HTML). Die Shell zeigt diese
Oberflächen als Fenster, jeder MCP-Client mit MCP-Apps-Unterstützung im Gespräch.

Der Hub spricht dreierlei:

- **MCP** unter `/mcp` (Streamable HTTP) für Agenten und Chat-Clients,
- **REST** unter `/hub/*` für die Shell und Skripte,
- **Gesundheit** unter `/health`.

Die genauen Wege und Formate stehen in [`docs/HUB-VERTRAG.md`](../docs/HUB-VERTRAG.md).

## Installieren und starten

```
pip install ".[suche]"          # Kern; [suche] bringt Qdrant-Client und numpy (optional)
fyos-hub                        # oder: python3 -m mcp_hub.server
```

Als Bild: `docker build -t fyos-hub hub/` (Build-Argument `EXTRA_PIP` installiert weitere Plugin-Pakete, z. B.
`--build-arg EXTRA_PIP="fyos-plugin-finanzen"`). Das Bild lauscht auf Port 8080.

Das Bild installiert Plugin-Pakete mit `uv` statt mit `pip`. Liegen sie in einem eigenen Paketindex, kommt dessen Adresse als
`--build-arg FYOS_INDEX_URL=<Index-URL>` und steht damit vor PyPI. `--index-strategy first-index` steht ausdrücklich im Befehl:
uv nimmt ein Paket nur aus dem ersten Index, der es führt, ein Paket gleichen Namens auf PyPI kann das eigene also nicht
verdrängen (Dependency Confusion; mit `pip --extra-index-url` wäre das möglich). Die Version von uv ist im Dockerfile fest.

Ohne Konfigurationsdatei lädt der Hub jedes installierte Plugin der Entry-Point-Gruppe `mcp_hub.plugins`
mit seinen Vorgaben. Eine Datei (`MCP_HUB_CONFIG_PATH`, Vorgabe `./config.yaml`) braucht es nur für
Plugin-Einstellungen, Geheimnisse oder `enabled: false`; siehe `config.example.yaml`.

## Mitgelieferte Plugins

| Plugin | Zweck |
|---|---|
| `skills` | Arbeitsanleitungen (`<quelle>/<name>/SKILL.md`) durchsuchen und lesen |
| `werkstatt` | angemeldete Skripte (Kopfblock `WERKSTATT`) finden und mit Trockenlauf ausführen |
| `mcp` | Werkzeugkasten: Plugins, Werkzeuge, Apps und Protokolle des Hubs ansehen |
| `shell` | Anordnung der offenen Shell (Fenster, Raster) als Werkzeuge |
| `audit` | Protokoll aller Werkzeugaufrufe (Ringpuffer, mit Qdrant Vektorsuche) |
| `event_bus_publisher` | Ereignisse je Werkzeugaufruf auf Redis-Streams (sonst im Speicher) |
| `beispiel_app` | Notizzettel mit Oberfläche, Vorlage für eigene Apps |
| `echo`, `math` | Referenz-Plugins, standardmäßig aus |

Alles Weitere (Finanzen, Haus, Betrieb …) sind eigene Pakete, die sich per Entry-Point melden.

## Ein eigenes Plugin

```
# pyproject.toml des Plugin-Pakets
[project.entry-points."mcp_hub.plugins"]
meins = "mein_paket.plugin:MeinPlugin"
```

```
uv pip install ./mein-plugin    # danach lädt der Hub es ohne Eintrag in config.yaml
```

Die Klasse erbt von `mcp_hub.plugin.HubPlugin` (`register`, `describe_tools`, `call`, optional `health`,
`describe_resources`). Hat das Plugin eine Oberfläche, liegen die HTML-Dateien in `<paket>/ui/`; das Plugin
übergibt den Paketnamen an `mcp_hub.plugins._oberflaeche.oberflaeche(datei, "mein_paket")`, und
`_identitaet.registriere_app(...)` gibt der App Titel, Symbol und Farbton. `plugins/beispiel_app.py` zeigt alles
in 200 Zeilen.

## Umgebungsvariablen

| Variable | Wirkung |
|---|---|
| `MCP_HUB_CONFIG_PATH` | Pfad der YAML-Datei |
| `MCP_HUB_HOST`, `MCP_HUB_PORT`, `MCP_HUB_PATH` | Adresse, Port (8080), MCP-Pfad (`/mcp`) |
| `FYOS_OEFFENTLICH_URL` | öffentliche Adresse der Installation (Links, erlaubter Origin, Zielgruppe der Anmeldung) |
| `FYOS_ERLAUBTE_HOSTS` | weitere erlaubte Hostnamen/Origins, mit Komma getrennt (Vorgabe: `localhost`, `127.0.0.1`) |
| `FYOS_OS_URL` | Adresse der Shell, falls nicht `<FYOS_OEFFENTLICH_URL>/os` |
| `TZ` | Zeitzone des Hubs (Tageszähler und Zeitfenster der Takte), z. B. `Europe/Berlin`; ohne Angabe UTC |
| `REDIS_URL` | Redis für Ansichten, Suchspur, Ereignisse, OS-Brücke; ohne: Speicher im Prozess |
| `QDRANT_URL` | Vektorsuche im Audit-Protokoll; ohne: Ringpuffer |
| `FYOS_EMBED_URL`, `FYOS_EMBED_MODEL`, `FYOS_EMBED_KEY` | OpenAI-kompatibler Einbettungsdienst (`/v1/embeddings`) für Audit und Werkzeugsuche |
| `FYOS_SKILLS_PATH` | Skill-Quellen, mit `:` getrennt (Vorgabe `/opt/fyos/skills`) |
| `FYOS_REPO_PATH` | Repo der Werkstatt (Vorgabe `/opt/fyos/repo`) |
| `FYOS_OIDC_ISSUER` | schaltet die Anmeldung ein (siehe unten) |

Die OS-Brücke braucht für mehrere Replikate Redis; ein einzelner Hub funktioniert nur mit `REDIS_URL`.

## Takte begrenzen

Wiederkehrende Aufgaben eines Plugins erscheinen in `mcp.zeitplan` und `/metrics`. Fährt das Plugin sie über den
Wächter `mcp_hub.takt`, können sie zwei Grenzen haben: `max_pro_tag` (Läufe je Tag) und `fenster` (Uhrzeiten wie
`"07:00-22:00"`, auch über Mitternacht). Außerhalb des Fensters oder über dem Limit läuft die Aufgabe nicht; der Hub
zählt die übersprungenen Läufe (`fyos_zeitplan_uebersprungen_total{takt, grund}`, dazu `fyos_zeitplan_laeufe_heute{takt}`)
und führt je Takt ein knappes Laufprotokoll (Start, Dauer, Ergebnis; die letzten 20 Läufe) im Hub-Speicher. Tag und
Uhrzeit gelten in der Zeitzone des Hubs (`TZ`). Ohne Grenzen läuft alles wie vorher. Die Takte des mitgelieferten
Plugins `openclaw` stellt `takt_grenzen` in seiner `config` ein (`config.example.yaml`); eigene Plugins binden den
Wächter nach [`docs/ERWEITERN.md`](../docs/ERWEITERN.md) §2.12 ein.

## Anmeldung (optional)

Innen, im eigenen Netz, ist der Hub offen. Soll er auch aus dem Internet erreichbar sein, genügt ein
OpenID-Connect-Anbieter: `FYOS_OIDC_ISSUER` und `FYOS_OIDC_CLIENTS` (Client-IDs, mit Komma) setzen, der Reverse-Proxy
setzt auf Anfragen aus dem Internet den Kopf `X-FYOS-Zugang: extern`. Dann verlangt `/mcp` für diese Anfragen ein
gültiges Token (RFC 9728, Discovery der Schlüssel, nur RS256/ES256/EdDSA). Weitere Schalter: `FYOS_OIDC_MODUS`
(`aus`, `an`, `streng`), `FYOS_OIDC_HOSTS`, `FYOS_OIDC_REST`, `FYOS_OIDC_BENUTZER`, `FYOS_OIDC_SCOPES`,
`FYOS_OIDC_JWKS_URL`. Ohne `FYOS_OIDC_ISSUER` antworten Anfragen von außen mit 403.

## Bewusst nicht enthalten

Aus dem Ursprung dieses Hubs sind nur die allgemeinen Teile übernommen. Nicht enthalten sind die Plugins
`wissen` und `gedaechtnis`, die Entwicklungsansicht (`entwicklung`, `lage`), das Chat-Tor (`chat_gateway`:
der Hub ist selbst ein OIDC-geschützter Ressourcenserver) und alles, was zu einer einzelnen Installation gehört.
Solche Plugins gehören in eigene Pakete.

## Tests

```
pip install -e ".[suche,test]"
python3 -m pytest hub/tests -q
```
