Saltar al contenido principal
Servidor MCP de Ahrefs: configuración para Claude, Codex y los demás
Volver al Blog
SEO y Marketing 3 de agosto de 2026 12 min de lecturapor Matthias Meyer

Servidor MCP de Ahrefs: configuración para Claude, Codex y los demás

Hay dos servidores MCP de Ahrefs y dos tipos de clave, y la combinación equivocada falla en silencio. La conexión para cada cliente y las trampas que cuestan dinero.

Contenido

Lo primero que ocurre cuando conectas Ahrefs a un cliente de IA es que no ocurre nada. Ningún error, ninguna herramienta, solo un servidor que parece conectado y se queda mudo. En mi caso la causa era trivial: Ahrefs mantiene dos servidores MCP distintos y dos tipos distintos de clave de API, y solo una de las cuatro combinaciones posibles es la que quieres hoy. Nadie te dice cuál has elegido.

Esa es la versión corta de por qué existe esta guía. La larga es que pasé un ciclo de suscripción enviando unas 1.100 llamadas registradas por este sistema, y lo que más tiempo me costó no fue el análisis SEO. Fue la instalación.

A qué te conectas realmente#

Ahrefs opera un servidor MCP alojado en https://api.ahrefs.com/mcp/mcp. Habla Streamable HTTP, el transporte actual de la especificación del Model Context Protocol y el que soporta cualquier cliente serio. SSE está obsoleto y no conviene construir nada nuevo sobre él.

Detrás de ese endpoint está casi todo lo que normalmente consultas en la interfaz web de Ahrefs: Site Explorer para backlinks y palabras clave orgánicas, Keywords Explorer para volumen y dificultad, Rank Tracker, Site Audit, y la integración con Search Console si tienes una cuenta vinculada. En mi instalación eran 130 herramientas invocables, más de las que anuncian las páginas comerciales, porque el servidor ha ido creciendo.

Conviene saber dos cosas antes de conectar nada. El acceso empieza en el plan Lite, así que una cuenta de prueba gratuita no entra. Y cada llamada facturable consume del mismo presupuesto mensual de unidades de API que el uso normal de la API, lo que significa que tu asistente de chat y tus tareas programadas comen del mismo plato. La facturación se divide en tres: bastantes endpoints no cuestan nada, algunos cobran una tarifa fija por petición y el resto cobra por fila. La última sección trata de distinguirlos.

Dos servidores, dos tipos de clave, un fallo silencioso#

Esta es la parte que me costó la primera tarde.

Existe un servidor local más antiguo, publicado como @ahrefs/mcp en npm y como ahrefs/ahrefs-mcp-server en GitHub. Ese repositorio está archivado, y su readme contiene una frase que conviene leer dos veces: funciona únicamente con claves de API v3, y no funciona con claves MCP.

El servidor alojado se comporta justo al revés. Quiere una clave con ámbito MCP, que se genera aparte en tu cuenta de Ahrefs. Ahrefs afirma sin rodeos que las claves de API y las claves MCP no son intercambiables.

La matriz queda así. Dos casillas funcionan, pero solo una de ellas es una elección sensata hoy:

Clave API v3Clave con ámbito MCP
Local @ahrefs/mcpfuncionaba, repositorio ahora archivadofalla
Remoto /mcp/mcpfallacorrecto

La combinación archivada no está tanto rota como abandonada. Sigue funcionando si ya la tienes, pero no recibe mantenimiento y Ahrefs remite al servidor alojado.

Los modos de fallo son discretos. Una clave MCP apuntada a la API REST devuelve Unauthorized, lo cual al menos te dice algo. Un cliente que no completa el handshake suele mostrar el servidor con cero herramientas, y entonces buscas una errata de configuración que no existe.

Si empiezas hoy: servidor alojado, clave con ámbito MCP, y descarta cualquier tutorial que te haga instalar algo por npm.

Autenticación: OAuth es la vía oficial, Bearer es la útil#

Ahrefs documenta OAuth como el camino estándar. Tu cliente abre una ventana del navegador, inicias sesión, y las credenciales quedan guardadas. Para trabajo interactivo está bien y es la opción menos engorrosa.

Se complica en cuanto quieres que un trabajo programado extraiga datos un domingo a las siete de la mañana. OAuth necesita a una persona frente a un navegador para la autorización inicial, y a partir de ahí mantienes una renovación de token que tiene que seguir funcionando sin supervisión. Así que para todo lo desatendido me autentico con un token Bearer y envío la clave MCP directamente en la cabecera Authorization. Mismo endpoint, mismas herramientas, sin navegador y sin nada que renovar.

La regla práctica a la que llegué: Bearer para todo lo que deba sobrevivir sin mí, OAuth para el portátil delante del que estoy sentado. Si solo lo usas en chat, acepta el aviso de OAuth y sáltate las secciones siguientes.

Claude Code#

Un comando, y el indicador de ámbito decide si el servidor vive en este proyecto o en tu configuración de usuario.

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

El ámbito de proyecto escribe en un .mcp.json junto al código, que es lo correcto cuando la clave pertenece a un cliente o a un sitio concreto. Ese archivo debe entrar en .gitignore antes de que pegues una clave en él, porque la cabecera queda ahí en texto plano.

Dos cosas me costaron tiempo aquí. Las herramientas aparecen como mcp__ahrefs__<nombre>, no como ahrefs.<nombre>, lo cual importa cuando escribes prompts que nombran una herramienta explícitamente. Y una sesión en marcha carga sus servidores MCP solo al arrancar, de modo que la conexión que acabas de añadir aparece en la siguiente sesión, no en esta. Reinicié tres veces convencido de que la configuración estaba mal.

Claude Desktop#

No hace falta archivo de configuración. Ajustes, luego Connectors, luego Add custom connector, y pegas la URL del endpoint. El client ID y el secreto de OAuth van en los ajustes avanzados, si tu servidor los necesita.

Hay un detalle de arquitectura que sorprende y que cambia lo que puedes conectar. Claude Desktop no alcanza tu servidor MCP desde tu máquina, sino desde la infraestructura en la nube de Anthropic. Para un servicio alojado como Ahrefs eso da igual. Para un servidor que corre en tu propio portátil o detrás de una VPN corporativa lo cambia todo, porque ese servidor tiene que ser accesible desde internet antes de que funcione siquiera.

Las cuentas gratuitas están limitadas a un conector propio; las de pago no.

Codex#

Codex lee ~/.codex/config.toml de forma global, o un .codex/config.toml en un directorio de proyecto marcado como de confianza. Una tabla TOML por servidor, y el transporte se deduce de qué claves defines: un command significa stdio, un url significa Streamable HTTP.

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

Fíjate en lo que espera bearer_token_env_var. Es el nombre de una variable de entorno, no el token en sí. Si escribes ahí la clave directamente, acabas con un archivo de configuración lleno de secreto y un servidor lleno de nada.

El subcomando codex mcp add existe, pero está pensado para servidores stdio. Para un endpoint remoto, editar el TOML es más rápido y más fácil de versionar. Verifica con codex mcp list.

Cursor, VS Code y Windsurf#

Los tres hablan el mismo JSON con una diferencia molesta: la clave que contiene la URL. Cursor la llama url. VS Code quiere url junto a un "type": "http" explícito, la misma forma que usa Claude Code. Windsurf la llama serverUrl. Todo lo demás del bloque se copia sin cambios, así que una configuración que funciona en un editor está a treinta segundos de renombrado de funcionar en el siguiente.

VS Code soporta MCP de forma nativa desde la 1.99, a través de Copilot Chat. Windsurf lo incorporó a principios de año. Si tu equipo está repartido entre editores, escribe el bloque una vez y guarda las tres variantes como snippet.

Sin ningún cliente, de forma desatendida#

MCP es un protocolo de sesión, no una llamada REST simple. Inicializas, envías una notificación de inicialización, y solo entonces puedes invocar una herramienta. Cada petición posterior lleva el identificador de sesión que recibiste.

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"}'

Tres cosas ahí se hacen mal con facilidad. No te saltes la segunda llamada: el identificador de sesión por sí solo no cierra el handshake, y un servidor que nunca recibió la notificación de inicialización rechaza las llamadas a herramientas. La cabecera Accept necesita ambos tipos de contenido aunque nunca pretendas leer un stream. Y a partir de la revisión 2025-06-18, toda petición posterior a la inicialización debe llevar la cabecera MCP-Protocol-Version, así que va en la notificación y en cada llamada a herramienta posterior, junto a mcp-session-id.

Envolver esto en un pequeño script de shell merece la pena, porque pone los mismos datos a disposición de tareas programadas y agentes que no tienen ninguna interfaz de chat. El mío está publicado como ahrefs-mcp-kit por si prefieres partir de algo que ya funciona. Un aviso por experiencia: si construyes ese script con un valor por defecto de bash como ${ARG:-{}} para el argumento JSON, el emparejamiento de llaves dentro del valor por defecto añade en silencio una llave de cierre sobrante y produce JSON inválido. La llamada falla sin salida y sin error. Define el valor por defecto en una línea aparte.

Tu plan decide tu límite de filas, y ahí está lo caro#

Esta es la sección que ojalá hubiera leído primero, porque explica un error que me costó un presupuesto mensual entero en una sola mañana.

Ahrefs escalona dos cosas por nivel de suscripción: cuántas unidades recibes al mes, y cuántas filas puede devolver una única petición.

PlanUnidades al mesFilas por petición
Lite100.000100
Standard400.000250
Advanced1.000.000500
Enterprise2.000.000sin límite

Esas cifras cambiaron el 28 de abril de 2026, y cambiaron mucho. Lite pasó de 25.000 unidades a 100.000 y de 10 filas a 100. Standard pasó de 150.000 a 400.000 unidades y de 25 filas a 250. Advanced duplicó sus unidades y pasó de 100 filas a 500.

Ahora ponlo junto a cómo se factura. Una llamada cuesta un mínimo de 50 unidades, y por encima de ese suelo pagas por fila, multiplicado por las columnas que hayas pedido. Las columnas premium como volume, keyword_difficulty y traffic_domain añaden unas diez unidades por fila cada una.

Junta ambos hechos y aparece la trampa. La llamada más cara de todo mi registro se ejecutó con un límite de 250 filas. No es una cifra que eligiera tras pensarlo. Es exactamente el límite de filas del plan que tenía, y la usé porque era el máximo disponible. Cada una de esas llamadas costó 5.250 unidades. La misma consulta con 50 filas habría costado 1.050 y me habría dicho lo mismo, porque las filas 51 a 250 eran ruido de cola larga que nunca llegué a usar.

El límite de filas no es una recomendación. Es un techo, y desde abril ese techo está hasta diez veces más alto que antes. Para código con un límite explícito eso es inofensivo: 50 sigue siendo 50. Lo nota quien no pasa ningún límite, quien pide el máximo vigente en cada momento, y quien tiene una consulta que antes quedaba cortada por el techo antiguo y ahora devuelve las diez veces más filas completas. Los tres casos son habituales en scripts escritos antes de la primavera, y ninguno se ve distinto en el código.

Fija el límite según lo que vayas a leer de verdad. Para expansión de palabras clave ahora empiezo en 50 y solo subo cuando un resultado quedó visiblemente cortado en el borde y las filas extra cambian algo.

Diez trampas que me costaron una ejecución cada una#

Ninguna está en la documentación de forma que la encuentres antes de tropezar. Todas produjeron o un error que hubo que descifrar o, peor, un resultado vacío que parecía un hallazgo.

where es JSON, nunca una expresión en texto. Escribir "position>3 and position<15" devuelve bad where: invalid JSON syntax. El mismo filtro en JSON necesita una cláusula por condición:

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

Los operadores son gt, gte, lt, lte, eq.

select cambia de tipo entre herramientas. Site Explorer y Keywords Explorer quieren una cadena separada por comas. batch-analysis quiere un array. Confundirlos da column '["domain"' not found en un sentido y expected array but got string en el otro.

No transcribas los acentos ni las diéresis. La coincidencia de palabras clave es literal. Un término alemán escrito con ue en lugar de ü devuelve cero filas y parece una palabra clave muerta. Lo mismo vale para las tildes en español.

El filtro de país puede borrar la verdad en silencio. Filtrar palabras clave orgánicas por país devolvió cero filas para un dominio que demostrablemente posiciona, porque sus posiciones estaban en otros países. Pagué 50 unidades por una respuesta vacía y casi concluyo que el dominio no posicionaba para nada. Extrae sin el filtro y toma keyword_country como columna.

Usa mode: "subdomains" por defecto. En cualquier sitio cuyo dominio raíz redirige a www, mode: "domain" mide el host exacto y devuelve ceros fantasma. He visto un dominio informar de unos cientos de backlinks y ningún tráfico en modo domain frente a veintidós mil backlinks en modo subdomains.

volume-history acepta keyword, en singular. Pasar keywords falla con required arguments [keyword] are missing. Veintiséis llamadas en una tanda, todas rechazadas, todas por una letra.

Hoy no es un date_to válido. Incluso los endpoints agregados rechazan la fecha actual con bad date_to. Usa la de ayer.

Nunca busques una incidencia de Site Audit por su nombre. Los nombres no son únicos. Encontré el mismo nombre asociado a dos identificadores distintos, uno de ellos permanentemente vacío. Toma el identificador de tu propia respuesta de incidencias en lugar de buscar por texto.

Las tablas de detalle vacías de Search Console no significan conexión rota. Los endpoints de detalle se materializan con unas seis semanas de retraso, así que una consulta de los últimos 30 días vuelve vacía mientras los agregados están al día. He visto a dos personas concluir que su integración estaba muerta cuando funcionaba perfectamente.

Trata org_traffic como un modelo, no como una medición. Se estima a partir de las palabras clave del índice de Ahrefs multiplicadas por una curva de clics. Lo que el índice no recoge sencillamente no existe en esa cifra. En los sitios de nicho que contrasté con datos reales de Search Console se quedaba muy por debajo, en un caso por un factor que no habría creído sin ver ambas cifras juntas. En tus propios dominios, Search Console es la verdad. En dominios ajenos, etiquétalo como suelo y no como cifra.

Agota las superficies gratuitas antes de gastar#

El mejor hábito que me llevo: todo lo asociado a un proyecto propio verificado es gratis o casi gratis, y casi nadie lo toca.

En todo mi registro, los endpoints de Search Console, los de gestión y la consulta gratuita de domain rating costaron en conjunto cero unidades y devolvieron algo menos de 6.000 filas. Hay exactamente una excepción y es una trampa: gsc-anonymous-queries lleva el mismo prefijo gsc- pero factura desde 50 unidades por llamada y en un sitio pequeño no devuelve casi nada. site-audit-page-explorer cuesta 50 unidades fijas por petición independientemente de cuántas filas vuelvan, lo que sobre casi 10.000 filas salió a un tercio de unidad por fila, con más de veinte campos técnicos por URL. El rank tracker y la información de suscripción también son gratuitos.

Al otro lado de la cuenta: tres herramientas, todas consultas de palabras clave y backlinks facturadas por fila, supusieron el 78 por ciento de todo lo que gasté. Las superficies gratuitas y baratas devolvieron unas 18.000 filas por 3.400 unidades. Las tres caras devolvieron 30.000 filas por 592.000. Eso es un factor de unas cien veces de valor con el mismo presupuesto.

El orden, entonces: comprueba las unidades restantes, agota las superficies gratuitas del proyecto, profundiza con las herramientas de tarifa plana, y solo después recurre a las facturadas por fila, con un límite que hayas elegido a conciencia.

Lo que le diría a alguien que empieza mañana#

Genera una clave con ámbito MCP, apunta tu cliente al endpoint alojado y no instales nada. Usa OAuth si trabajas en chat, Bearer si algo tuyo corre según un calendario. Antes de tu primera consulta real, abre la herramienta de información de suscripción y lee tu límite de filas del plan en lugar de suponerlo.

Y luego apunta tus propias cifras. Los costes de este artículo son lo que medí en un plan a lo largo de 1.102 llamadas, y el modelo de precios se movió notablemente en abril. Cada respuesta lleva incorporado su coste real, o sea que la herramienta te dice lo que ha cobrado si te molestas en mirar. Dos días registrando ese campo me enseñaron más sobre dónde se va el dinero que cualquier cantidad de documentación.

Matthias Meyer

Matthias Meyer

Founder & AI Director

Founder & AI Director de StudioMeyer. Construye sitios web y sistemas de IA desde hace más de 10 años. Vive en Mallorca desde hace 15 años y dirige allí un estudio de IA y diseño: diseño web, conectores IA, sistemas de IA y modelos propios, además de cuatro servidores MCP de autoservicio.

Claude + Claude Code

Tres posts más del mismo cluster temático que muestran cómo encaja el cuadro:

Visión general del cluster: Claude en 2026: modelos, apps, Claude Code y la API