Die meisten Anleitungen zum Model Context Protocol gehen davon aus, dass Sie einen Server vor einem Shop oder einer Datenbank bauen, etwas mit Bestellungen und Lagerbeständen. Wir haben das weniger Naheliegende getan: Wir haben eine statische Marketing-Website in einen aktiven, schreibgeschützten MCP-Server verwandelt. Jeder MCP-Client kann jetzt einen POST an einen einzigen Endpunkt senden und in typisiertem JSON-RPC fragen, welche Leistungen es gibt oder wie man eine Anfrage startet, ohne eine einzige HTML-Seite zu parsen.
Dies ist ein Praxisbericht über diesen Aufbau. Er ist bewusst schmal gehalten: eine Handvoll schreibgeschützter Tools, keine Datenbank, keine Schreibvorgänge. Diese Schmalheit ist der Kern, und die meisten interessanten Entscheidungen ergeben sich daraus. Es folgt, warum eine Content-Website überhaupt von einem MCP-Endpunkt profitiert, warum der Endpunkt eine Cloudflare Pages Function statt einer Framework-Route ist, warum wir JSON-RPC selbst geschrieben haben, statt das SDK einzubinden, und der eine Bug mit abschließendem Schrägstrich, der jeden POST stillschweigend zerstört, wenn Sie ihn übersehen.
Wozu ein MCP-Server auf einer statischen Marketing-Website
Ein Shop stellt MCP bereit, weil ein Agent eine Aktion ausführen muss: Lagerbestand prüfen, einen Warenkorb aufbauen, eine Bestellung aufgeben. Eine Marketing-Website hat keinen Warenkorb. Wofür ist die Tool-Oberfläche also da?
Die ehrliche Antwort ist Agent-Discovery. Unsere Website betrieb bereits eine Agent-Discovery-Schicht: eine llms.txt, einen Satz von .well-known-Dokumenten und eine MCP-Server-Card unter /.well-known/mcp/server-card.json. Diese Card beschrieb einen Server, seinen Transport und seine Fähigkeiten. Sie deklarierte sogar eine tools-Fähigkeit. Es gab ein Problem: Dahinter lebte nichts. Die Card bewarb einen Server, der nicht existierte. Ein Agent, der der Card folgte und eine Verbindung aufzubauen versuchte, bekam nichts.
Das ist ein häufiger Zustand für diese Dateien. Sie werden generiert, sie werden in einem Link-Header beworben, und dann verweisen sie auf eine Ressource, die nie gebaut wurde. Die Card ist ein Versprechen ohne Implementierung.
Das Ziel war also nicht, eine neue Oberfläche zu erfinden. Es war, eine bestehende, beworbene Oberfläche real zu machen. Die Tools ergeben sich direkt daraus, wofür eine Marketing-Website da ist:
check_servicesgibt den Leistungskatalog zurück: id, Name, Beschreibung, Kategorie und kanonische URL.request_quotegibt die lokalisierte Kontakt-URL und Anweisungen zum Einreichen einer Anfrage zurück.
Die erste Version lief mit diesen beiden. Drei weitere schreibgeschützte Tools kamen später dazu, jedes als neuer Eintrag im Array TOOLS und als neuer Zweig in callTool, ohne Änderung am Transport:
check_tech_stackgibt den Technologie-Stack zurück, mit dem wir arbeiten, nach Kategorien gruppiert und optional nach Kategorie gefiltert.get_case_studiesgibt die Fallstudien mit ihrem Stack zurück, optional gefiltert nach einem Suchbegriff.search_blog_postsdurchsucht den Blog nach Titel, Beschreibung, Kategorien, Tags oder Slug, mit optionalem Sprach- und Kategoriefilter und einer Begrenzung der Trefferzahl.
Fünf Tools, alle schreibgeschützt. Ein Agent kann jetzt aufzählen, was wir tun, und einem Nutzer deterministisch den richtigen nächsten Schritt geben, statt aus Prosa zu raten.
MCP-Server als Cloudflare Pages Function betreiben
Die Website wird mit Astro im statischen Ausgabemodus gebaut. Es gibt keinen SSR-Adapter. Jede Seite wird zur Build-Zeit zu HTML vorgerendert, und die API-artigen Dateien, die sie ausliefert (der Leistungskatalog als JSON, das Agent-Profil), sind statische Dateien unter public/, keine Server-Routen.
Diese eine Tatsache treibt das gesamte Design. Ein dynamischer Endpunkt, der einen Request-Body liest und eine berechnete Antwort zurückgibt, kann hier keine Astro-Route sein, weil es keinen Server gibt, der ihn ausführt. Einen hinzuzufügen würde bedeuten, den Ausgabemodus auf hybrid oder server umzustellen und einen Adapter anzuhängen, was eine große, riskante Änderung an einem Build ist, der bereits Tausende von Seiten erzeugt.
Die Website betreibt allerdings bereits handgeschriebene Cloudflare Pages Functions: eine Middleware, die Weiterleitungen behandelt, und ein paar kleine API-Handler. Das ist die Nahtstelle. Ein dynamischer MCP-Endpunkt ist einfach eine weitere Pages Function, functions/mcp.ts, die neben den anderen sitzt. Sie wird mit dem normalen Build ausgeliefert, braucht keinen Adapter und ändert nichts daran, wie die Seiten gerendert werden.
Die Lektion lässt sich verallgemeinern. Wenn Sie einer ansonsten statischen Website einen dynamischen Endpunkt hinzufügen wollen, greifen Sie nicht zum Server-Modus des Frameworks. Greifen Sie zu dem Edge-Function-Primitiv, das Ihr Host Ihnen ohnehin schon gibt. Bei Cloudflare Pages ist das eine Function. Der Preis ist eine Datei, kein Neuaufbau Ihres Rendering-Modells.
Wie man einen MCP-Server ohne offizielles SDK baut
Der Reflex, wenn Sie “MCP-Server” lesen, ist, @modelcontextprotocol/sdk zu installieren. Für einen stdio-Server mit vielen Tools, Ressourcen und Prompts ist dieser Reflex richtig. Für wenige schreibgeschützte Tools auf einer Edge-Function ist er es nicht.
Das MCP-Wire-Protokoll ist JSON-RPC 2.0. Ein Server, der einem MCP-Client antwortet, muss einen kleinen Satz von Methoden behandeln:
initialize, wo der Server seine Protokollversion, Fähigkeiten und Identität zurückgibt.tools/list, wo er die Tool-Definitionen mit ihrem JSON Schema zurückgibt.tools/call, wo er ein benanntes Tool ausführt und das Ergebnis in MCP-Content verpackt zurückgibt.- Benachrichtigungen wie
notifications/initialized, die keine id tragen und keine Antwort erwarten.
Das ist die gesamte Oberfläche für einen schreibgeschützten Server. Sie von Hand zu implementieren sind ungefähr 120 Zeilen. Der Dispatch ist ein einfaches switch:
switch (method) {
case "initialize":
return ok({ protocolVersion, capabilities: { tools: { listChanged: false } }, serverInfo });
case "tools/list":
return ok({ tools: TOOLS });
case "tools/call":
return ok({ content: [{ type: "text", text: callTool(name, args, services).text }] });
default:
return err(-32601, `Method not found: ${method}`);
}Wägen Sie das gegen das SDK ab. Das SDK ist für stdio und für das vollständige Protokoll ausgelegt. Auf einer Edge-Laufzeit erben Sie seine Bundling-Annahmen und seine Transport-Maschinerie für Funktionen, die Sie nicht nutzen. Für wenige Read-Tools ist eine Abhängigkeit, über die Sie nachdenken müssen, ein schlechterer Handel als 120 Zeilen, die Sie vollständig kontrollieren. Das ist dieselbe Read-only-First-Zurückhaltung, angewandt auf Abhängigkeiten: das Minimum bereitstellen, das Minimum besitzen.
Die eine Sache, die es wert ist, von Hand sorgfältig zu machen, ist der Fehlervertrag. JSON-RPC hat definierte Fehlercodes, und MCP-Clients erwarten sie: -32700 für einen Parse-Fehler, -32601 für eine unbekannte Methode, -32602 für schlechte Parameter. Diese richtig hinzubekommen ist das, was einen selbst geschriebenen Server für einen strengen Client wie einen echten wirken lässt.
Warum sollte ein öffentlicher MCP-Server schreibgeschützt sein
Das verlockende nächste Tool ist eines, das einen Lead erfasst: einen Namen, eine E-Mail und eine Nachricht entgegennehmen und sie uns mailen. Bauen Sie das nicht als offenes MCP-Tool.
Ein öffentlicher Endpunkt mit einem schreibfähigen Tool ist eine Spam-Senke. Jeder im Internet kann tools/call mit submit_quote und einer gefälschten Nutzlast aufrufen, und schon ist Ihr Posteingang, Ihr CRM oder Ihre Datenbank ein Ziel ohne Reibung und ohne Mensch im Ablauf. In dem Moment, in dem ein Tool schreibt, braucht der Endpunkt Authentifizierung, Rate-Limiting und Missbrauchsbehandlung, was eine große Oberfläche ist, die eine Marketing-Website verteidigen muss.
Also schreibt request_quote nichts. Es gibt die lokalisierte Kontakt-URL und strukturierte Anleitung zurück:
if (name === "request_quote") {
const lang = LOCALES.includes(String(args?.lang)) ? String(args.lang) : "en";
return { text: JSON.stringify({
contact_url: CONTACT_URLS[lang],
method: "web-form",
note: "Read-only endpoint. Submit the inquiry through the contact form; this tool does not send it for you.",
reply_time: "within one working day",
}, null, 2) };
}Der Agent erhält alles, was er braucht, um den Nutzer voranzubringen: die richtige Kontaktseite für die Sprache des Nutzers und eine klare Aussage, dass die Einreichung über das Formular erfolgt. Der Mensch bleibt im Ablauf. Der Endpunkt hat nichts zu missbrauchen.
Das ist kein schwächeres Design, das durch Vorsicht erzwungen wird. Es ist dieselbe Haltung, die wir Kunden empfehlen, die MCP für ihre eigenen Systeme bauen: Read-only First, Schreibvorgänge nur hinter Authentifizierung hinzufügen, wenn es einen konkreten Grund gibt. Ein schreibgeschützter Server ist ehrlich darüber, was er ist, und es ist sicher, ihn offen zu lassen.
| Design-Entscheidung | Offenes Lese-Schreib-Tool | Schreibgeschützte Übergabe |
|---|---|---|
| Authentifizierung nötig | Ja | Nein |
| Spam-Oberfläche | Hoch | Keine |
| Mensch im Ablauf | Optional | Immer |
| Zu verteidigender Code | Rate-Limit, Validierung, Missbrauchsbehandlung | Keiner |
| Eignung für eine öffentliche Marketing-Website | Schlecht | Gut |
Warum ein Trailing-Slash-Redirect den POST-Body verwirft
Diese hat echte Debugging-Zeit gekostet, daher ist sie es wert, klar ausgesprochen zu werden.
Unsere Website kanonisiert URLs auf einen abschließenden Schrägstrich. Die Middleware leitet jeden Pfad ohne einen per 301 auf die Version mit Schrägstrich um. Das ist für Seiten in Ordnung. Für einen JSON-RPC-Endpunkt ist es eine stille Katastrophe.
Ein POST /mcp trifft die Weiterleitung und gibt 301 auf /mcp/ zurück. Viele HTTP-Clients senden, wenn sie der Weiterleitung folgen, den POST-Body nicht erneut oder stufen die Methode herab. Der MCP-Client sieht eine leere oder fehlgeschlagene Antwort und schließt daraus, dass der Server kaputt ist. Der Server ist in Ordnung. Der Request kam nie mit seinem Body an.
Die Lösung besteht nicht darin, die Middleware zu bekämpfen. Sie besteht darin, die URL zu bewerben, die die Website tatsächlich ausliefert. Die MCP-Server-Card und jede Discovery-Referenz verweisen auf /mcp/, mit dem abschließenden Schrägstrich, sodass ein wohlerzogener Client direkt an die kanonische URL postet und die Weiterleitung nie berührt. Wenn Ihr Framework oder Host Schrägstriche normalisiert, entscheiden Sie, welche Form kanonisch ist, und bewerben Sie überall genau diese Form.
Sie können den aktiven Endpunkt auf dieselbe Weise überprüfen wie wir:
curl -s -X POST https://wppoland.com/mcp/ \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Ein POST ohne Schrägstrich an denselben Host zeigt Ihnen stattdessen das 301.
Wie ein MCP-Server bei GEO und KI-Agenten hilft
Generative Engine Optimization dreht sich vor allem darum, für Modelle lesbar und überprüfbar zu sein. Strukturierte Daten, klare Entitäten, konsistente Aussagen über Ihre eigenen Oberflächen und externe hinweg. Ein aktiver MCP-Endpunkt ist eine starke Version dieser Lesbarkeit: Er ist kein Hinweis auf Ihren Inhalt, er ist eine aufrufbare Schnittstelle zu ihm.
Zwei Dinge machen ihn den geringen Aufwand wert, auch wenn die Verbreitung noch früh ist. Erstens schließt er die Lücke zwischen dem, was die Discovery-Schicht bewirbt, und dem, was existiert. Eine Card, die auf einen funktionierenden Server verweist, ist ein kohärentes Signal; eine Card, die auf nichts verweist, ist ein kaputtes, und kaputte Signale sind schlimmer als fehlende. Zweitens ist der Endpunkt für jeden, der diese Fähigkeit verkauft, ein Beweis. Wir bauen MCP-Server für Kunden, und die glaubwürdigste Demonstration davon ist ein öffentlicher, schreibgeschützter, der auf unserer eigenen Domain läuft, neben dem quelloffenen WooCommerce-MCP-Server, den wir veröffentlichen.
Kein großer KI-Anbieter verpflichtet sich heute formal dazu, MCP-Server-Cards oder llms.txt zu lesen. Das ist ein fairer Vorbehalt, und wir sagen ihn klar. Der Endpunkt ist günstig im Betrieb, er macht ein beworbenes Versprechen wahr, und er ist ein funktionierendes Artefakt statt einer Behauptung. Diese drei zusammen überspringen die Latte.
MCP-Telemetrie einer anderen Website, Oktober 2026: Amir Pournasserian hat den Verkehr auf dem MCP-Server seiner Website mitgezählt, dreieinhalb Tage lang (14. bis 18. Oktober 2026). Clients öffneten rund 300 Verbindungen (308 initialize, 281 tools/list) und machten 29 Tool-Aufrufe, 15 Suchen und 14 Abrufe. Viele nannten sich selbst MCP- oder Agentenverzeichnisse und Ranglisten. Sie fragten außerdem rund ein Dutzend Adressen zur Server-Erkennung ab, die sein Server nicht hat, darunter /.well-known/mcp/server-card.json, das der Autor auf frühere Entwürfe der Server Cards zurückführt. Unter genau dieser Adresse liegt unsere Card, verlinkt aus unserer /.well-known/ai-catalog.json. Der Vorschlag MCP Server Cards, SEP-2127, inzwischen mit Status Final, erlaubt Cards unter jeder nicht reservierten Adresse, auf die ein AI Catalog verweist, und empfiehlt <streamable-http-url>/server-card. Einen verbreiteten Client, der einem Katalog von selbst folgt, fand der Autor nicht. Claude Code, Claude.ai, VS Code und Cursor übernehmen die Server-URL vom Nutzer oder aus ihrer eigenen Galerie. Eine private Website, dreieinhalb Tage Daten. Die meisten Clients holten die Tool-Liste, wenige riefen ein Tool auf.
Welche MCP-Funktionen ein kleiner Server weglassen kann
Eine kurze Liste, denn zu wissen, was ein Aufbau überspringt, ist genauso nützlich wie zu wissen, was er enthält.
- Kein Tool auf Vorrat.
get_case_studiesfehlte in der ersten Version, und Agenten lasen die Fallstudien aus derllms.txtund den verlinkten Seiten, bis das Tool kam. Jedes Tool auf der Liste beantwortet heute eine Frage, die Agenten tatsächlich stellen. - Keine
resources- oderprompts-Fähigkeit. Der Server deklariert nurtools, weil das alles ist, was er implementiert. Eine Fähigkeit zu deklarieren, die man nicht bedient, ist schlimmer als sie wegzulassen: Ein Client, derresources/listaufruft, bekommt einen Fehler statt eines sauberen “nicht unterstützt”. - Kein SDK, wie oben behandelt.
- Keine Schreibvorgänge, wie oben behandelt.
Jede Auslassung ist eine Entscheidung, kein Versehen. Der Endpunkt tut genau das, was seine schreibgeschützten Tools erfordern, und nicht mehr, weshalb er klein genug ist, um in einer Sitzung durchdacht zu werden, und sicher genug, um ihn offen für das Internet zu lassen.
Was die MCP-Spezifikation von einem Streamable-HTTP-Endpunkt verlangt
Der Transport hinter POST /mcp/ ist Streamable HTTP. Die MCP-Transportspezifikation (Version 2025-06-18) definiert ihn als Nachfolger des HTTP+SSE-Transports aus Version 2024-11-05. Die meisten Regeln betreffen Streaming, und ein schreibgeschützter Server erfüllt sie, ohne irgendetwas zu streamen.
- Jede JSON-RPC-Nachricht des Clients ist ein neuer HTTP-POST an einen einzigen Endpunktpfad. Der Client muss einen
Accept-Header senden, der sowohlapplication/jsonals auchtext/event-streamnennt. - Ist der POST-Body eine Notification oder eine Response, muss der Server mit
202 Acceptedohne Body antworten. Unsere Function tut das fürnotifications/initialized. - Ist der Body ein Request, entscheidet der Server: Er öffnet einen SSE-Stream oder gibt ein einzelnes JSON-Objekt mit
Content-Type: application/jsonzurück. Der Client muss beides verarbeiten, also kann ein Server ohne Streaming-Bedarf immer mit einfachem JSON antworten. - Sessions sind optional. Ein Server darf in der Antwort auf
initializeeinenMcp-Session-Id-Header vergeben, muss es aber nicht. Ohne Session-ID gibt es keinen Session-Speicher zu betreiben, was zu einer Edge Function ohne Zustand zwischen Requests passt. - Nach der Initialisierung muss der Client bei jedem Request den Header
MCP-Protocol-Versionsenden. Fehlt er und hat der Server keinen anderen Weg, die Version zu erkennen, soll er2025-03-26annehmen. Auf einen ungültigen oder nicht unterstützten Wert muss er mit400 Bad Requestantworten.
Dieselbe Seite enthält eine Sicherheitswarnung zu diesem Transport. Server müssen den Origin-Header bei allen eingehenden Verbindungen validieren, lokal laufende Server sollen nur an 127.0.0.1 statt an alle Netzwerkschnittstellen binden, und Server sollen Verbindungen authentifizieren. Der Angriff, den die Spezifikation benennt, ist DNS-Rebinding: Eine entfernte Website nutzt den Browser des Besuchers, um einen MCP-Server auf dessen eigenem Rechner zu erreichen.
Wie die Aushandlung der MCP-Protokollversion funktioniert
Die Versionsaushandlung passiert in initialize, und die Lifecycle-Spezifikation hält sie kurz. Der Client muss eine Protokollversion senden, die er unterstützt, und das soll die neueste sein, die er unterstützt. Unterstützt der Server diese Version, muss er mit derselben antworten. Andernfalls muss er eine andere Version nennen, die er unterstützt, wiederum vorzugsweise seine neueste. Ein Client, der die Version aus der Antwort nicht unterstützt, soll die Verbindung trennen.
Protokollversionen sind Datumsstrings wie 2024-11-05, 2025-03-26 und 2025-06-18. Unsere Function verwendet 2025-06-18 als Standardwert. Für einen handgeschriebenen Server läuft die Regel auf eine Entscheidung hinaus: die Liste der Versionen, für die Sie antworten. Die Lifecycle-Seite zeigt die Form des Fehlers bei einer Unverträglichkeit: Code -32602, die Meldung “Unsupported protocol version” und ein data-Objekt mit den unterstützten und der angefragten Version.
Nach der Aushandlung müssen beide Seiten die ausgehandelte Version respektieren und nur ausgehandelte Fähigkeiten nutzen. Das ist der formale Grund, warum der oben beschriebene Server nur tools deklariert: Eine Fähigkeit in der Antwort auf initialize ist ein Versprechen, auf das sich der Client verlassen darf.
Wie man ein MCP-Tool als schreibgeschützt markiert
Das MCP-Schema gibt jedem Tool ein optionales annotations-Objekt mit vier Verhaltenshinweisen:
| Hinweis | Bedeutung | Standard |
|---|---|---|
readOnlyHint | das Tool verändert seine Umgebung nicht | false |
destructiveHint | das Tool kann destruktive Änderungen vornehmen; nur relevant, wenn readOnlyHint false ist | true |
idempotentHint | wiederholte Aufrufe mit denselben Argumenten haben keine zusätzliche Wirkung; nur relevant, wenn readOnlyHint false ist | false |
openWorldHint | das Tool kann mit einer offenen Welt externer Entitäten interagieren | true |
Die Standardwerte sind pessimistisch. Ein Tool ohne Annotationen gilt als eines, das schreiben, Daten zerstören und über die eigene Domäne hinausgreifen kann. Die Tools in diesem Build haben keine Annotationen, also würden die Hinweise allein sie genau so beschreiben. readOnlyHint: true in jeder Tool-Definition zu ergänzen ist eine Änderung von einer Zeile.
Das Schema hält außerdem fest, dass diese Eigenschaften Hinweise sind und keine verlässliche Beschreibung des Verhaltens, und die Tool-Spezifikation verlangt, dass Clients Annotationen als nicht vertrauenswürdig behandeln, sofern sie nicht von vertrauenswürdigen Servern stammen. Ein Hinweis ersetzt nie die Kontrolle. Die Schreibschutz-Eigenschaft dieses Endpunkts liegt im Code, in dem kein Tool einen Schreibpfad hat. Die Annotation beschreibt diese Eigenschaft nur einem Client, der ihr vertrauen will.
Die Tool-Spezifikation legt jedem Server drei Pflichten auf: alle Tool-Eingaben validieren, angemessene Zugriffskontrollen umsetzen und Tool-Aufrufe ratenbegrenzen. Unsere Function deckt die erste ab, indem sie fehlerhafte Argumente mit -32602 ablehnt. Die Spezifikation trennt außerdem zwei Arten von Fehlschlägen: Protokollfehler wie ein unbekanntes Tool gehen als JSON-RPC-Fehler zurück, während ein Tool, das gelaufen und gescheitert ist, ein normales Ergebnis mit isError: true liefert, damit das Modell den Fehlertext sieht und darauf reagieren kann.
Weitere Anleitungen zum Bau von MCP-Servern
Einen detaillierten Leitfaden zur Konfiguration, zum Testen von JSON-RPC-Schemata und zur Anbindung von Claude Desktop an unseren Live-Endpunkt finden Sie im WPPoland Live-MCP-Server Implementierungs-Leitfaden. Wenn Sie die ausführlichere Version dieser Entscheidungen wollen, deckt das Cluster rund um diesen Beitrag das angrenzende Terrain ab: einen MCP-Server für WooCommerce aufbauen für den zustandsbehafteten, shop-orientierten Fall, MCP vs REST: wann was gewinnt für die Frage, ob Sie MCP überhaupt brauchen, und MCP-Authentifizierungsmuster für den Moment, in dem Sie ein schreibfähiges Tool hinzufügen und Authentifizierung brauchen. Die kommerzielle Version dieser Arbeit finden Sie auf der Leistungsseite MCP-Server-Entwicklung.
Die Kurzfassung passt in einen Satz: Eine Marketing-Website kann ein MCP-Server sein, der Endpunkt ist eine Edge-Function, halten Sie jedes Tool schreibgeschützt und bewerben Sie die URL, die Ihr Host tatsächlich ausliefert.






