# WOHNO — MCP-Server einrichten (Agent Setup)

Du bist ein KI-Assistent/Coding-Agent und sollst den **WOHNO-MCP-Server** als Connector
einrichten. WOHNO (Immobilien-Portal) ist sein eigener OAuth-2.1-Authorization-Server;
ein WOHNO-Kunde bindet damit sein eigenes LLM sicher an, um Inserate, Bewerbungen und
Besichtigungstermine zu **lesen und zu steuern**. Der Server ist **live** (OAuth + Tools).

## MCP-Server-Konfiguration (das hier einrichten)

- **Name:** `wohno`
- **Server-URL:** `https://wohno.de/api/mcp`  (MCP über Streamable HTTP)
- **Auth:** OAuth 2.1 (PKCE S256), Dynamic Client Registration — Details unten
- **Beispiel (MCP-Client-Config, z. B. Claude Code / ähnliche):**
  ```json
  {
    "mcpServers": {
      "wohno": {
        "url": "https://wohno.de/api/mcp",
        "transport": "streamable-http"
      }
    }
  }
  ```
  Der Client entdeckt den Authorization-Server automatisch über die `.well-known`-Metadaten
  unten und führt den OAuth-Flow beim ersten Aufruf durch. Falls dein Client OAuth nicht
  automatisch macht, führe die Schritte 1–4 manuell aus und setze das Access-Token als
  `Authorization: Bearer`.

## OAuth-2.1-Flow (falls manuell einzurichten)

1. **Discovery:** `GET https://wohno.de/.well-known/oauth-protected-resource` (Resource `https://wohno.de/api/mcp`
   + Authorization-Server) und `GET https://wohno.de/.well-known/oauth-authorization-server` (Endpoints,
   Grants, `code_challenge_methods_supported: ["S256"]`).
2. **Dynamic Client Registration:** `POST https://wohno.de/oauth/register` mit
   `{ "redirect_uris": ["<deine Callback-URL>"], "client_name": "<dein Client-Name>", "logo_uri": "<https-Logo, optional>" }`
   → `client_id` (public client). redirect_uris: exakt, https, ohne Fragment/Userinfo.
   `client_name`/`logo_uri` erscheinen auf dem Consent-Screen des Nutzers.
3. **Authorize + Consent (PKCE):** `code_verifier` (43–128 URL-safe) + `code_challenge =
   base64url(sha256(code_verifier))`; leite den Nutzer auf
   `https://wohno.de/oauth/authorize?response_type=code&client_id=<id>&redirect_uri=<cb>&code_challenge=<challenge>&code_challenge_method=S256&scope=<scopes>&resource=https://wohno.de/api/mcp&state=<state>`.
   Nutzer loggt sich ein, wählt ggf. Organisation(en)/Standard-Organisation, bestätigt. Du
   erhältst `code` (+ `state`).
4. **Token:** `POST https://wohno.de/oauth/token` (form):
   `grant_type=authorization_code&code=<code>&code_verifier=<verifier>&client_id=<id>&redirect_uri=<cb>`
   → `{ access_token, token_type: "Bearer", expires_in: 3600, refresh_token, scope }`.
5. **MCP-Aufrufe:** Access-Token als `Authorization: Bearer` an `https://wohno.de/api/mcp`.
   Bei `401` neu autorisieren; rotieren per `POST https://wohno.de/oauth/token` (form):
   `grant_type=refresh_token&refresh_token=<refresh_token>&client_id=<id>` — sende dabei
   IMMER deine `client_id` mit (dringend empfohlen, wird künftig Pflicht; eine fremde
   `client_id` wird mit `invalid_grant` abgewiesen). Refresh-Tokens sind single-use —
   Wiederverwendung sperrt die ganze Token-Familie.

## Verfügbare Tools (Überblick)

Ruf zuerst **`whoami`** auf — es liefert `connection_type`, gewährte `scopes` und `blocked_scopes`
(Scopes, die der Plan noch nicht freischaltet). Für ein persönliches Konto (`connection_type: "user"`)
liefert `whoami` zusätzlich `organizations` (deine aktiven Organisations-Mitgliedschaften) und
`default_organization_id` — beide nur, wenn die Verbindung Organisations-Scopes trägt — sowie
`has_seeker_profile` (nur aussagekräftig mit `seeker:*`-Scopes). Orientiere dich daran, bevor du ein
Organisations-Tool aufrufst. Gehörst du zu **mehreren** Organisationen, akzeptiert jedes Organisations-
Tool ein optionales `organization_id`-Argument (eine der IDs aus `whoami.organizations`); ohne
Argument wird deine Standard-Organisation verwendet. Fehlt eine Standard-Organisation und ist keine
`organization_id` angegeben, antwortet das Tool mit `organization_required` + der Org-Liste. Dann je Aufgabe:

- **Orientierung:** `whoami`, `get_usage` (Aufruf-Zähler — MCP ist quota-frei).
- **Inserate:** `list_listings`, `get_listing` (Detail: Beschreibung/Adresse/Amenities),
  `update_listing_status` (active/paused/rented), `get_listing_analytics` (Funnel).
- **Suche:** `search_discovery` (öffentliche Inserate über Anbieter hinweg).
- **Bewerbungen:** `list_applications`, `get_application` (beide **PII-reduziert**),
  `get_allowed_transitions`, `set_application_status`, `score_application` (Pro).
- **Termine:** `list_appointments`, `get_appointment`, `create_appointment_slot`.

## Wichtiges Verhalten

- **PII-Reduktion:** Bewerbungen enthalten NIE Name/Kontakt/Einkommen/SCHUFA/Anschreiben-Text —
  nur pseudonyme `applicant_ref`/`display_name`, Flags und aggregierte Werte.
- **Bewerber-Score:** `list_applications`/`get_application` zeigen den Gesamt-Score (`total`);
  die Kategorie-Aufschlüsselung ist über `score_application` und erst ab dem **Pro-Plan** verfügbar.
- **Zusage-Workflow:** Eine Bewerbung wird nur aus `invited` heraus `accepted`; `invited`
  setzt buchbare Besichtigungs-Slots voraus (sonst `viewing_slots_required`). Reihenfolge:
  `create_appointment_slot` → Status `invited` → `accepted`. Nutze `get_allowed_transitions`,
  um die von der aktuellen Lage erreichbaren Status zu sehen, bevor du schreibst.
- **Fehler sind strukturiert:** Der Fehlertext enthält `code` (z. B. `plan_required`,
  `insufficient_scope`, `invalid_transition`) und wo sinnvoll `required_plan`/`required_scope`
  und eine `remediation_url` — folge dem Code/der URL statt zu raten.
- **dryRun:** Schreib-Tools akzeptieren `dryRun: true` → Vorschau (`would_succeed`,
  `side_effects`) ohne Mutation.
- **Launch-Gate (Sucher):** solange die Plattform noch nicht live ist, liefern
  `submit_application`/`get_viewing_slots`/`book_viewing` `feature_unavailable` —
  `withdraw_application`/`cancel_viewing` funktionieren IMMER, unabhängig vom Launch-Status.

## Sucher-Pipeline (persönliche/`user`-Verbindung ohne Organisation)

Verbindest du dich **persönlich** (kein Organisations-Consent, sondern „Persönlich" beim
Autorisieren gewählt), bist du ein **Sucher** — die Tools oben (Organisations-Inserate,
-Bewerbungen, -Termine) sind für dich nicht sichtbar; stattdessen siehst du eine eigene
Sucher-Werkzeugkiste. Ruf auch hier zuerst **`whoami`** auf: für einen Sucher liefert es
zusätzlich `has_seeker_profile`, `application_folder_completeness` (Vollständigkeit deiner
Bewerbungsmappe), `search_profile_count` und **`next_steps`** — konkrete, ausführbare
nächste Schritte statt Rätselraten. Folge `next_steps` statt zu raten.

- **Suche:** `list_my_search_profiles` (deine gespeicherten Suchprofile), `create_search_profile` /
  `update_search_profile` / `delete_search_profile` (Budget/Ort/Zimmer/Merkmale — Beträge in EURO),
  `search_discovery` (öffentliche Inserate über alle Anbieter hinweg, mit direkt folgbarem Link je
  Treffer; `rentMax`-Filter UND `rent_cold`-Output ebenfalls EURO, konsistent zu den Suchprofilen).
- **Favoriten:** `list_my_favorites`, `add_favorite` / `remove_favorite`.
- **Bewerbungsmappe (Bewerbungs-Grundlage):** `get_my_application_folder` (vollständige Mappe
  inkl. sensibler Felder — verlangt `seeker:profile:write`, write-implies-read) /
  `update_application_folder` (Merge-Upsert: nur gesendete Felder ändern sich; `""` löscht ein
  Datumsfeld, `null` löscht ein nullable Feld, ein weggelassenes Feld bleibt unangetastet).
- **Bewerben:** `list_my_applications` / `get_my_application`, `submit_application` (verlangt
  eine Bewerbungsmappe — sonst `profile_required`; erst `dryRun: true` zur Vorschau,
  dann echt mit `idempotencyKey`), `withdraw_application` (immer möglich, auch außerhalb der
  Launch-Fenster).
- **Besichtigungen:** `get_viewing_slots` (freie Slots eines Inserats), `book_viewing`
  (verlangt eine aktive Bewerbung für dasselbe Inserat — sonst `application_required`),
  `cancel_viewing` (immer möglich).
- **Orientierung:** `whoami`, `get_usage`.

**Geführte Workflows (MCP-Prompts):** dein Client bietet dir ggf. `wohnungssuche_starten`,
`bewerbung_vorbereiten_und_abschicken` und `meine_bewerbungen_und_termine` als auswählbare
Prompts an — jeder spielt eine der obigen Aufgaben Schritt für Schritt über die echten Tools ab.
Sind sie in deinem Client nicht sichtbar, fasse den passenden Workflow-Text oben in eigene
Worte, statt zu raten.

## Scopes

Nur MCP-relevante Domänen-Scopes, z. B. `listings:read`, `listings:write`, `applications:read`,
`applications:write`, `appointments:read`, `appointments:write`, `discovery:read`, `analytics:read`,
`matching:score`, `usage:read`. Für eine persönliche Sucher-Verbindung: `seeker:searches:read/write`,
`seeker:profile:read/write`, `seeker:favorites:read/write`, `seeker:applications:read/write`,
`seeker:appointments:read/write`. Administrative Scopes (Key-/Mitglieder-/Webhook-/Org-Verwaltung)
sind für Agenten gesperrt. Ein Organisations-Write-Scope (`*:write`) schließt den zugehörigen
Lesezugriff mit ein — bei den Sucher-`seeker:*:write`-Scopes gilt das NICHT automatisch, außer bei
`seeker:profile:write` (schließt `get_my_application_folder` als write-implies-read ein); jedes
andere Sucher-Read-Tool prüft weiter sein eigenes `seeker:*:read`. Bei persönlichen (`user`-)
Verbindungen wird jeder Aufruf zusätzlich pro Tool gegen deine **aktuelle Rolle in der aufgelösten
Organisation** geprüft (Least-Privilege, live) — die gewährten Scopes sind die Obergrenze, nicht die Garantie.

## Hinweise

PKCE S256 Pflicht (`plain` abgelehnt) · Authorization-Codes einmalig (10 min) · `resource`
(`https://wohno.de/api/mcp`) am /authorize verpflichtend · Tokens sind opak (Bearer) — wie Geheimnisse behandeln.
Dein Connector cacht die Tool-Definitionen (Namen/Schemas) aus `tools/list` — nach
Schema-Änderungen auf WOHNO-Seite (z. B. ein neues `organization_id`-Argument) siehst du sie
erst nach einem **Neuverbinden** des Connectors, nicht während einer laufenden Session.
**Neue Scopes ebenso:** ein Refresh (`grant_type=refresh_token`) bewahrt exakt den
bestehenden Scope-Satz, erweitert ihn NIE — für einen zusätzlichen Scope Connector
vollständig entfernen + neu hinzufügen (startet den Consent-Flow neu, Schritte 1–4).

Integrations-Fragen: hallo@wohno.de
