# MCP-Server-Sicherheit: read-only, Token-Scopes und menschliche Freigabe

> Warum ein KI-Agent in deinem Uptimeify-Konto lesen darf, aber strukturell nichts schreiben kann.

Source: https://uptimeify.io/de/blog/mcp-server-sicherheit-read-only

Ein KI-Agent, der deine Monitoring-Daten liest, kann in deinem Uptimeify-Konto nichts verändern. Das ist keine Zusage, sondern eine Eigenschaft des Tokens: Jeder Schreibversuch eines Agent-Tokens endet mit `403` und dem Fehlercode `agentTokenReadOnly`. Ebenso jeder GET-Request außerhalb der Allowlist seines Scopes. Dieser Beitrag zeigt, wo diese Grenze verläuft, wer sie freigibt und wie du sie in einer Sekunde wieder schließt.

- Agent-Token sind read-only. Schreibversuche und Lesezugriffe außerhalb der Scope-Allowlist enden mit `403 agentTokenReadOnly`.
- Eine anonyme Registrierung erreicht nur `tools.public`, also ausschließlich die öffentlichen Check-Tools. Kontodaten sieht sie nicht.
- `api.read` gibt es erst nach einem Claim-Flow, den ein eingeloggter Mensch mit der exakt gebundenen E-Mail-Adresse bestätigt. Bei der anonymen Variante sieht nur dieser Mensch den Bestätigungscode, der Agent nie.
- Der Code läuft nach 600 Sekunden ab, der Access-Token nach 3.600 Sekunden. Der Widerruf über `/oauth2/revoke` ist idempotent und liefert immer `200`.

## Die eigentliche Frage: nicht "darf der Agent rein", sondern "was kann er anrichten"

Die meisten Sicherheitsgespräche über MCP-Server drehen sich um Zugang. Das ist die schwächere Frage. Zugang ist binär, Wirkung nicht. Vier Größen entscheiden über das tatsächliche Risiko: welche Verben ein Token kennt, welche Fläche es sieht, wie lange es lebt und wer es freigegeben hat.

Der Uptimeify-MCP-Server ist zustandslos und läuft unter `POST https://uptimeify.io/mcp`. Er stellt zwei Gruppen von Werkzeugen bereit. 20 Check-Tools arbeiten komplett ohne Konto und ohne Token: SSL-Prüfung, DNS-Lookup, DNS-Propagation, MX, SPF, DKIM, DMARC, DNSBL, WHOIS, Domain-Ablauf, HTTP-Header, HSTS, Redirects, Port-Check, Ping, Website-Status, Antwortzeit, IP-Geolocation, ASN und Reverse-DNS. Dreizehn weitere Tools lesen deine eigenen Monitoring- und Kontodaten und brauchen dafür ein Token: `list_monitors`, `monitor_status`, `list_incidents`, `check_history`, `uptime_summary`, `list_alert_channels`, `alert_history`, `list_status_pages`, `list_maintenance_windows`, `get_organization`, `list_users`, `list_customers` und `billing_summary`. Die letzten vier sind die engsten der Reihe: Sie geben eine feste Feldliste heraus und sonst nichts, es verlassen sie also weder Kontakt-E-Mail noch Anschrift, Umsatzsteuer-Identifikationsnummer, Telefonnummer, Tarifname oder Zahlungsmittel.

Keines dieser 33 Werkzeuge schreibt. Ein Agent, der für dich die TLS-Kette von `deinkunde.com` prüft oder nachsieht, ob der SPF-Record noch stimmt, braucht dafür überhaupt keinen Zugriff auf dein Konto. Das ist der erste und wirksamste Schnitt: Der größte Teil der typischen Agenten-Arbeit findet außerhalb deiner Daten statt.

## Read-only als Architektur, nicht als Einstellung

Read-only ist bei Agent-Token kein Häkchen in einem Formular, sondern die Eigenschaft des Tokens. Jeder schreibende Aufruf und jeder `GET` oder `HEAD` außerhalb der Allowlist des jeweiligen Scopes wird mit 403 und `data.code = agentTokenReadOnly` abgewiesen. Es gibt keinen Pfad, über den ein Agent-Token einen Monitor anlegt, einen Kunden ändert oder einen Incident schließt. Diese Operationen bleiben Menschen mit Login oder klassischen API-Token vorbehalten.

Es gibt genau zwei Scopes:

| Scope | Wie er entsteht | Was er erreicht |
| --- | --- | --- |
| `tools.public` | anonyme Registrierung, sofort aktiv, keine Freigabe nötig | ausschließlich die öffentlichen Check-Tools unter `GET /api/tools/*` |
| `api.read` | erst nach bestätigtem Claim durch einen eingeloggten Menschen | zusätzlich `GET /api/websites*`, `GET /api/incidents*` und `GET /api/health`, begrenzt auf die Organisation des freigebenden Nutzers oder einen einzelnen Kunden darin |

Die Registrierungs- und Token-Endpunkte werden nur auf dem kanonischen Host `uptimeify.io` ausgeliefert. Auf einer White-Label-Domain deiner Agentur antworten sie mit `404`. Deine Kunden-Statusseiten bleiben damit außerhalb des Auth-Flows, auch wenn sie unter deiner eigenen Domain laufen.

Read-only heißt hier nicht "der Agent verzichtet freiwillig", sondern "der Schreibpfad existiert für dieses Token nicht".

## Wer den Zugriff freigibt: der Mensch, nicht der Agent

Damit ein Agent überhaupt Kontodaten lesen darf, muss ein eingeloggter Mensch das bestätigen. Der Claim-Flow kennt zwei Wege, und der Unterschied ist der interessante Teil.

Bei einer `service_auth`-Registrierung startet die Zeremonie sofort. Die Registrierung steht auf `pending` und stellt kein Token aus, bis ein angemeldeter Nutzer sie autorisiert. Der Agent bekommt den `user_code` und die `verification_uri` zurück und zeigt beides seinem Menschen an, wie bei einem Device-Flow am Fernseher.

Bei einer anonymen Registrierung ist es strenger. Sie funktioniert sofort mit `tools.public` und kann optional später auf `api.read` gehoben werden. In diesem Fall bekommt der Agent den `user_code` nie zu sehen. Er erhält nur eine Verification-URL. Den Code sieht ausschließlich der eingeloggte Nutzer mit der exakt gebundenen E-Mail-Adresse auf der Claim-Seite. Ein Agent kann sich seine eigene Erweiterung damit nicht selbst zusammenreimen, auch dann nicht, wenn er die Registrierung vollständig kontrolliert.

Zwei weitere Eigenschaften machen den Flow prüfbar:

- Eine Registrierung ist genau einmal claimbar. Ein zweiter Versuch endet mit `409 claimed_or_in_flight`.
- Der resultierende Scope hängt am freigebenden Menschen. Ein Token reicht nie weiter als der Nutzer, der es autorisiert hat, entweder auf dessen Organisation oder auf einen einzelnen Kunden darin.

Damit ist die häufigste Rückfrage aus Kundengesprächen beantwortet, bevor sie kommt: Nein, ein Agent sieht nicht die Daten anderer Mandanten, und nein, er kann sich den Zugriff nicht selbst erteilen.

Welche Tools der MCP-Server anbietet, welche davon ohne Konto laufen und wie du ihn in deinem Client einträgst, steht auf der Übersichtsseite.

## Token-Lebensdauer und Widerruf

Kurze Laufzeiten sind die zweite Verteidigungslinie nach dem Scope. Wenn ein Token nur eine Stunde lebt, ist ein Leak ein Vorfall mit Ablaufdatum statt eines Dauerzustands.

- Der **Access-Token** ist opak, beginnt mit `wsma_` und läuft nach 3.600 Sekunden ab. Danach stellt der Agent ihn aus seiner Identity-Assertion neu aus.
- Die **Identity-Assertion** ist ein EdDSA-JWT mit rund 24 Stunden Gültigkeit. Sie ist das dauerhafte Credential, nicht der Access-Token.
- Der **Bestätigungscode** im Claim-Flow und das zugehörige Claim-Token verfallen nach 600 Sekunden. Wer zu langsam ist, startet die Zeremonie neu.
- Der **Widerruf** über `POST /oauth2/revoke` ist idempotent und liefert immer `200`, auch für ein unbekanntes oder längst widerrufenes Token. Das Ergebnis ist eindeutig, statt dich in eine Fehlersuche zu schicken.

Dazu kommen Limits, die einen entlaufenen Agenten früh bremsen: 120 Anfragen pro Minute und IP auf dem MCP-Endpunkt, 15 bis 30 pro Minute je anonymem Tool, 60 pro Minute je authentifiziertem Tool. Wer darüber liegt, bekommt `429`. Die Registrierung ist auf 30 Anfragen pro Minute begrenzt, der Token-Endpunkt auf 60.

## Was du behandeln musst wie ein Passwort

An genau einer Stelle liegt das Geheimnis nicht im Header, sondern im Pfad. Wenn du im Incident Management eine Alarmquelle anlegst, etwa für Zabbix, Grafana, Datadog, Sentry, Prometheus Alertmanager oder einen eigenen Webhook, erzeugt Uptimeify eine Ingest-URL der Form `https://<deine-domain>/api/im/ingest/<token>`. Das Token ist Teil der URL. Die vollständige URL ist deshalb wie ein Passwort zu behandeln: nicht in ein öffentliches Repository, nicht in ein Ticket, nicht in einen geteilten Screenshot.

Drei Details dazu, die den Umgang leichter machen:

- Die URL wird genau einmal im Klartext angezeigt, direkt nach dem Anlegen der Quelle. Danach nie wieder.
- Sie lässt sich jederzeit rotieren. Im Tab "Einstellungen" der Alarmquelle erzeugst du eine frische URL, die alte verfällt.
- Ein unbekanntes oder wegrotiertes Token liefert `404 not_found`. Die Antwort verrät nicht, ob es dieses Token je gegeben hat. Das ist bewusst so gebaut und nimmt einem Scanner die Rückmeldung, die er zum Raten bräuchte.

Für die klassischen API-Token gilt dieselbe Disziplin. Sie beginnen mit `wsm_`, werden nur einmal vollständig ausgegeben und lassen sich beim Anlegen mit einer Laufzeit zwischen 1 und 365 Tagen versehen. Wer sie auf einen einzelnen Kunden begrenzt, begrenzt automatisch auch den Schaden. Was ein Token technisch ist und wofür du es einsetzt, steht im Glossareintrag zum API-Token.

## Wo die Daten liegen

Alles, was über den MCP-Server abgefragt wird, läuft auf derselben Infrastruktur wie der Rest der Plattform: European-only Stack, Frankfurt, ohne US-Sub-Prozessoren. Der MCP-Server ist kein separater Dienst mit eigener Datenhaltung, sondern eine Lesefläche auf die bestehende API. Er kopiert nichts an einen anderen Ort. Die aktuelle Liste der eingesetzten Dienstleister steht offen auf der Seite zu den [Subprozessoren](/de/subprozessoren), die technischen Details zu Verschlüsselung, Zugriffskontrolle und Betrieb auf den Seiten zu [DSGVO und EU-Hosting](/de/funktionen/vertrauen-und-compliance/dsgvo-eu-hosting) sowie [Enterprise-Sicherheit](/de/funktionen/vertrauen-und-compliance/enterprise-sicherheit).

Zum Datenweg gehört auch, wie lange etwas bleibt. Im Incident Management werden Alert-Payloads 90 Tage nach Eingang genullt, aufgelöste Alerts 90 Tage nach Eingang gelöscht, ebenso Zustellprotokolle ausgehender Integrationen 90 Tage nach dem Versand. Der Incident selbst und seine Timeline bleiben 12 Monate vollständig erhalten, danach überlebt nur noch eine tägliche Aggregation aus Fallzahlen sowie Bestätigungs- und Lösungszeiten, insgesamt 24 Monate. Ein Agent mit `api.read` sieht ohnehin nur die normalisierte Sicht, nie den rohen Payload einer Alarmquelle. Der wird von dieser Schnittstelle grundsätzlich nicht ausgeliefert, weil in ihm stehen kann, was der Betreiber der Quelle hineingeschrieben hat, inklusive fremder Schlüssel.

Wenn du Lücken in unserer Absicherung findest, ist der Weg dafür dokumentiert: [Security Policy](/de/security/policy).

## Checkliste vor der Freigabe

Bevor du einem Agenten `api.read` gibst, geh diese sieben Punkte durch. Sie dauern zusammen keine zehn Minuten und ersparen dir die unangenehme Version des Gesprächs.

1. **Braucht der Agent überhaupt Kontodaten?** Für Checks gegen fremde Domains reichen die öffentlichen Tools ohne jede Registrierung.
2. **Wer bestätigt die Freigabe?** Der Claim hängt an einer konkreten, eingeloggten Person mit gebundener E-Mail-Adresse. Halte fest, wer das war.
3. **Auf welchen Ausschnitt?** Organisation oder einzelner Kunde. Im Zweifel der kleinere Ausschnitt.
4. **Wo läuft der Agent?** Ein Token in einem lokalen Client ist etwas anderes als eines in einer geteilten Automatisierung. Der Ablauf nach einer Stunde hilft, ersetzt aber keine Ablage-Disziplin.
5. **Sind Ingest-URLs sauber abgelegt?** Sie enthalten ihr Token im Pfad. Sichtbar in Repos, Tickets oder Screenshots heißt: rotieren.
6. **Ist der Widerrufsweg bekannt?** `POST /oauth2/revoke`, idempotent, immer `200`. Wer im Vorfall erst sucht, verliert Minuten.
7. **Kannst du die Antwort auf die Datenfrage belegen?** Standort, Sub-Prozessoren und Aufbewahrungsfristen stehen dokumentiert. Verlink sie im Angebot, statt sie zu beschreiben.

Sicherheit bei Agenten-Zugriff entsteht nicht dadurch, dass jemand verspricht, vorsichtig zu sein. Sie entsteht dadurch, dass die riskante Operation gar keinen Pfad hat. Alles andere ist Vertrauen ohne Beleg.
