Zum Hauptinhalt springen
Ahrefs MCP Server: Einrichtung für Claude, Codex und den Rest
Zurück zum Blog
Mit KI arbeiten 3. August 2026 Aktualisiert: 30. September 2026 12 min Lesezeitvon Matthias Meyer

Ahrefs MCP Server: Einrichtung für Claude, Codex und den Rest

Es gibt zwei Ahrefs-MCP-Server und zwei Schlüsselarten, und die falsche Paarung scheitert lautlos. Die Verdrahtung pro Client, plus die Fallen, die echtes Geld kosten.

Inhalt▾

Das Erste, was passiert, wenn man Ahrefs an einen KI-Client hängt, ist: nichts. Keine Fehlermeldung, keine Tools, nur ein Server, der verbunden aussieht und stumm bleibt. Bei mir war die Ursache banal: Ahrefs betreibt zwei verschiedene MCP-Server und zwei verschiedene Arten von API-Schlüssel, und nur eine der vier möglichen Kombinationen ist heute die richtige. Welche man erwischt hat, sagt einem niemand.

Das ist die Kurzfassung, warum es diesen Text gibt. Die lange Fassung: Ich habe über einen Abrechnungszeitraum rund 1.100 protokollierte Abrufe durch dieses Ding geschickt, und was am meisten Zeit gekostet hat, war nicht die SEO-Analyse. Es war die Installation.

Womit man sich eigentlich verbindet#

Ahrefs betreibt einen gehosteten MCP-Server unter https://api.ahrefs.com/mcp/mcp. Er spricht Streamable HTTP, den aktuellen Transport der Model Context Protocol Spezifikation, und den unterstützt jeder ernsthafte Client. Der ältere HTTP+SSE-Transport gilt als überholt. SSE als Antwortformat von Streamable HTTP bleibt nutzbar.

Hinter diesem Endpunkt liegt fast alles, was man sonst in der Ahrefs-Weboberfläche anklickt: Site Explorer für Backlinks und organische Keywords, Keywords Explorer für Volumen und Schwierigkeit, Rank Tracker, Site Audit, und die Search Console Anbindung, sofern ein Konto verknüpft ist. In meiner Installation waren das 130 aufrufbare Werkzeuge, also mehr als die Marketing-Seiten nennen, weil der Server gewachsen ist.

Zwei Dinge sollte man vorher wissen. Der Zugang beginnt beim Lite-Tarif, ein kostenloses Testkonto kommt nicht hinein. Und jeder abrechenbare Abruf zieht aus demselben monatlichen Kontingent an API-Units wie die normale API-Nutzung, was bedeutet: Der Chat-Assistent und die Cronjobs essen vom selben Teller. Die Abrechnung teilt sich dabei in drei: Etliche Endpunkte kosten gar nichts, manche einen Pauschalpreis je Anfrage, der Rest pro Zeile. Der letzte Abschnitt handelt davon, sie auseinanderzuhalten.

Zwei Server, zwei Schlüsselarten, ein stiller Fehlschlag#

Das ist der Teil, der meinen ersten Abend gekostet hat.

Es gibt einen älteren lokalen Server, veröffentlicht als @ahrefs/mcp auf npm und als ahrefs/ahrefs-mcp-server auf GitHub. Dieses Repository ist inzwischen archiviert, und in seiner Readme steht ein Satz, den man zweimal lesen sollte: Es funktioniert ausschließlich mit API-v3-Schlüsseln, und es funktioniert nicht mit MCP-Schlüsseln.

Der gehostete Server verhält sich genau umgekehrt. Er will einen Schlüssel mit MCP-Scope, den man im Ahrefs-Konto getrennt erzeugt. Ahrefs schreibt unmissverständlich, dass API-Schlüssel und MCP-Schlüssel nicht austauschbar sind.

Die Matrix sieht damit so aus. Zwei Felder funktionieren, aber nur eines davon ist heute eine sinnvolle Wahl:

API-v3-SchlüsselMCP-Schlüssel
Lokal @ahrefs/mcpfunktionierte, Repo jetzt archiviertscheitert
Remote /mcp/mcpscheitertrichtig

Die archivierte Kombination ist weniger kaputt als aufgegeben. Sie läuft weiter, wenn man sie schon hat, wird aber nicht mehr gepflegt, und Ahrefs verweist stattdessen auf den gehosteten Server.

Die Fehlerbilder sind leise. Ein MCP-Schlüssel gegen die REST-API liefert Unauthorized, das ist immerhin eine Auskunft. Ein Client, der den Handshake nicht abschließt, zeigt den Server oft einfach mit null Tools an, und dann sucht man einen Tippfehler in einer Konfiguration, die keinen hat.

Wer heute anfängt: gehosteter Server, MCP-Schlüssel, und jede Anleitung ignorieren, die etwas per npm installieren will.

Anmeldung: OAuth ist der offizielle Weg, Bearer der nützliche#

Ahrefs dokumentiert OAuth als Standardweg. Der Client öffnet ein Browser-Fenster, man meldet sich an, die Anmeldedaten werden zwischengespeichert. Für interaktive Arbeit ist das in Ordnung und tatsächlich die bequemste Variante.

Unangenehm wird es in dem Moment, in dem ein geplanter Job sonntagmorgens um sieben Daten ziehen soll. OAuth braucht für die erste Autorisierung einen Menschen vor einem Browser, und danach pflegt man eine Token-Erneuerung, die unbeaufsichtigt weiterlaufen muss. Für alles Kopflose melde ich mich deshalb mit einem Bearer-Token an und schicke den MCP-Schlüssel direkt im Authorization-Header. Gleicher Endpunkt, gleiche Tools, kein Browser, nichts zu erneuern.

Die Regel, bei der ich gelandet bin: Bearer für alles, was ohne mich laufen muss, OAuth für den Rechner, vor dem ich sitze. Wer das nur im Chat nutzt, nimmt die OAuth-Anmeldung und überspringt die nächsten Abschnitte.

Claude Code#

Ein Befehl, und die Scope-Angabe entscheidet, ob der Server in diesem Projekt oder in der Benutzer-Konfiguration landet.

claude mcp add --transport http ahrefs https://api.ahrefs.com/mcp/mcp \
  --header "Authorization: Bearer $AHREFS_API_KEY" -s project

Projekt-Scope schreibt in eine .mcp.json neben dem Code, was richtig ist, wenn der Schlüssel zu einem Kunden oder einer Site gehört. Diese Datei gehört in die .gitignore, bevor ein Schlüssel hineinkommt, denn der Header steht dort im Klartext.

Zwei Dinge haben mich hier Zeit gekostet. Die Tools heißen mcp__ahrefs__<toolname>, nicht ahrefs.<toolname>, was zählt, sobald man in einem Prompt ein Werkzeug namentlich anspricht. Und eine laufende Sitzung lädt ihre MCP-Server ausschließlich beim Start, die eben hinzugefügte Verbindung erscheint also in der nächsten Sitzung, nicht in dieser. Ich habe dreimal neu gestartet in der Überzeugung, die Konfiguration sei falsch.

Claude Desktop#

Keine Konfigurationsdatei nötig. Einstellungen, dann Connectors, dann Add custom connector, dann die Endpunkt-URL einfügen. OAuth Client ID und Secret liegen unter den erweiterten Einstellungen, falls der Server sie braucht.

Ein architektonisches Detail überrascht dabei viele und entscheidet, was man überhaupt anbinden kann. Claude Desktop erreicht den MCP-Server nicht vom eigenen Rechner aus, sondern aus der Cloud-Infrastruktur von Anthropic. Bei einem gehosteten Dienst wie Ahrefs spielt das keine Rolle. Bei einem Server, der auf dem eigenen Laptop oder hinter einem Firmen-VPN läuft, macht es den ganzen Unterschied: Der muss erst aus dem öffentlichen Internet erreichbar sein, bevor überhaupt etwas funktioniert.

Kostenlose Konten sind auf einen eigenen Connector begrenzt, bezahlte nicht.

Codex#

Codex liest ~/.codex/config.toml global, oder eine .codex/config.toml in einem Projektverzeichnis, das man als vertrauenswürdig markiert hat. Eine TOML-Tabelle pro Server, und der Transport ergibt sich daraus, welche Schlüssel gesetzt sind: Ein command bedeutet stdio, ein url bedeutet Streamable HTTP.

[mcp_servers.ahrefs]
url = "https://api.ahrefs.com/mcp/mcp"
bearer_token_env_var = "AHREFS_API_KEY"

Wichtig ist, was bearer_token_env_var erwartet. Dort steht der Name einer Umgebungsvariable, nicht das Token selbst. Wer den Schlüssel direkt hineinschreibt, bekommt eine Konfigurationsdatei voller Geheimnis und einen Server voller nichts.

Codex unterstützt auch entfernte Server über die CLI: codex mcp add ahrefs --url https://api.ahrefs.com/mcp/mcp. Für einen Bearer-Zugang kann die oben gezeigte TOML-Konfiguration mit bearer_token_env_var verwendet werden. Prüfe die Verbindung mit codex mcp list; die Codex-Dokumentation beschreibt auch OAuth.

Cursor, VS Code und Windsurf#

Die Editoren unterscheiden nicht nur den URL-Schlüssel, sondern auch Dateipfad, Hüllobjekt und Geheimnisverwaltung. In VS Code liegt das native Format in .vscode/mcp.json mit einem obersten servers-Objekt; das portable Format in .mcp.json verwendet mcpServers. Für HTTP nennt die Konfiguration url und "type": "http". Cursor und Windsurf haben eigene Einrichtungsvorgaben. Übertrage deshalb nicht den ganzen Block durch bloßes Umbenennen: Prüfe Format, Pfad, Anmeldung und den ersten Aufruf in jedem Client. Die VS-Code-Konfigurationsreferenz zeigt auch Eingabevariablen für Geheimnisse.

Pflege für das Team je Client ein geprüftes Snippet. Speichere echte Zugangsdaten getrennt und veröffentliche sie nicht mit der Projektkonfiguration.

Kopflos, ganz ohne Client#

Das folgende Beispiel beschreibt den Handshake der Revision 2025-06-18, den ich für diese Ahrefs-Messung verwendet habe. Es ist kein allgemeines Muster für die aktuelle Revision 2026-07-28: Dort stehen Metadaten pro Anfrage im Vordergrund, Protokollsessions sind entfernt. Nutze für Ahrefs die vom Dienst unterstützte Revision und übernimm die Sitzungs-ID nur, wenn der Server eine vergibt. Der aktuelle Unterschied steht in der HTTP-Spezifikation.

curl -sD hdr -X POST "$AHREFS_MCP_URL" \
  -H "Authorization: Bearer $AHREFS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
       "protocolVersion":"2025-06-18","capabilities":{},
       "clientInfo":{"name":"sm","version":"1"}}}'

SID=$(grep -i '^mcp-session-id:' hdr | awk '{print $2}' | tr -d '\r')

curl -s -X POST "$AHREFS_MCP_URL" \
  -H "Authorization: Bearer $AHREFS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-06-18" \
  -H "mcp-session-id: $SID" \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}'

Drei Dinge daran macht man leicht falsch. Den zweiten Aufruf nicht weglassen: Die Sitzungs-ID allein schließt den Handshake nicht ab, und ein Server, der die Initialized-Benachrichtigung nie bekommen hat, weist Werkzeug-Aufrufe zurück. Der Accept-Header braucht beide Inhaltstypen, auch wenn man nie vorhat, einen Stream zu lesen. Und ab der Revision 2025-06-18 muss jede Anfrage nach der Initialisierung den Header MCP-Protocol-Version tragen, er gehört also an die Benachrichtigung und an jeden folgenden Werkzeug-Aufruf, neben mcp-session-id.

Das Beispiel ist für 2025-06-18 geschrieben. Es bestätigt nicht, welche Revision Ahrefs heute unterstützt. Übernimm es nur, wenn der Dienst diese Revision akzeptiert; neuere Clients können einen anderen Ablauf verwenden.

Das in ein kleines Shell-Skript zu packen lohnt sich, weil dieselben Daten damit auch Cronjobs und Agenten offenstehen, die gar keine Chat-Oberfläche haben. Meines liegt als ahrefs-mcp-kit öffentlich, falls du lieber mit etwas anfängst, das schon läuft. Eine Warnung aus Erfahrung: Wer in so einem Helfer den JSON-Parameter mit einem Bash-Default wie ${ARG:-{}} vorbelegt, bekommt durch das Klammer-Matching in der Vorbelegung stillschweigend eine überzählige schließende Klammer und damit ungültiges JSON. Der Aufruf scheitert dann ohne Ausgabe und ohne Fehler. Die Variable gehört in eine eigene Zeile.

Der Tarif bestimmt die Zeilengrenze, und genau das wird teuer#

Diesen Abschnitt hätte ich gern zuerst gelesen, denn er erklärt einen Fehler, der mich an einem einzigen Vormittag ein Monatsbudget gekostet hat.

Ahrefs staffelt das monatliche Kontingent und die maximalen Zeilen pro Anfrage. Die folgende Tabelle zeigt den Tarifstand meines damaligen Messzeitraums nach der April-Änderung, kein aktuelles Tarifangebot. Für heutige Kontingente gilt die aktuelle Ahrefs-Hilfe, die inzwischen höhere Werte nennt.

TarifUnits pro MonatZeilen pro Anfrage
Lite100.000100
Standard400.000250
Advanced1.000.000500
Enterprise2.000.000unbegrenzt

Diese Zahlen haben sich am 28. April 2026 geändert, und zwar deutlich. Lite ging von 25.000 auf 100.000 Units und von 10 auf 100 Zeilen. Standard von 150.000 auf 400.000 Units und von 25 auf 250 Zeilen. Advanced verdoppelte die Units und ging von 100 auf 500 Zeilen.

Daneben steht die offizielle Kostenformel: max(50, Zeilen × Feldkosten). Feldkosten sind die Summe der Kosten aller eindeutigen Felder in Ausgabe, Filter und Sortierung. Ein Feld zählt auch dann nur einmal, wenn es mehrfach vorkommt. Der Standardwert ist eine Unit je Feld; teurere Felder haben eigene Kosten, keine pauschalen zusätzlichen zehn Units.

Beides zusammen ergibt die Falle. Der teuerste Aufruf meines gesamten Protokolls lief mit einer Grenze von 250 Zeilen. Das ist keine Zahl, die ich nach Überlegung gewählt hätte. Es ist exakt die Zeilengrenze meines Tarifs, und ich habe danach gegriffen, weil sie das Maximum war. Jeder dieser Aufrufe kostete 5.250 Units. Dieselbe Abfrage mit 50 Zeilen hätte 1.050 gekostet und dasselbe ergeben, weil die Zeilen 51 bis 250 Rauschen waren, das ich nie benutzt habe.

Die Zeilengrenze ist keine Empfehlung. Sie ist eine Decke, und seit April liegt diese Decke je nach Tarif bis zu zehnmal höher als vorher. Für Code mit einem ausdrücklichen Grenzwert ist das harmlos, 50 bleibt 50. Zu spüren bekommt es, wer gar kein Limit übergibt, wer nach dem jeweils aktuellen Maximum greift, und wer eine Abfrage laufen hat, die früher an der alten Decke abgeschnitten wurde und jetzt die vollen zehnmal mehr Zeilen liefert. Alle drei Fälle sind in Skripten von vor dem Frühjahr üblich, und keiner sieht im Code anders aus.

Die Grenze sollte sich danach richten, was man tatsächlich liest. Für Keyword-Erweiterung beginne ich inzwischen bei 50 und gehe nur höher, wenn ein Ergebnis sichtbar an der Grenze abgeschnitten war und die zusätzlichen Zeilen etwas ändern.

Zehn Fallen, die mich je einen Durchlauf gekostet haben#

Keine davon steht so in der Dokumentation, dass man sie findet, bevor man hineinläuft. Alle haben entweder eine Fehlermeldung erzeugt, die entziffert werden wollte, oder schlimmer: ein leeres Ergebnis, das wie ein Befund aussah.

where ist JSON, nie ein String-Ausdruck. Wer "position>3 and position<15" schreibt, bekommt bad where: invalid JSON syntax. Derselbe Filter als JSON braucht eine Klausel je Bedingung:

{"and":[{"field":"position","is":["gt",3]},
        {"field":"position","is":["lt",15]}]}

Operatoren sind gt, gte, lt, lte, eq.

select wechselt den Typ zwischen den Werkzeugen. Site Explorer und Keywords Explorer wollen einen kommagetrennten String. batch-analysis will ein Array. Verwechselt man das, kommt in der einen Richtung column '["domain"' not found und in der anderen expected array but got string.

Umlaute nicht umschreiben. Die Keyword-Suche vergleicht wörtlich. Ein deutscher Begriff mit ue statt ü liefert null Zeilen und sieht aus wie ein totes Keyword. Für spanische Akzente gilt dasselbe.

Der Länderfilter kann die Wahrheit stillschweigend wegschneiden. Eine Filterung organischer Keywords nach Land ergab null Zeilen für eine Domain, die nachweislich rankt, weil ihre Platzierungen in anderen Ländern lagen. Ich habe 50 Units für eine leere Antwort gezahlt und wäre fast zum Schluss gekommen, die Domain ranke für nichts. Besser ohne Filter ziehen und keyword_country als Spalte mitnehmen.

Standardmäßig mode: "subdomains" verwenden. Bei jeder Site, deren Apex auf www weiterleitet, misst mode: "domain" nur den exakten Host und liefert Phantom-Nullen. Ich habe eine Domain gesehen, die im Domain-Modus ein paar hundert Backlinks und keinen Verkehr meldete, im Subdomain-Modus aber zweiundzwanzigtausend Backlinks.

volume-history erwartet keyword, im Singular. Mit keywords scheitert der Aufruf an required arguments [keyword] are missing. Sechsundzwanzig Aufrufe in einem Durchgang, alle abgewiesen, alle wegen eines Buchstabens.

Heute ist kein gültiges date_to. Selbst die aggregierten Endpunkte weisen das aktuelle Datum mit bad date_to ab. Gestern nehmen.

Ein Site-Audit-Problem nie über den Namen suchen. Die Namen sind nicht eindeutig. Ich habe denselben Namen an zwei verschiedenen Problem-IDs gefunden, von denen eine dauerhaft leer war. Die ID gehört aus der eigenen Issues-Antwort genommen, nicht über einen Textabgleich.

Leere Search-Console-Detailtabellen bedeuten keine kaputte Verbindung. Die Detail-Endpunkte materialisieren mit rund sechs Wochen Verzögerung. Eine Abfrage über die letzten 30 Tage kommt also leer zurück, während die Aggregate bis gestern aktuell sind. Ich habe zwei Leute erlebt, die daraus schlossen, ihre Anbindung sei tot, während sie einwandfrei lief.

org_traffic ist eine Schätzung, keine Messung. Ahrefs modelliert organischen Suchtraffic aus seinen Keyword- und Rankingdaten. In meiner Stichprobe unterschätzte der Wert den gemessenen Traffic; daraus folgt keine garantierte Untergrenze für andere Domains. Für eigene Domains nutze zusätzlich die Search Console und beachte deren eigenen Geltungsbereich.

Erst die kostenlosen Flächen, dann Geld ausgeben#

Die beste Gewohnheit, die ich mitgenommen habe: Alles, was zu einem verifizierten eigenen Projekt gehört, ist kostenlos oder fast kostenlos, und fast niemand rührt es an.

Über mein gesamtes Protokoll kosteten die Search-Console-Endpunkte, die Verwaltungs-Endpunkte und die kostenlose Domain-Rating-Abfrage zusammen null Units und lieferten knapp 6.000 Zeilen. Es gibt genau eine Ausnahme davon, und sie ist eine Falle: gsc-anonymous-queries trägt dasselbe gsc--Präfix, rechnet aber ab 50 Units pro Abruf ab und liefert auf einer kleinen Site fast nichts. site-audit-page-explorer kostet pauschal 50 Units pro Anfrage, unabhängig davon, wie viele Zeilen zurückkommen, was über knapp 10.000 Zeilen auf etwa ein Drittel Unit pro Zeile hinauslief, bei mehr als zwanzig technischen Feldern je URL. Rank Tracker und Abo-Informationen sind ebenfalls kostenlos.

Auf der anderen Seite der Rechnung: Drei Werkzeuge, allesamt zeilenbasierte Keyword- und Backlink-Abfragen, standen für 78 Prozent meiner gesamten Ausgaben. Die günstigen und kostenlosen Flächen lieferten rund 18.000 Zeilen für 3.400 Units. Die drei teuren lieferten 30.000 Zeilen für 592.000. Das ist rund Faktor hundert im Gegenwert bei gleichem Budget.

Die Reihenfolge lautet also: Restbudget prüfen, die kostenlosen Projektflächen ausschöpfen, mit den pauschal abgerechneten Audit-Werkzeugen in die Tiefe gehen, und erst dann zu den zeilenbasierten greifen, mit einer Grenze, die man bewusst gewählt hat.

Was ich jemandem sagen würde, der morgen anfängt#

Einen Schlüssel mit MCP-Scope erzeugen, den Client auf den gehosteten Endpunkt richten, nichts installieren. OAuth nehmen, wer im Chat arbeitet, Bearer, wenn etwas nach Zeitplan laufen soll. Vor der ersten echten Abfrage die Abo-Information aufrufen und die eigene Zeilengrenze aus dem Tarif ablesen, statt sie anzunehmen.

Und dann die eigenen Zahlen aufschreiben. Die Kosten in diesem Text sind das, was ich auf einem Tarif über 1.102 Abrufe gemessen habe, und das Preismodell hat sich im April spürbar bewegt. Jede Antwort trägt ihre tatsächlichen Kosten mit, das Werkzeug sagt einem also, was es berechnet hat, wenn man hinsieht. Zwei Tage dieses Feld mitzuschreiben hat mir mehr darüber beigebracht, wo das Geld hingeht, als jede Menge Dokumentation.

Matthias Meyer

Matthias Meyer

Founder & AI Director

Founder & AI Director von StudioMeyer. Baut seit über 10 Jahren Websites und KI-Systeme. Lebt seit 2011 auf Mallorca und führt dort ein KI- und Webdesign-Studio: Webdesign, KI-Verbinder, KI-Systeme und eigene Modelle, dazu drei MCP-Server zum Selbstbedienen.

Änderungshistorie

  • Ahrefs-Setup um Codex-HTTP, Revisionsgrenzen und korrekte Kostenformel ergänzt; Tariftabelle historisch eingeordnet.
KI-Werkzeuge einrichten

Drei weitere Posts aus dem gleichen Themen-Cluster die zeigen wie das Bild zusammenpasst:

Cluster-Übersicht: Claude Code für KMU: Sechs Begriffe für den Einstieg