Einleitung: Was du am Ende dieser Anleitung hast

Stell dir vor, du öffnest einen Chat mit einem KI-Assistenten und schreibst: „Geh auf die Seite eines Wettbewerbers, sammle die Namen und Preise aller Produkte aus dem Katalog und fasse sie in einer Tabelle zusammen.“ Der Assistent antwortet nicht mit „ich habe keinen Internetzugang“, sondern lädt tatsächlich die Seite, zieht die Daten heraus und liefert dir ein fertiges Ergebnis. Genau das baust du, wenn du diese Anleitung bis zum Ende durchgehst. Das Bindeglied zwischen dem Sprachmodell und dem Web ist dein eigener MCP-Server, geschrieben in Python.

Eine wichtige Anmerkung: Wir behandeln hier nicht den fertigen Playwright MCP und andere Out-of-the-Box-Lösungen. Dafür gibt es im Blog separate Beiträge. Hier ist die Aufgabe eine andere: einen Server von Grund auf zu schreiben, damit du jede Zeile verstehst, eigene Tools hinzufügen, mobile Proxys einbinden und die Logik an konkrete Aufgaben anpassen kannst. Eine eigene Lösung ist immer flexibler als eine fremde.

Für wen diese Anleitung gedacht ist

  • Marketingspezialisten und Geschäftsinhaber, die schnell Preise, Bewertungen, Produktbeschreibungen und Wettbewerberinhalte sammeln müssen, ohne einen Scraper beim Entwickler in Auftrag zu geben.
  • Affiliate- und Traffic-Spezialisten, die Angebote, Landingpages und Creatives überwachen und die Routine an einen KI-Agenten delegieren wollen.
  • Entwickler, die vom MCP-Protokoll gehört haben, aber noch keinen eigenen Server gebaut haben und eine funktionierende Vorlage wollen.
  • Nutzer mobiler Proxys, denen wichtig ist, dass die Anfragen des Agenten über ihren Proxy laufen und nicht direkt über die heimische IP.

Was du vorher wissen solltest

Die Anleitung richtet sich an Einsteiger. Programmiererfahrung ist nicht zwingend nötig, aber ein Verständnis davon, was eine Kommandozeile ist und wie man eine Datei in einem Texteditor öffnet, ist hilfreich. Der gesamte Code kann komplett kopiert werden, und jeder Teil ist einfach erklärt. Wenn du bereits Python schreibst, gibt es gegen Ende des Artikels einen separaten Block mit fortgeschrittenen Möglichkeiten.

Wie viel Zeit du brauchst

Plane 2-3 Stunden für den ersten Durchlauf ein. Die Installation der Tools dauert etwa 30 Minuten, ein minimal funktionsfähiger MCP-Server steht nach einer Stunde, und die restliche Zeit geht für das Hinzufügen der Datenextraktions-Tools, das Einbinden des Proxys und das Testen drauf. Alles von Null auf einem anderen Computer zu wiederholen, schaffst du dann in 20-30 Minuten.

Vorbereitung: Tools, Zugänge und Systemanforderungen

Bevor du Code schreibst, stelle sicher, dass du alles Nötige hast. Dieser Abschnitt lässt sich in einer halben Stunde durchgehen und erspart dir die Hälfte der typischen Probleme in den nächsten Schritten.

Systemanforderungen

  • Ein Computer mit Windows 10/11, macOS 12 oder neuer oder Linux (Ubuntu 22.04 und neuer). Alles Beschriebene funktioniert auf jedem dieser Systeme, Unterschiede gibt es nur bei den Dateipfaden.
  • Mindestens 4 GB Arbeitsspeicher und 1 GB freier Speicherplatz auf der Festplatte.
  • Stabiler Internetzugang.

Was du installieren musst

  1. Python 3.11 oder neuer. Im Jahr 2026 sind die Versionen 3.12 und 3.13 aktuell. Lade das Installationsprogramm von der offiziellen Seite des Python-Projekts herunter. Setze unter Windows im ersten Fenster des Installers unbedingt das Häkchen bei Add python.exe to PATH, sonst findet das Terminal den Befehl python nicht. Unter macOS installierst du Python am besten über Homebrew mit dem Befehl brew install python. Unter Ubuntu führst du sudo apt install python3 python3-venv python3-pip aus.
  2. Einen Texteditor für Code. Wir empfehlen Visual Studio Code. Er ist kostenlos, hebt die Syntax hervor und zeigt Fehler an. Jeder andere Editor, sogar der Editor, funktioniert auch, aber mit VS Code ist es bequemer.
  3. Einen MCP-Client, also eine Anwendung mit einem KI-Agenten, mit dem du den Server verbindest. Die einfachste Variante für Einsteiger: Claude Desktop. Auch der Editor Cursor, VS Code mit der GitHub-Copilot-Erweiterung und einige andere Tools unterstützen MCP. Installiere mindestens eines davon, bevor du anfängst.
  4. Node.js 20 oder neuer. Das brauchst du nicht für den Server selbst, sondern für das Tool MCP Inspector, mit dem wir die Tools debuggen. Lade das Installationsprogramm der LTS-Version von der offiziellen Node.js-Seite herunter und installiere es mit den Standardeinstellungen.

Zugänge

Für den Abschnitt über Proxys brauchst du die Daten deines mobilen Proxys: Host, Port, Login und Passwort sowie den Link zum Wechseln der IP-Adresse, falls dein Tarif das unterstützt. All das findest du im Kundenbereich deines Anbieters. Wenn du noch keinen Proxy hast, kannst du die Anleitung auch ohne durchgehen: Der Server läuft dann direkt, und den Proxy fügst du später mit einer Zeile hinzu.

Backups

Wir werden die Konfigurationsdatei des MCP-Clients bearbeiten. Kopiere sie vorher an einen sicheren Ort, zum Beispiel auf den Desktop mit der Beschriftung „backup“. Wenn etwas schiefgeht, stellst du einfach die Kopie wieder her. Den Server-Code bewahre in einem separaten Ordner auf und speichere nach jedem Arbeitsschritt eine Kopie der Datei oder mache einen Commit in Git, wenn du damit umgehen kannst.

Tipp: Lege auf der Festplatte einen separaten Ordner mit kurzem Pfad ohne Leerzeichen und kyrillische Zeichen an, zum Beispiel C:/mcp-collector unter Windows oder ~/mcp-collector unter macOS und Linux. Leerzeichen und russische Buchstaben in Pfaden bringen den Start von Servern aus Konfigurationen heraus regelmäßig zum Scheitern, und du verbringst eine Stunde mit der Ursachensuche.

Grundbegriffe: Wie ein MCP-Server aufgebaut ist und warum der KI-Agent ihn braucht

Bevor wir die erste Codezeile schreiben, klären wir die Begriffe. Ohne dieses Wissen wirkt die Anleitung wie eine Sammlung magischer Zaubersprüche, mit ihm wird jede Aktion logisch.

Was ist MCP

MCP (Model Context Protocol) ist ein offenes Protokoll, das beschreibt, wie ein Sprachmodell mit externen Tools kommuniziert. Vor seiner Einführung hat sich jeder Dienst etwas Eigenes ausgedacht, um „der KI Hände zu geben“. MCP hat das standardisiert: Wenn du einen Server nach dem Protokoll geschrieben hast, versteht ihn jeder kompatible Client, sei es Claude Desktop, Cursor oder dein eigener Agent. Man kann MCP mit einem USB-Anschluss vergleichen: Egal, was du anschließt, einen USB-Stick oder eine Maus, der Anschluss ist derselbe.

Client und Server

In der MCP-Architektur gibt es zwei Beteiligte. Der Client ist die Anwendung mit KI, die Fragen stellt und Tools aufruft. Der MCP-Server ist das Programm, das diese Tools bereitstellt. In unserem Fall ist der Server die Fähigkeit, „ins Internet zu gehen und Daten zu holen“, und der Client ist dein KI-Assistent. Der Server läuft lokal auf deinem Computer, und der Client kommuniziert direkt mit ihm.

Tools, Ressourcen und Prompts

Ein MCP-Server kann dem Client drei Arten von Objekten bereitstellen:

  • Tools – Funktionen, die das Modell aufrufen kann: „Lade eine Seite herunter“, „Extrahiere alle Links“, „Wechsle die Proxy-IP“. Das ist die Grundlage unserer Anleitung.
  • Ressourcen – Daten, die der Server zum Lesen bereitstellt, zum Beispiel den Inhalt einer Konfigurationsdatei oder das Ergebnis der letzten Sammlung.
  • Prompts – vorbereitete Anfragevorlagen, die der Nutzer mit einem Befehl aufrufen kann.

Für die Datenerfassung reichen Tools. Ressourcen und Prompts behandeln wir im fortgeschrittenen Block.

Wie das Modell versteht, was es aufrufen soll

Hier gibt es einen wichtigen Punkt. Wenn der Client sich mit dem Server verbindet, fragt er die Liste der Tools mit ihren Namen, Beschreibungen und Parametern ab. Diese Beschreibungen landen im Kontext des Modells. Danach entscheidet das Modell selbst, welches Tool es mit welchen Argumenten aufruft, und stützt sich dabei genau auf den Text der Beschreibung. Deshalb sind die Funktionsbeschreibungen in unserem Code keine Formalität, sondern eine Anleitung für die KI. Je klarer du schreibst, was ein Tool tut und wann es einzusetzen ist, desto präziser arbeitet der Agent.

Transport: stdio und HTTP

Server und Client müssen irgendwie Nachrichten austauschen. Das Protokoll sieht dafür zwei Hauptwege vor. stdio – der Client startet dein Skript selbst als Unterprozess und kommuniziert über die Standardein- und -ausgabe. Das ist die einfachste Variante für die lokale Arbeit, und damit fangen wir an. Streamable HTTP – der Server läuft als Webdienst, mit dem sich der Client über eine Adresse verbindet. Diese Variante brauchst du, wenn der Server auf einer entfernten Maschine läuft oder sich mehrere Clients mit ihm verbinden. Sie kommt im fortgeschrittenen Block dran.

⚠️ Achtung: Beim stdio-Transport ist die gesamte Standardausgabe des Prozesses durch die Protokollnachrichten belegt. Wenn du im Code ein normales print zum Debuggen schreibst, erhält der Client Müll statt einer korrekten Antwort und bricht die Verbindung ab. Debug-Meldungen dürfen nur in den Fehlerstrom stderr geschrieben werden. Merke dir diese Regel, sie spart dir viel Zeit.

Warum Datenerfassung über MCP praktisch ist

Ein klassischer Scraper ist fest verdrahtet: Er kann bestimmte Felder von einer bestimmten Seite sammeln. Sobald sich das Layout ändert, bricht der Scraper. Die Kombination „KI-Agent plus MCP-Server“ funktioniert anders: Der Server bietet universelle Tools (herunterladen, Text extrahieren, Elemente per Selektor finden), und das Modell versteht selbst die Struktur der Seite und formuliert das Ergebnis. Du gewinnst Flexibilität, ohne den Code für jede neue Quelle neu zu schreiben.

Schritt 1: Projekt anlegen und Abhängigkeiten installieren

Ziel des Schritts: Eine isolierte Python-Umgebung vorbereiten und die Bibliotheken installieren, die für den MCP-Server nötig sind. Am Ende des Schritts hast du einen Projektordner mit einer funktionierenden virtuellen Umgebung.

Wozu eine virtuelle Umgebung

Eine virtuelle Umgebung ist eine separate Kopie von Python mit eigenen Bibliotheken im Projektordner. Sie ist nötig, damit unser Server nicht mit anderen Python-Programmen auf dem Computer in Konflikt gerät und der MCP-Client genau weiß, welchen Interpreter er starten soll. Ohne sie sind die Probleme „im Terminal funktioniert es, im Client nicht“ garantiert.

Schritt-für-Schritt-Anleitung

  1. Öffne das Terminal. Drücke unter Windows Win+R, gib powershell ein und drücke Enter. Öffne unter macOS die Terminal-App über Spotlight (Cmd+Leertaste, dann Terminal eingeben). Drücke unter Linux Ctrl+Alt+T.
  2. Erstelle den Projektordner und wechsle hinein. Führe unter Windows zwei Befehle aus: mkdir C:/mcp-collector, dann cd C:/mcp-collector. Unter macOS und Linux: mkdir ~/mcp-collector, dann cd ~/mcp-collector.
  3. Prüfe die Python-Version mit dem Befehl python --version (unter macOS und Linux eventuell python3 --version). Du solltest eine Zeile wie Python 3.12.x sehen. Wenn die Version unter 3.11 liegt oder der Befehl nicht gefunden wird, gehe zurück zum Vorbereitungsabschnitt und installiere Python neu.
  4. Erstelle die virtuelle Umgebung mit dem Befehl python -m venv .venv. Im Projektordner erscheint ein versteckter Ordner .venv. Das dauert 10-20 Sekunden.
  5. Aktiviere die Umgebung. Unter Windows in PowerShell: .venv/Scripts/Activate.ps1. Wenn PowerShell meldet, dass die Ausführung von Skripts verboten ist, führe den Befehl Set-ExecutionPolicy -Scope CurrentUser RemoteSigned aus, bestätige mit Y und wiederhole die Aktivierung. Unter macOS und Linux: source .venv/bin/activate. Nach der Aktivierung erscheint am Anfang der Terminalzeile die Markierung (.venv).
  6. Aktualisiere den Paketmanager: python -m pip install --upgrade pip.
  7. Installiere die Bibliotheken mit einem Befehl: pip install "mcp[cli]" httpx beautifulsoup4. Hier ist mcp das offizielle Python-SDK des Protokolls (2026 ist der Zweig 1.x aktuell), httpx eine moderne Bibliothek für HTTP-Anfragen mit Proxy-Unterstützung, beautifulsoup4 ein Werkzeug zum Parsen von HTML. Die Installation dauert 1-2 Minuten.
  8. Erstelle eine leere Datei server.py im Projektordner. In VS Code: Öffne den Ordner über File, Open Folder, klicke dann auf das Symbol für eine neue Datei in der linken Leiste und gib den Namen ein.

Was die Bibliotheken bedeuten

  • mcp übernimmt das gesamte Protokoll: die Registrierung der Tools, den Nachrichtenaustausch, die Beschreibung der Parameter. Das Modul FastMCP darin erlaubt es, ein Tool als gewöhnliche Funktion mit Dekorator zu deklarieren.
  • httpx lädt Seiten herunter. Im Gegensatz zum veralteten requests unterstützt es HTTP/2, Asynchronität und eine bequeme Proxy-Konfiguration.
  • beautifulsoup4 verwandelt HTML in einen Baum, in dem man Elemente leicht nach Tags und CSS-Selektoren sucht.

Tipp: Merke dir gleich den vollständigen Pfad zum Interpreter innerhalb der virtuellen Umgebung. Unter Windows ist das C:/mcp-collector/.venv/Scripts/python.exe, unter macOS und Linux /Users/name/mcp-collector/.venv/bin/python (oder /home/name/... unter Linux). Du brauchst ihn beim Verbinden mit dem Client. Den genauen Pfad findest du mit dem Befehl where python unter Windows oder which python unter macOS und Linux bei aktivierter Umgebung.

✅ Prüfung: Führe den Befehl pip list aus. In der Liste müssen die Pakete mcp, httpx und beautifulsoup4 vorhanden sein. Führe außerdem python -c "import mcp, httpx, bs4; print('ok')" aus – als Antwort muss das Wort ok ohne Fehler erscheinen.

Mögliche Probleme

  • Der Befehl python wird nicht gefunden. Installiere Python unter Windows neu mit dem Häkchen Add to PATH. Verwende unter macOS python3 statt python.
  • pip meldet Zugriffsrechte. Wahrscheinlich ist die Umgebung nicht aktiviert und du installierst Pakete in das System-Python. Prüfe die Markierung (.venv) am Anfang der Zeile.
  • Build-Fehler bei der Installation. Aktualisiere pip und versuche es erneut. Wenn das nicht hilft, prüfe, dass die Python-Version nicht unter 3.11 liegt.

Schritt 2: Einen minimalen MCP-Server mit dem ersten Tool schreiben

Ziel des Schritts: Einen funktionierenden MCP-Server mit einem Tool schreiben, das eine Seite unter einer Adresse herunterlädt und ihr HTML zurückgibt. Das ist das Fundament, auf dem wir die Funktionen aufbauen.

Der Server-Code

Öffne die Datei server.py und füge folgenden Code vollständig ein:

import sys
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP('web-collector')

HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36',
'Accept-Language': 'ru-RU,ru;q=0.9,en;q=0.8',
}

def log(message: str) -> None:
print(message, file=sys.stderr)

@mcp.tool()
def fetch_page(url: str, max_chars: int = 20000) -> str:
'''Скачивает страницу по указанному URL и возвращает её HTML-код.
Используй, когда нужно посмотреть исходную разметку страницы.
Параметр max_chars ограничивает длину ответа, чтобы не переполнять контекст.'''
log(f'fetch_page: {url}')
with httpx.Client(headers=HEADERS, timeout=20.0, follow_redirects=True) as client:
response = client.get(url)
response.raise_for_status()
return response.text[:max_chars]

if __name__ == '__main__':
mcp.run()

Den Code Zeile für Zeile erklärt

  1. FastMCP('web-collector') erzeugt ein Server-Objekt mit dem Namen web-collector. Diesen Namen zeigt der Client in der Liste der verbundenen Server an.
  2. HEADERS sind die Header, die wir an die Seiten senden. Viele Seiten liefern unvollständigen Inhalt oder einen Fehler, wenn die Anfrage ohne den gewohnten Browser-User-Agent kommt. Der Header Accept-Language signalisiert, dass wir die russischsprachige Version der Seite wollen.
  3. Die Funktion log schreibt Meldungen nach stderr. Genau so und nicht über ein normales print, weil stdout durch das Protokoll belegt ist. Diese Meldungen siehst du in den Logs des Clients und im MCP Inspector.
  4. @mcp.tool() ist der Dekorator, der eine gewöhnliche Funktion in ein MCP-Tool verwandelt. Das SDK liest automatisch den Funktionsnamen, die Parametertypen und den Docstring und erstellt daraus die Beschreibung für das Modell. Der Standardwert max_chars = 20000 bedeutet, dass der Parameter optional ist.
  5. Der Docstring in dreifachen Anführungszeichen ist das, was die KI liest. Hier erklären wir, was das Tool tut und wann es einzusetzen ist. Schreibe solche Beschreibungen ausführlich und in der Sprache, in der du mit dem Agenten kommunizierst.
  6. httpx.Client mit dem Parameter follow_redirects=True folgt automatisch Weiterleitungen, und timeout=20.0 verhindert, dass eine Anfrage endlos hängt.
  7. raise_for_status() wirft einen Fehler, wenn die Seite einen Code 4xx oder 5xx zurückgibt. Das SDK fängt ihn ab und schickt dem Client eine verständliche Fehlermeldung statt Stille.
  8. mcp.run() startet den Server standardmäßig mit dem stdio-Transport. Er wartet dann auf Befehle vom Client.

Erste Prüfung mit dem MCP Inspector

server.py direkt zu starten ist sinnlos: Er wartet auf Nachrichten vom Client und zeigt nichts an. Zum Prüfen verwenden wir den MCP Inspector – eine Weboberfläche, die den Client simuliert und es erlaubt, Tools manuell aufzurufen.

  1. Stelle sicher, dass die virtuelle Umgebung aktiviert ist und du dich im Projektordner befindest.
  2. Führe den Befehl mcp dev server.py aus. Dieser Befehl gehört zum installierten Paket mcp mit der Erweiterung cli. Beim ersten Start lädt er den Inspector über npx herunter, das dauert etwa eine Minute.
  3. Im Terminal erscheint eine Adresse wie http://localhost:6274 und, in neueren Versionen, ein Zugriffstoken. Öffne die Adresse im Browser (oft öffnet sie sich von selbst).
  4. Prüfe im linken Bereich des Inspectors, dass der Transport STDIO ausgewählt ist, der Befehl python und die Argumente server.py lauten. Klicke auf die Schaltfläche Connect.
  5. Die Statusanzeige wird grün mit der Beschriftung Connected. Wechsle oben im Menü auf den Tab Tools und klicke auf List Tools.
  6. In der Liste erscheint das Tool fetch_page mit der Beschreibung aus dem Docstring und zwei Parametern. Klicke darauf.
  7. Gib im Feld url https://example.com ein, lass das Feld max_chars leer oder gib 5000 ein. Klicke auf Run Tool.
  8. Rechts erscheint das Ergebnis: der HTML-Code der Seite, beginnend mit dem Tag doctype. Unten, im Tab mit den Server-Logs, siehst du die Zeile fetch_page: https://example.com.

✅ Prüfung: Der Inspector zeigt den Status Connected, in der Liste Tools steht fetch_page, und der Aufruf mit der Adresse example.com liefert HTML ohne Fehler zurück. Wenn das so ist, funktioniert dein erster MCP-Server.

Mögliche Probleme

  • mcp dev meldet, dass npx nicht gefunden wird. Node.js ist nicht installiert. Installiere es und starte das Terminal neu.
  • Der Inspector hat sich geöffnet, aber Connect gibt einen Fehler. Prüfe, dass im Feld für den Befehl python aus der aktivierten Umgebung steht. Du kannst auch den vollständigen Pfad zu python.exe innerhalb von .venv eintragen.
  • SyntaxError beim Verbinden. Der Code wurde mit verlorenen Einrückungen kopiert. In Python sind Einrückungen Pflicht: Der Funktionskörper wird um vier Leerzeichen verschoben. Prüfe die Datei im Editor.
  • Das Tool gibt den Fehler 403 zurück. Die Seite hat die Anfrage nicht akzeptiert. Bei example.com passiert das nicht, bei echten Seiten kommen wir im Proxy-Schritt darauf zurück.

Schritt 3: Den MCP-Server mit dem KI-Client verbinden

Ziel des Schritts: Den Server in den Einstellungen des KI-Clients registrieren, damit der Agent dein Tool sieht und es aus einem normalen Chat heraus aufrufen kann. Wir behandeln die Verbindung mit Claude Desktop als häufigste Variante und zeigen kurz die Alternativen.

Verbindung mit Claude Desktop

  1. Öffne Claude Desktop. Gehe in die Einstellungen: unter Windows über das Menü in der linken oberen Ecke, Punkt Settings; unter macOS über das Menü Claude, Punkt Settings.
  2. Wechsle auf den Tab Developer und klicke auf die Schaltfläche Edit Config. Es öffnet sich der Ordner mit der Datei claude_desktop_config.json. Wenn die Datei nicht existiert, erstellt der Client sie.
  3. Erstelle eine Sicherungskopie dieser Datei, indem du sie auf den Desktop kopierst.
  4. Öffne die Datei in VS Code oder einem anderen Editor. Wenn die Datei leer ist, füge den Inhalt vollständig ein. Wenn bereits andere Server darin stehen, füge deinen Block innerhalb des Objekts mcpServers mit einem Komma getrennt hinzu.
{
"mcpServers": {
"web-collector": {
"command": "C:/mcp-collector/.venv/Scripts/python.exe",
"args": ["C:/mcp-collector/server.py"]
}
}
}

Ersetze unter macOS und Linux die Pfade durch deine eigenen, zum Beispiel /Users/ivan/mcp-collector/.venv/bin/python und /Users/ivan/mcp-collector/server.py. Beachte: Selbst unter Windows sind die Pfade mit Schrägstrichen geschrieben. Das ist einfacher, weil Backslashes in JSON verdoppelt werden müssten, während Windows Schrägstriche problemlos versteht.

  1. Speichere die Datei. Stelle sicher, dass keine überflüssigen Kommas nach dem letzten Element stehen und alle Klammern geschlossen sind. Ein einziges überflüssiges Komma macht das JSON ungültig, und der Client ignoriert die Konfiguration stillschweigend.
  2. Schließe Claude Desktop vollständig und starte es neu. Unter Windows reicht es nicht, das Fenster zu schließen: Klicke mit der rechten Maustaste auf das Symbol im Infobereich und wähle Quit. Der Client liest die Konfiguration nur beim Start.
  3. Öffne nach dem Start einen neuen Chat. Suche unter dem Eingabefeld das Tool-Symbol (das Symbol mit den Schiebereglern oder dem Stecker). Klicke darauf: In der Liste muss der Server web-collector mit einem Tool fetch_page stehen.
  4. Schreibe in den Chat: „Lade die Seite https://example.com mit fetch_page herunter und sag mir, welchen Titel diese Seite hat.“ Der Client fragt um Erlaubnis für den Tool-Aufruf. Klicke auf Allow oder Allow for this chat.
  5. Nach ein paar Sekunden antwortet der Agent, dass der Titel der Seite Example Domain lautet. Er hat eine echte Anfrage über deinen Server gemacht.

Verbindung mit Cursor und VS Code

Öffne in Cursor Settings, den Bereich MCP, und klicke auf Add new global MCP server. Es öffnet sich die Datei mcp.json mit genau derselben Struktur wie bei Claude Desktop. Füge denselben Block ein und speichere. Erstelle in VS Code mit Copilot im Wurzelverzeichnis des Arbeitsordners die Datei .vscode/mcp.json, wobei statt des Schlüssels mcpServers der Schlüssel servers verwendet wird und darin dieselben command und args stehen. Nach dem Speichern erscheint über dem Serverblock eine Schaltfläche Start. In allen Clients ist das Prinzip gleich: den Befehl zum Starten des Interpreters und den Pfad zum Skript angeben.

Tipp: Gib im command genau den python aus der virtuellen Umgebung an und nicht einfach das Wort python. Der Client startet den Prozess mit seinem eigenen Satz von Umgebungsvariablen, und der Systembefehl python kann eine andere Version ohne die installierten Bibliotheken sein. Der vollständige Pfad schließt dieses Problem ein für alle Mal aus.

✅ Prüfung: In der Oberfläche des Clients ist der Server web-collector sichtbar, der Agent ruft auf Anfrage fetch_page auf und gibt den Inhalt der Seite example.com korrekt wieder. In den Logs des Clients (bei Claude Desktop ist das der Ordner logs neben der Konfiguration, Datei mcp-server-web-collector.log) ist die Zeile fetch_page: https://example.com zu sehen.

Mögliche Probleme

  • Der Server erscheint nicht in der Liste. Prüfe das JSON auf Gültigkeit: Füge den Inhalt in einen beliebigen Online-JSON-Validator ein oder öffne ihn in VS Code, der Fehler unterstreicht. Stelle sicher, dass der Client vollständig neu gestartet wurde.
  • Neben dem Server steht ein roter Fehlerindikator. Öffne die Logdatei. Meist steht dort ModuleNotFoundError: Es wurde der falsche python angegeben. Prüfe den Pfad im command.
  • Der Agent sagt, dass er keinen Internetzugang bekommt. Er hat das Tool nicht gesehen. Stelle sicher, dass die Tools im Schieberegler-Panel aktiviert sind, und bitte explizit: „verwende das Tool fetch_page“.
  • Fehler spawn ENOENT. Der Pfad zu python oder zu server.py ist falsch angegeben. Kopiere den Pfad aus dem Datei-Explorer und ersetze Backslashes durch Schrägstriche.

Schritt 4: Tools zur Datenextraktion hinzufügen

Ziel des Schritts: Den Server so erweitern, dass er nicht rohes HTML, sondern nützliche Daten liefert: reinen Text, eine Liste von Links und Elemente nach CSS-Selektor. Danach kann der Agent strukturierte Informationen sammeln, ohne den Kontext mit Markup zu verbrauchen.

Warum fetch_page allein zu wenig ist

Das HTML einer echten Seite wiegt Hunderte Kilobyte, und der größte Teil sind Skripte, Styles und dienstliches Markup. Wenn wir dem Modell jedes Mal alles geben, stößt es schnell an die Kontextgrenze, und du zahlst für überflüssige Tokens. Die richtige Strategie: Der Server macht eine grobe Bereinigung und Strukturierung, und das Modell arbeitet dann mit kompakten Daten. Deshalb fügen wir drei spezialisierte Tools hinzu.

Aktualisierter Code

Ersetze den Inhalt von server.py durch die erweiterte Version. Die Funktion fetch_page bleibt, aber die allgemeine Download-Logik wurde in eine separate Funktion _get_html ausgelagert, die alle Tools nutzen.

import sys
from urllib.parse import urljoin
import httpx
from bs4 import BeautifulSoup
from mcp.server.fastmcp import FastMCP

mcp = FastMCP('web-collector')

HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36',
'Accept-Language': 'ru-RU,ru;q=0.9,en;q=0.8',
}

def log(message: str) -> None:
print(message, file=sys.stderr)

def _get_html(url: str) -> str:
log(f'GET {url}')
with httpx.Client(headers=HEADERS, timeout=20.0, follow_redirects=True) as client:
response = client.get(url)
response.raise_for_status()
return response.text

def _clean(text: str) -> str:
return ' '.join(text.split())

@mcp.tool()
def fetch_page(url: str, max_chars: int = 20000) -> str:
'''Возвращает сырой HTML страницы. Используй только когда нужна именно разметка,
например чтобы подобрать CSS-селектор. Для чтения содержимого используй extract_text.'''
return _get_html(url)[:max_chars]

@mcp.tool()
def extract_text(url: str, max_chars: int = 15000) -> str:
'''Возвращает чистый текст страницы без скриптов, стилей и разметки.
Лучший выбор, когда нужно прочитать статью, описание товара или отзывы.'''
soup = BeautifulSoup(_get_html(url), 'html.parser')
for tag in soup(['script', 'style', 'noscript', 'svg', 'header', 'footer', 'nav']):
tag.decompose()
title = _clean(soup.title.get_text()) if soup.title else ''
body = _clean(soup.get_text(' '))
return f'Заголовок: {title}. Текст: {body}'[:max_chars]

@mcp.tool()
def extract_links(url: str, limit: int = 100, contains: str = '') -> list[dict]:
'''Возвращает список ссылок со страницы: текст ссылки и полный адр��с.
Параметр contains фильтрует ссылки, в адресе которых есть указанная подстрока,
например /product/ или /catalog/.'''
soup = BeautifulSoup(_get_html(url), 'html.parser')
result = []
seen = set()
for a in soup.find_all('a', href=True):
full = urljoin(url, a['href'])
if full in seen or (contains and contains not in full):
continue
seen.add(full)
result.append({'text': _clean(a.get_text())[:120], 'url': full})
if len(result) >= limit:
break
return result

@mcp.tool()
def select_elements(url: str, css_selector: str, limit: int = 50) -> list[str]:
'''Находит на странице элементы по CSS-селектору и возвращает их текст.
Примеры селекторов: h2, .price, div.product-card, table tr.
Используй, когда нужны конкретные повторяющиеся блоки: цены, названия, строки таблицы.'''
soup = BeautifulSoup(_get_html(url), 'html.parser')
elements = soup.select(css_selector)[:limit]
return [_clean(el.get_text(' ')) for el in elements]

if __name__ == '__main__':
mcp.run()

Was jedes Tool tut

  1. extract_text entfernt aus dem Dokument Skripte, Styles, Kopf, Fuß und Menü und fügt den übrigen Text zu einer Zeile mit einzelnen Leerzeichen zusammen. Die Funktion _clean entfernt über split und join überflüssige Zeilenumbrüche und Tabulatoren. Am Anfang der Antwort wird der Seitentitel eingefügt, damit der Agent sofort weiß, was er vor sich hat.
  2. extract_links sammelt alle a-Tags, wandelt relative Adressen mit urljoin in absolute um, entfernt Duplikate über die Menge seen und erlaubt es, Links nach einer Teilzeichenkette zu filtern. So bekommt der Agent in einem Aufruf zum Beispiel alle Produktkarten aus einem Katalog.
  3. select_elements ist das mächtigste Tool. Es nimmt einen CSS-Selektor und gibt den Text der gefundenen Elemente zurück. Der Agent kann zuerst über fetch_page ein Stück HTML ansehen, verstehen, dass die Preise in der Klasse price liegen, und dann select_elements mit dem Selektor .price aufrufen.

Achte auf die Docstrings: Wir geben dem Modell explizit Hinweise, welches Tool es in welcher Situation wählen soll. Das verbessert die Qualität der Agentenarbeit merklich.

Wie man prüft

  1. Starte mcp dev server.py und verbinde dich im Inspector. In der Liste Tools stehen jetzt vier Tools.
  2. Rufe extract_links mit der url einer beliebigen Nachrichtenseite oder eines Katalogs und dem Parameter contains mit einem Teil der Adresse des Bereichs auf. Das Ergebnis ist eine Liste von Objekten mit den Feldern text und url.
  3. Rufe select_elements mit derselben Adresse und dem Selektor h2 auf. Du bekommst eine Liste von Überschriften.
  4. Starte Claude Desktop neu (die Konfiguration muss nicht geändert werden, nur der Code hat sich geändert) und bitte: „Sammle von der Startseite dieser Seite alle h2-Überschriften und Links, die in den Nachrichtenbereich führen, und stelle sie als Tabelle dar“.

Tipp: Wenn du nicht weißt, welcher Selektor nötig ist, öffne die Seite im Browser, drücke F12, wähle das Werkzeug zum Auswählen von Elementen (das Symbol mit dem Pfeil in der linken oberen Ecke des Panels) und klicke auf den gewünschten Block. Im Code siehst du seine Klasse. Ein Selektor mit Punkt und Klassennamen, zum Beispiel .product-title, funktioniert meistens. Mehr noch: Du kannst den Agenten einfach bitten: „Lade das HTML und wähle selbst einen Selektor für die Preise“.

✅ Prüfung: Alle vier Tools sind im Inspector und im Client sichtbar, extract_text liefert lesbaren Text ohne Tags, extract_links gibt eine Liste mit absoluten Adressen zurück, select_elements mit dem Selektor h2 liefert Überschriften.

Mögliche Probleme

  • select_elements gibt eine leere Liste zurück. Entweder ist der Selektor falsch, oder der Inhalt wird per JavaScript erst nach dem Laden der Seite nachgeladen. Prüfe über fetch_page: Wenn im HTML die benötigten Daten fehlen, rendert die Seite sie auf dem Client. Für solche Seiten braucht man eine Browser-Engine, das ist ein Thema für einen eigenen Artikel.
  • extract_text liefert Zeichensalat. Die Seite liefert eine ungewöhnliche Kodierung. Füge nach response.raise_for_status() die Zeile response.encoding = response.charset_encoding or 'utf-8' hinzu.
  • Die Antwort wird abgeschnitten. Erhöhe max_chars im Aufruf oder bitte den Agenten, die Seite in Teilen über mehrere Selektoren abzufragen.

Schritt 5: Mobile Proxys und IP-Rotation einbinden

Ziel des Schritts: Alle Anfragen des MCP-Servers über einen mobilen Proxy leiten, ein Tool zum IP-Wechsel und zur Prüfung der aktuellen Adresse hinzufügen. Danach arbeitet der Agent im Namen eines Mobilfunkanbieters und nicht mit deiner privaten oder Büro-IP.

Warum ein Datensammler einen mobilen Proxy braucht

Wenn du Daten von einer einzigen IP-Adresse sammelst, sehen die Seiten Dutzende gleicher Anfragen in Folge und beginnen, Captchas, gekürzten Inhalt oder den Fehler 429 „zu viele Anfragen“ auszugeben. Ein mobiler Proxy löst mehrere Probleme gleichzeitig. Erstens gehört die Adresse einem echten Mobilfunkanbieter, und solche Adressen teilen sich Tausende Teilnehmer, deshalb sind die Seiten ihnen gegenüber nachsichtiger. Zweitens kannst du die IP über einen Link oder per Timer wechseln und so die Last verteilen. Drittens trennst du die Arbeitsaktivität des Agenten von deinen persönlichen Sitzungen. Für einen Marketingspezialisten ist es außerdem eine Möglichkeit, die Seite so zu sehen, wie sie ein mobiler Nutzer einer bestimmten Region sieht.

⚠️ Achtung: Ein Proxy ist ein Werkzeug für die stabile und korrekte Arbeit des Sammlers, nicht für die Verletzung von Regeln. Sammle nur öffentlich zugängliche Daten, beachte die Nutzungsbedingungen der Seiten und die robots.txt, erzeuge keine übermäßige Last und sammle keine personenbezogenen Daten ohne rechtliche Grundlage. Die Verantwortung für die Nutzung des Werkzeugs liegt bei dir.

Schritt-für-Schritt-Anleitung

  1. Öffne den Kundenbereich deines Anbieters mobiler Proxys und finde die Verbindungsdaten: Host, Port, Login, Passwort. Normalerweise sind sie in einer Zeile wie login:password@host:port zusammengefasst. Kopiere dort auch den Link zum IP-Wechsel, falls vorhanden.
  2. Füge in der Datei server.py oben nach den übrigen Imports die Zeile import os hinzu. Füge dann unterhalb des HEADERS-Blocks die Einstellungen hinzu:
PROXY_URL = os.environ.get('MOBILE_PROXY_URL', '')
ROTATE_URL = os.environ.get('PROXY_ROTATE_URL', '')

def _client() -> httpx.Client:
kwargs = {'headers': HEADERS, 'timeout': 30.0, 'follow_redirects': True}
if PROXY_URL:
kwargs['proxy'] = PROXY_URL
return httpx.Client(**kwargs)
  1. Ersetze in der Funktion _get_html die Zeile mit httpx.Client durch den Aufruf _client(). Jetzt sieht sie so aus: with _client() as client. Alle Tools laufen automatisch über den Proxy.
  2. Füge zwei neue Tools vor der Zeile if __name__ hinzu:
@mcp.tool()
def current_ip() -> str:
'''Показывает IP-адрес, с которого сервер сейчас выходит в интернет.
Используй, чтобы убедиться, что прокси подключён, или после смены IP.'''
with _client() as client:
return client.get('https://api.ipify.org').text.strip()

@mcp.tool()
def rotate_ip() -> str:
'''Запрашивает смену IP-адреса мобильного прокси через ссылку из личного кабинета.
Вызывай, если сайт начал отдавать ошибки 429 или капчу. После вызова подожди 5-10 секунд.'''
if not ROTATE_URL:
return 'Ссылка смены IP не настроена в переменной PROXY_ROTATE_URL'
response = httpx.get(ROTATE_URL, timeout=15.0)
log(f'rotate_ip: status {response.status_code}')
return f'Запрос смены IP отправлен, ответ прокси-сервиса: {response.status_code}'
  1. Übergib die Proxy-Daten über Umgebungsvariablen in der Konfiguration des Clients. Wir schreiben Login und Passwort bewusst nicht in den Code, damit sie nicht versehentlich zusammen mit der Datei irgendwohin gelangen. Öffne claude_desktop_config.json und ergänze den Serverblock um den Abschnitt env:
{
"mcpServers": {
"web-collector": {
"command": "C:/mcp-collector/.venv/Scripts/python.exe",
"args": ["C:/mcp-collector/server.py"],
"env": {
"MOBILE_PROXY_URL": "http://login:password@proxy-host:port",
"PROXY_ROTATE_URL": "https://ссылка-смены-ip-из-кабинета"
}
}
}
}
  1. Setze die echten Werte anstelle von login, password, proxy-host und port ein. Wenn der Anbieter den Proxy über das SOCKS5-Protokoll bereitstellt, ersetze http:// durch socks5:// und installiere ein zusätzliches Paket mit dem Befehl pip install httpx[socks].
  2. Speichere die Konfiguration und starte den Client vollständig neu.
  3. Bitte den Agenten: „Rufe current_ip auf und sag mir, welche Adresse wir haben. Rufe dann rotate_ip auf, warte zehn Sekunden und prüfe die IP erneut.“ Die Adressen müssen sich unterscheiden.

Prüfung über den Inspector mit Proxy

Der Inspector kann ebenfalls Umgebungsvariablen übergeben. Klappe im linken Bereich den Abschnitt Environment Variables auf, füge MOBILE_PROXY_URL und PROXY_ROTATE_URL mit deinen Werten hinzu, verbinde dich und rufe current_ip auf. Die Antwort muss mit der IP übereinstimmen, die der Kundenbereich des Anbieters anzeigt.

Tipp: Rufe rotate_ip nicht vor jeder Anfrage auf. Bei den meisten Anbietern dauert der IP-Wechsel einige Sekunden, und zu häufige Anfragen können an ein Limit für den Wechsel stoßen. Eine vernünftige Strategie: die Adresse alle 30-100 Anfragen wechseln oder nur bei Fehlern 429 und 403. Man kann diese Logik direkt in _get_html einbauen, was wir im nächsten Schritt tun.

✅ Prüfung: Das Tool current_ip gibt die Proxy-Adresse zurück und nicht deine heimische. Nach rotate_ip und einer Pause ändert sich die Adresse. Die Tools extract_text und extract_links funktionieren weiter, und in den Logs sind GET-Zeilen mit den Seitenadressen zu sehen.

Mögliche Probleme

  • Fehler 407 Proxy Authentication Required. Falscher Login oder falsches Passwort, oder sie enthalten Sonderzeichen. Zeichen wie @ oder : im Passwort müssen kodiert werden: @ durch %40, : durch %3A ersetzen.
  • Fehler ConnectTimeout. Falscher Host oder Port, oder deine IP ist nicht in der Liste der erlaubten Adressen im Kundenbereich des Anbieters, falls der Tarif eine solche Bindung hat.
  • current_ip zeigt deine eigene Adresse. Die Umgebungsvariable ist nicht bis zum Server durchgekommen. Prüfe die Schreibweise von MOBILE_PROXY_URL in der Konfiguration und stelle sicher, dass der Client neu gestartet wurde.
  • rotate_ip gibt den Status 429 oder eine Meldung über ein Limit zurück. Du wechselst die IP häufiger, als der Tarif erlaubt. Erhöhe das Intervall.

Schritt 6: Den Server robust machen: Wiederholungen, Verzögerungen, Cache und Limits

Ziel des Schritts: Das Lehrbeispiel in ein Werkzeug verwandeln, das nicht beim ersten Netzwerkfehler abstürzt, die Seiten nicht mit Anfragen bombardiert und den Kontext des Modells nicht überläuft. Das ist der letzte Pflichtschritt vor dem vollwertigen Einsatz.

Was wir hinzufügen und warum

  • Automatische Wiederholungen. Netzwerkfehler passieren. Statt dem Agenten sofort einen Fehler zurückzugeben, versuchen wir die Anfrage noch zweimal mit einer Pause.
  • Automatischer IP-Wechsel bei Blockierung. Wenn die Seite mit 429 oder 403 antwortet und der Rotationslink eingerichtet ist, wechselt der Server selbst die Adresse und wiederholt die Anfrage.
  • Verzögerung zwischen Anfragen. Ein höflicher Sammler sendet nicht Dutzende Anfragen pro Sekunde. Eine Pause von ein bis zwei Sekunden verringert die Last auf die Seite und das Blockierungsrisiko.
  • Cache. Der Agent fragt dieselbe Seite oft mehrmals mit verschiedenen Tools ab. Ein Cache im Speicher für einige Minuten erspart wiederholte Ladevorgänge.
  • Größenlimit. Wir laden keine Seiten herunter, die schwerer als einige Megabyte sind.

Code

Füge oben in der Datei import time hinzu und ersetze die Funktion _get_html durch diese:

CACHE: dict[str, tuple[float, str]] = {}
CACHE_TTL = 300
REQUEST_DELAY = 1.5
MAX_BYTES = 3_000_000
_last_request = 0.0

def _get_html(url: str) -> str:
global _last_request
now = time.time()
cached = CACHE.get(url)
if cached and now - cached[0] < CACHE_TTL:
log(f'cache hit: {url}')
return cached[1]
last_error = None
for attempt in range(3):
wait = REQUEST_DELAY - (time.time() - _last_request)
if wait > 0:
time.sleep(wait)
try:
with _client() as client:
_last_request = time.time()
response = client.get(url)
if response.status_code in (403, 429) and ROTATE_URL:
log(f'status {response.status_code}, rotating ip')
httpx.get(ROTATE_URL, timeout=15.0)
time.sleep(8)
continue
response.raise_for_status()
if len(response.content) > MAX_BYTES:
raise ValueError(f'Страница слишком большая: {len(response.content)} байт')
html = response.text
CACHE[url] = (time.time(), html)
return html
except httpx.HTTPError as error:
last_error = error
log(f'attempt {attempt + 1} failed: {error}')
time.sleep(2 * (attempt + 1))
raise RuntimeError(f'Не удалось загрузить {url} после 3 попыток: {last_error}')

Wie das funktioniert

  1. Das Wörterbuch CACHE speichert für jede Adresse die Ladezeit und das HTML. Wenn die Seite vor weniger als fünf Minuten angefragt wurde, geben wir die gespeicherte Kopie zurück, ohne eine Anfrage zu machen.
  2. Vor jeder Anfrage berechnen wir, wie viel Zeit seit der vorherigen vergangen ist, und schlafen bei Bedarf bis zu REQUEST_DELAY Sekunden.
  3. Eine Schleife aus drei Versuchen. Bei einer Antwort 403 oder 429 mit eingerichteter Rotation wechselt der Server die IP, wartet acht Sekunden und versucht es erneut. Bei Netzwerkfehlern wartet er zwei, vier, sechs Sekunden zwischen den Versuchen.
  4. Wenn die Seite größer als drei Megabyte ist, betrachten wir das als Fehler: Solche Dokumente passen ohnehin nicht in den Kontext.
  5. Nach drei Fehlversuchen wird ein verständlicher Fehler mit Adresse und Ursache geworfen. Der Agent erhält ihn als Text und kann dich informieren oder einen anderen Weg versuchen.

Außerdem empfehlen wir, ein Tool zum Leeren des Caches hinzuzufügen, damit der Agent eine Seite zwangsweise neu laden kann:

@mcp.tool()
def clear_cache() -> str:
'''Очищает кэш загруженных страниц. Вызывай, если нужно получить свежую версию страницы.'''
count = len(CACHE)
CACHE.clear()
return f'Кэш очищен, удалено записей: {count}'

Tipp: Die Werte REQUEST_DELAY und CACHE_TTL sollten in Umgebungsvariablen ausgelagert werden, analog zum Proxy, damit man sie ohne Codeänderung anpassen kann. Für die Preisüberwachung passen eine Verzögerung von zwei bis drei Sekunden und ein Cache von einer Minute, für das Sammeln von Artikeln eine Verzögerung von einer Sekunde und ein Cache von einer Stunde.

✅ Prüfung: Rufe extract_text für eine Seite zweimal hintereinander auf. Beim zweiten Mal erscheint in den Logs die Zeile cache hit, und die Antwort kommt sofort. Gib eine nicht existierende Domain an – nach einigen Sekunden erhält der Agent die Meldung „Не удалось загрузить... после 3 попыток“ und hängt nicht.

Mögliche Probleme

  • NameError: ROTATE_URL ist nicht definiert. Die Funktion _get_html ist oberhalb des Blocks mit den Proxy-Einstellungen deklariert. Verschiebe die Einstellungen PROXY_URL und ROTATE_URL weiter nach oben in der Datei.
  • Der Agent beschwert sich über langsame Arbeit. Das ist normal: Verzögerungen und IP-Rotation brauchen Zeit. Wenn du es eilig hast, verringere REQUEST_DELAY auf 0.5, aber denke an das Blockierungsrisiko.
  • Der Speicher wächst. Der Cache speichert alle Seiten der Sitzung. Für lange Sitzungen füge bei jedem Aufruf eine Bereinigung von Einträgen älter als TTL hinzu oder begrenze die Größe des Wörterbuchs.

Ergebnisprüfung: Checkliste für einen fertigen MCP-Server

Gehe die Checkliste durch und hake jeden Punkt ab. Wenn alle erfüllt sind, ist dein MCP-Server für die Datenerfassung bereit für den echten Einsatz.

Was funktionieren muss

  • Der Befehl mcp dev server.py startet ohne Fehler, der Inspector verbindet sich und zeigt den Status Connected.
  • In der Liste der Tools stehen fetch_page, extract_text, extract_links, select_elements, current_ip, rotate_ip und clear_cache.
  • Der Server web-collector erscheint ohne Fehlerindikator im Tool-Panel des KI-Clients.
  • Der Agent wählt auf eine freie Anfrage hin selbst das passende Tool und ruft es auf.
  • current_ip zeigt die Adresse des mobilen Proxys, und nach rotate_ip ändert sich die Adresse.
  • Eine wiederholte Anfrage derselben Seite wird aus dem Cache bedient.
  • Eine fehlerhafte Adresse führt zu einer verständlichen Fehlermeldung und nicht zum Hängen.

Komplexer Test

  1. Wähle eine öffentliche Seite mit einem Katalog oder einem Artikel-Feed, deren Daten verwendet werden dürfen.
  2. Bitte den Agenten: „Öffne die Startseite der Seite, finde die Links zum Katalogbereich, gehe in die ersten fünf Karten, sammle Name und Preis und stelle sie als Tabelle mit den Spalten Name, Preis, Link dar“.
  3. Beobachte die Aufrufkette: Der Agent sollte extract_links mit Filter aufrufen, dann mehrfach select_elements oder extract_text, und am Ende eine Tabelle erstellen.
  4. Prüfe einige Zeilen manuell, indem du die Karten im Browser öffnest. Die Daten müssen übereinstimmen.

Erfolgskriterien

Das Sammeln von fünf Karten dauert mit den Verzögerungen nicht länger als 30-40 Sekunden. In den Logs des Clients gibt es keine Fehler auf Traceback-Ebene. Der Agent fragt nicht nach, welches Tool er verwenden soll, sondern handelt selbst. Wenn das so ist, herzlichen Glückwunsch: Du hast deinen eigenen MCP-Server gebaut und einen KI-Agenten mit dem Web verbunden.

Typische Fehler beim Erstellen eines MCP-Servers und ihre Lösungen

Hier sind die Probleme gesammelt, mit denen fast jeder beim ersten Durchlauf zu kämpfen hat. Format: Problem, Ursache, Lösung.

1. Der Server verbindet sich im Inspector, funktioniert aber nicht im Client

Ursache: In der Konfiguration des Clients ist ein System-python ohne installierte Bibliotheken angegeben oder ein falscher Pfad zur Datei. Lösung: Gib den vollständigen Pfad zu python innerhalb von .venv und den vollständigen Pfad zu server.py an, verwende Schrägstriche und starte den Client vollständig neu.

2. Der Client bricht die Verbindung direkt nach dem Start ab

Ursache: Im Code ist ein normales print ohne file=sys.stderr geblieben, und der dienstliche stdout-Strom ist verschmutzt. Lösung: Ersetze alle print durch die Funktion log. Prüfe außerdem, dass die Bibliotheken nicht nach stdout schreiben: Manche Fortschrittsbalken tun das standardmäßig.

3. Der Agent ruft keine Tools auf und antwortet aus seinem Wissen

Ursache: Die Tool-Beschreibungen sind zu kurz oder zu vage, und das Modell versteht nicht, wann sie einzusetzen sind. Lösung: Erweitere die Docstrings, füge Formulierungen wie „verwende, wenn...“ und Beispiele hinzu. Nenne in den ersten Anfragen das Tool explizit.

4. Fehler 403 beim Laden echter Seiten

Ursache: Die Seite akzeptiert keine Anfragen ohne Browser-Header oder von einer verdächtigen IP. Lösung: Prüfe, dass HEADERS übergeben werden, aktualisiere den User-Agent auf eine aktuelle Browserversion, binde einen mobilen Proxy ein und stelle sicher, dass die Rotation funktioniert.

5. Leeres Ergebnis bei select_elements trotz korrektem Selektor

Ursache: Die Daten werden per JavaScript nach dem Laden der Seite nachgeladen und sind im ursprünglichen HTML nicht vorhanden. Lösung: Prüfe über fetch_page. Wenn die Daten fehlen, versuche, die interne API der Seite im Network-Tab des Browsers zu finden: Oft kommen die Karten als JSON über eine separate Adresse, die man direkt mit extract_text abfragen kann.

6. Fehler 407 oder ConnectTimeout bei der Arbeit über den Proxy

Ursache: Falsche Zugangsdaten, nicht kodierte Sonderzeichen im Passwort oder falscher Port. Lösung: Kopiere die Verbindungszeile erneut aus dem Kundenbereich, kodiere die Sonderzeichen, prüfe das Protokoll http oder socks5.

7. Die JSON-Konfiguration wird nicht angewendet

Ursache: Überflüssiges Komma, fehlendes Anführungszeichen oder Backslashes in den Pfaden. Lösung: Prüfe die Datei in einem Validator, ersetze Backslashes durch Schrägstriche, stelle sicher, dass nach dem letzten Element kein Komma steht.

8. Der Server läuft, aber die Daten kommen in falscher Kodierung

Ursache: Die Seite gibt die Kodierung nicht in den Headern an. Lösung: Setze response.encoding explizit oder verwende das Attribut response.content mit manueller Dekodierung über decode('utf-8', errors='ignore').

Zusätzliche Möglichkeiten: Block für Fortgeschrittene

Der Basisserver ist fertig. Wenn du sicher Python schreibst und mehr willst, hier sind Entwicklungsrichtungen, von denen jede an einem Abend umgesetzt werden kann.

Entfernter Server über Streamable HTTP

Damit der Server auf einer separaten Maschine läuft oder sich mehrere Clients mit ihm verbinden, ersetze die letzte Zeile durch mcp.run(transport='streamable-http'). Standardmäßig startet der Server auf Port 8000, und die Verbindungsadresse ist http://adresse-der-maschine:8000/mcp. Gib in der Konfiguration des Clients statt command und args den Schlüssel url mit dieser Adresse an. In diesem Modus kann man nach stdout schreiben, aber es ist besser, die Gewohnheit zu behalten, nach stderr zu loggen. Schließe den Port unbedingt von der Außenwelt ab und füge eine Token-Prüfung im Header hinzu, wenn der Server nicht nur aus dem lokalen Netz erreichbar ist.

Ressourcen und Prompts

Eine Ressource mit den aktuellen Einstellungen hilft dem Agenten, den Arbeitskontext zu verstehen:

@mcp.resource('collector://settings')
def settings() -> str:
'''Текущие настройки сборщика.'''
return f'proxy: {"on" if PROXY_URL else "off"}, delay: {REQUEST_DELAY}, cache ttl: {CACHE_TTL}'

Ein Prompt definiert ein fertiges Szenario, das der Nutzer mit einem Befehl aufruft:

@mcp.prompt()
def price_monitor(url: str) -> str:
'''Сценарий мониторинга цен в каталоге.'''
return f'Открой {url}, собери ссылки на карточки товаров, зайди в каждую, вытащи название и цену и составь таблицу. Если увидишь ошибку 429, вызови rotate_ip и продолжи.'

Ergebnisse in eine Datei speichern

Füge ein Tool save_csv hinzu, das eine Liste von Wörterbüchern und einen Dateipfad entgegennimmt und die Daten über das Modul csv schreibt. Der Agent kann dann nicht nur sammeln, sondern die Ergebnisse auch in einer Tabelle ablegen, die du in Excel öffnest. Beschränke den Speicherpfad auf einen Ordner, damit der Agent nicht überall auf der Festplatte schreiben kann.

Asynchronität und paralleles Sammeln

FastMCP unterstützt asynchrone Funktionen: Deklariere ein Tool mit async def und verwende httpx.AsyncClient. Dann kann das Tool fetch_many zehn Seiten gleichzeitig über asyncio.gather laden. Vergiss nicht den Semaphor, der die Zahl paralleler Anfragen begrenzt, und bedenke, dass die Verzögerung zwischen Anfragen bei Parallelität anders berechnet werden muss.

Mehrere Proxys und intelligente Rotation

Wenn du mehrere mobile Proxys für verschiedene Regionen hast, speichere sie in einer Umgebungsvariable als kommagetrennte Liste und füge dem Tool einen Parameter region hinzu. Der Server wählt den Proxy nach Region, und der Agent kann Preise vergleichen, die die Seite Nutzern aus verschiedenen Städten zeigt. Das ist eine der gefragtesten Aufgaben bei Marketingspezialisten und Affiliate-Spezialisten.

Verpackung in Docker

Für den Start auf einem Server baue ein Image auf Basis von python:3.12-slim, kopiere server.py und die Abhängigkeitsdatei, installiere die Pakete und gib den Einstiegspunkt mit HTTP-Transport an. Übergebe die Proxy-Variablen beim Start des Containers und backe sie nicht ins Image.

⚠️ Achtung: Veröffentliche niemals Code mit Logins, Passwörtern und Rotationslinks in öffentlichen Repositories. Halte sie nur in Umgebungsvariablen oder in einer .env-Datei, die in .gitignore eingetragen ist. Ein Leck des IP-Wechsel-Links ermöglicht Unbekannten, deinen Proxy zu steuern.

FAQ: Häufige Fragen zum Erstellen eines MCP-Servers

Kann man einen MCP-Server auch in einer anderen Sprache als Python schreiben?

Ja. Offizielle SDKs gibt es für TypeScript, Java, Kotlin, C# und andere Sprachen. Die Prinzipien sind gleich: Tools mit Beschreibungen deklarieren und einen Transport starten. Python wurde in der Anleitung wegen der Einfachheit und des reichen Angebots an Bibliotheken für die Arbeit mit HTML gewählt.

Braucht man einen kostenpflichtigen Tarif des KI-Clients für die Arbeit mit MCP?

Claude Desktop unterstützt lokale MCP-Server auch im kostenlosen Plan, allerdings mit Limits bei der Zahl der Nachrichten. Cursor und VS Code erlauben ebenfalls das Verbinden von Servern. Prüfe die aktuellen Bedingungen beim jeweiligen Client.

Muss man unbedingt einen Proxy verwenden?

Nein, der Server funktioniert auch direkt. Ein Proxy ist nötig, wenn das Anfragevolumen spürbar ist, die Seiten empfindlich auf die Anfragefrequenz reagieren oder es dir wichtig ist, Inhalte aus einer bestimmten Region und von einer mobilen IP zu sehen.

Wie erkennt man, dass die Anfragen tatsächlich über den Proxy laufen?

Rufe das Tool current_ip auf und vergleiche die Adresse mit der, die der Kundenbereich des Anbieters zeigt. Zusätzlich kannst du den Agenten bitten, über extract_text die Seite eines IP-Bestimmungsdienstes zu laden.

Wie viele Tools kann man in einem Server haben?

Technisch gibt es fast keine Grenzen, aber jede Beschreibung belegt Platz im Kontext des Modells. Die Praxis zeigt, dass 5-15 gut beschriebene Tools besser funktionieren als 50 kleine. Gruppiere ähnliche Funktionen über Parameter.

Wie aktualisiert man den Server ohne Neustart des Clients?

Beim stdio-Transport startet der Client den Prozess beim Start, deshalb werden Codeänderungen erst nach einem Neustart des Clients übernommen. Im Entwicklungsmodus ist es bequemer, Änderungen über den Inspector zu prüfen und den Client am Ende neu zu starten.

Was tun, wenn die Seite Daten erst nach der Ausführung von JavaScript liefert?

Unser Server arbeitet mit dem ursprünglichen HTML und sieht solche Daten nicht. Optionen: die interne API der Seite im Network-Tab des Browsers finden oder eine Browser-Engine einbinden. Der zweite Weg wird in separaten Beiträgen des Blogs beschrieben, hier lassen wir ihn bewusst außen vor.

Wie beschränkt man den Agenten, damit er keine unerwünschten Seiten besucht?

Füge in _get_html eine Domänenprüfung über eine Whitelist oder Blacklist aus einer Umgebungsvariable hinzu und gib für verbotene Adressen einen verständlichen Fehler zurück. Das ist zuverlässiger, als sich auf Anweisungen im Chat zu verlassen.

Kann man einen MCP-Server gleichzeitig aus mehreren Clients nutzen?

Bei stdio startet jeder Client seine eigene Kopie des Prozesses, und das ist normal: Sie stören sich nicht, haben aber getrennte Caches. Für einen gemeinsamen Cache und einen einheitlichen Proxy wechsle zum HTTP-Transport aus dem fortgeschrittenen Block.

Fazit: Was du gemacht hast und wie es weitergeht

Fassen wir zusammen. Du hast die Python-Umgebung vorbereitet und das offizielle SDK des Protokolls installiert. Du hast einen MCP-Server von Grund auf geschrieben und verstanden, wie das Modell Tools über ihre Beschreibungen versteht. Du hast den Server mit dem KI-Client verbunden und gesehen, wie der Agent selbst Seiten lädt. Du hast Tools zum Extrahieren von Text, Links und Elementen per Selektor hinzugefügt. Du hast den Traffic über einen mobilen Proxy mit IP-Rotation geleitet. Schließlich hast du den Server robust gemacht: Wiederholungen, Verzögerungen, Cache und Limits. Das ist kein Lehrbeispiel mehr, sondern ein Arbeitswerkzeug für tägliche Aufgaben.

Was kommt als Nächstes? Beginne, den Server in echten Szenarien zu nutzen: Preisüberwachung bei Wettbewerbern, Sammeln von Bewertungen, Prüfen von Landingpages, Analyse von Inhalten in einer Nische. Unterwegs wirst du verstehen, welche Tools dir persönlich fehlen, und sie nach dem Vorbild der bestehenden hinzufügen. Jedes neue Tool ist eine Funktion mit einer klaren Beschreibung, nichts Komplizierteres.

Die nächste Stufe ist der fortgeschrittene Block: entfernter Server über HTTP, paralleles Sammeln, Arbeit mit mehreren Proxys nach Regionen und Speichern der Ergebnisse in Tabellen. Und wenn du an Seiten mit dynamischem Inhalt stößt, schau in die verwandten Blogbeiträge zur Browser-Automatisierung. Das Wichtigste hast du bereits geschafft: Dein KI-Agent ist über deinen eigenen MCP-Server ins Web gegangen, und du kontrollierst vollständig, wie er das tut.