# ¿Las registraciones duplicadas de servidores MCP se están ejecutando dos veces?

Una registración MCP duplicada rara vez es un simple exceso de configuración sin consecuencias. Cuando dos entradas describen el mismo servidor con nombres distintos, un agente puede recibir dos rutas hacia la misma capacidad. Con stdio, eso suele significar dos procesos hijos. Con un endpoint remoto, puede significar dos conexiones autenticadas, dos inventarios de herramientas y dos lugares independientes desde los que el agente puede hacer una llamada equivocada.

Lo molesto es que la precedencia de configuración no resuelve este tipo de fallo. La precedencia solo decide qué ocurre cuando las entradas chocan por nombre. No determina si `repo-api`, `internal-api` y `my-api` son tres etiquetas para un mismo ejecutable y una misma cuenta. Trata la identidad de la registración como un asunto operativo, no como un asunto de nombres.

## Las registraciones duplicadas crean rutas de ejecución independientes

Dos entradas distintas de servidores MCP pueden iniciar dos veces lo mismo porque el cliente trata las registraciones como definiciones de conexión, no como alias que deba deduplicar. La especificación de transporte de MCP indica que un cliente inicia un servidor stdio como subproceso e intercambia JSON-RPC mediante la entrada y la salida estándar de ese proceso. Si dos entradas configuradas ejecutan el mismo comando, el resultado habitual son dos subprocesos, cada uno con su propia inicialización y ciclo de vida.

Eso no significa que un agente vaya a invocar mecánicamente todas las herramientas dos veces. Los modelos deciden qué herramienta expuesta llamar. En la práctica, el riesgo es peor: el agente puede ver herramientas con descripciones parecidas en ambas registraciones, llamar a una en el primer turno y a la otra durante un reintento, o usar las dos porque sus nombres sugieren responsabilidades distintas. Además, el inicio del servidor puede producir efectos secundarios antes de la primera llamada a una herramienta.

He visto servidores que parecen pasivos hasta que se revisa su ruta de inicialización. Renuevan un token de acceso. Crean un directorio de caché. Abren una base de datos SQLite local. Inician un proceso de consulta periódica para mantener caliente un índice. Registran un consumidor de webhooks. Ninguna de esas decisiones infringe MCP. Se vuelven problemáticas cuando un equipo supone que «servidor MCP» significa un único objeto compartido e inerte.

Un servidor remoto cambia la forma del fallo, pero no elimina la necesidad de prevenirlo. Streamable HTTP está diseñado para un proceso de servidor independiente capaz de gestionar varias conexiones de clientes. Eso resulta útil cuando varios clientes son intencionales. También significa que el servicio puede recibir dos conexiones que afirman pertenecer al agente del mismo desarrollador, a menos que el servicio tenga una forma clara de distinguirlas y limitar lo que pueden hacer.

Por eso, la primera pregunta de diagnóstico es sencilla: ¿estas dos entradas crean dos rutas de ejecución hacia la misma autoridad externa? Si la respuesta es sí, son duplicadas aunque su JSON sea distinto y sus nombres parezcan razonables.

## El nombre de un servidor no es su identidad

Una registración MCP tiene al menos dos identidades, y los equipos suelen mezclarlas.

La **identidad visible** es el nombre configurado, como `repo-api` o `staging-db`. Importa porque el cliente lo usa para presentar herramientas y resolver conflictos de configuración. Sirve para las personas y para la gestión interna del cliente.

La **identidad de ejecución** es aquello a lo que llega realmente la registración: un ejecutable con sus argumentos y el entorno relevante, o una URL remota con su contexto de autenticación. Sirve para los procesos y los sistemas externos.

En una configuración ordenada ambas identidades coinciden. No tienen por qué hacerlo, y suponer que coinciden es la razón por la que las duplicaciones sobreviven a las revisiones.

Considera estas entradas:

```json
{
  "mcpServers": {
    "billing": {
      "command": "python3",
      "args": ["tools/billing_mcp.py", "--account", "prod"]
    },
    "finance-tools": {
      "command": "python3",
      "args": ["tools/billing_mcp.py", "--account", "prod"]
    }
  }
}
```

Los nombres son distintos, pero se trata de un programa con los mismos argumentos. A menos que el propio programa imponga una única instancia, habrá dos inicios.

Ahora considera una versión menos evidente:

```json
{
  "mcpServers": {
    "deploy": {
      "command": "./bin/deploy-mcp",
      "args": ["--workspace", "/Users/dev/work/acme"]
    },
    "release-helper": {
      "command": "node",
      "args": ["scripts/mcp-launch.js", "deploy", "--workspace", "/Users/dev/work/acme"]
    }
  }
}
```

Una comparación de texto indica que estas entradas son diferentes. Una comparación a nivel de proceso puede mostrar que el wrapper inicia el mismo ejecutable `deploy-mcp` con el mismo espacio de trabajo. Por eso la revisión de configuración necesita una regla de identidad, no una simple comprobación de líneas duplicadas.

Usa este orden al comparar entradas:

1. Compara el endpoint remoto normalizado o el ejecutable final que inicia el comando.
2. Compara los argumentos que seleccionan una cuenta, un tenant, un repositorio, un espacio de trabajo o un destino de escritura.
3. Compara el directorio de trabajo y los nombres de las variables de entorno no secretas que cambian el comportamiento.
4. Compara por separado quién controla las credenciales. Dos entradas que llegan al mismo endpoint con autoridades distintas no son duplicados inofensivos. Son una decisión de diseño de permisos que necesita una justificación.

No compares los valores secretos para realizar esta auditoría. No los necesitas, y copiarlos en la salida de auditoría crea un segundo problema de seguridad. Registra que una entrada usa `BILLING_TOKEN` y otra `PERSONAL_BILLING_TOKEN`; después determina si esas variables autorizan a la misma cuenta.

## La precedencia de los ámbitos no puede limpiar nombres distintos

Claude Code documenta tres ámbitos MCP: local, de proyecto y de usuario. Las entradas del ámbito de proyecto viven en un archivo `.mcp.json` del repositorio, mientras que las del ámbito de usuario están disponibles en todos los proyectos. Su documentación también indica que un nombre de servidor idéntico se resuelve primero en el ámbito local, después en el de proyecto y por último en el de usuario. La documentación anterior llamaba «global» al ámbito de usuario.

Ese comportamiento te protege de un caso concreto: el mismo nombre aparece en más de un ámbito. No te protege de la registración duplicada habitual:

```text
User scope:    personal-git      -> /Users/dev/bin/git-mcp
Project scope: repository-git    -> /Users/dev/bin/git-mcp
```

Ambos nombres pueden seguir visibles porque no hay ningún conflicto de nombres. Los dos pueden iniciarse y exponer herramientas casi idénticas.

Hay otra trampa. Un desarrollador ve que el archivo del proyecto contiene `repository-git`, añade `personal-git` en el ámbito de usuario porque quiere usar la herramienta fuera de ese repositorio y después olvida que la entrada del usuario también se carga dentro del repositorio. La experiencia inmediata es agradable. La limpieza queda pendiente hasta que una llamada a una herramienta escribe dos registros de auditoría o un proceso en segundo plano bloquea el mismo directorio de estado.

Usa los ámbitos para definir la responsabilidad, no por comodidad:

- Coloca una registración en el ámbito del proyecto cuando el repositorio la necesite y la configuración sea segura para compartir.
- Colócala en el ámbito de usuario cuando sea una utilidad personal que deba funcionar en varios repositorios.
- Usa el ámbito local para un experimento privado y específico del repositorio que no deba incluirse en el commit.
- No dupliques una entrada del proyecto en el ámbito de usuario. Si la necesitas en otro lugar, invócala solo donde se aplique la configuración del proyecto o define una entrada deliberadamente separada, con un destino distinto y un propósito documentado.

También conviene prestar atención a una sustitución con el mismo nombre. No crea dos entradas activas como pueden hacerlo dos nombres distintos, pero puede ocultar una configuración del equipo detrás de una personal. El agente entonces realiza acciones con un ejecutable privado o un endpoint personal mientras los revisores creen que está vigente la definición del repositorio. Es un fallo de procedencia, no de cantidad de procesos, y también hay que corregirlo.

## Demuestra el duplicado desde la configuración hasta el proceso y la llamada

No elimines la primera entrada que parezca redundante. Establece la cadena desde la configuración hasta el proceso y la acción externa. Así evitarás una solución que se vea limpia pero elimine en silencio la única entrada que usa la cuenta o el espacio de trabajo correctos.

Empieza dentro del repositorio afectado:

```sh
claude mcp list
claude mcp get repository-git
claude mcp get personal-git
```

Anthropic documenta `claude mcp list`, `claude mcp get` y `claude mcp remove` como los comandos habituales de gestión. Usa la salida para identificar todos los nombres visibles y después inspecciona las entradas sospechosas una por una.

Anota cinco datos de cada entrada en un archivo temporal: nombre configurado, ámbito, comando o URL, argumentos y cuenta o espacio de trabajo externo al que llega. Evita pegar valores de entorno. En un servidor remoto, registra el host y la ruta, no un encabezado de autorización.

Después, inicia una sesión corta del agente e inspecciona los procesos mientras esté conectado. En macOS o Linux, sustituye `billing_mcp.py` por una parte única del comando que esperas:

```sh
ps -ax -o pid,ppid,lstart,command | grep '[b]illing_mcp.py'
```

Un inicio stdio duplicado produce una salida parecida a esta:

```text
91204 91188 Tue Jul 21 10:14:07 2026 python3 tools/billing_mcp.py --account prod
91219 91188 Tue Jul 21 10:14:09 2026 python3 tools/billing_mcp.py --account prod
```

Los identificadores de proceso son distintos. El proceso padre puede ser el mismo proceso del agente o dos procesos relacionados. La evidencia importante es que ambos comandos tienen la misma identidad de ejecución y ciclos de vida superpuestos.

Después realiza una llamada deliberadamente segura y de solo lectura a una herramienta. Elige una llamada con un resultado limitado y previsible, como recuperar el identificador de la cuenta actual o listar un objeto conocido. Comprueba los registros del sistema de destino, los del servidor o tu journal de acciones. Si ves dos conexiones independientes pero una sola llamada, has encontrado un inicio duplicado del servidor. Si ves dos llamadas, determina si el agente seleccionó dos herramientas, reintentó después de un error o el propio servidor repitió el trabajo. Son causas distintas que requieren soluciones distintas.

La especificación del ciclo de vida de MCP exige la inicialización antes de la operación normal. Ver dos eventos de inicialización basta para demostrar que existen dos conexiones. No demuestra que haya ocurrido una acción de negocio, así que no digas a los responsables del incidente «la implementación se ejecutó dos veces» solo porque hayas visto dos handshakes.

## Crea huellas de las configuraciones sin leer las credenciales

Una comprobación útil de duplicados produce una huella estable para cada entrada configurada y excluye los valores secretos. El siguiente script lee uno o más archivos de configuración JSON, extrae `mcpServers` y compara el transporte, el comando, los argumentos, la URL, el directorio de trabajo y los nombres de las variables de entorno. Entrégale solo archivos que tengas autorización para inspeccionar.

```python
#!/usr/bin/env python3
# save as mcp_duplicates.py
import hashlib
import json
import pathlib
import sys
from collections import defaultdict

if len(sys.argv) < 2:
    raise SystemExit("usage: mcp_duplicates.py CONFIG [CONFIG ...]")

entries = defaultdict(list)

for raw_path in sys.argv[1:]:
    path = pathlib.Path(raw_path).expanduser()
    with path.open() as handle:
        document = json.load(handle)

    for name, server in document.get("mcpServers", {}).items():
        identity = {
            "type": server.get("type", "stdio"),
            "command": server.get("command"),
            "args": server.get("args", []),
            "url": server.get("url"),
            "cwd": server.get("cwd"),
            "env_names": sorted(server.get("env", {}).keys()),
            "header_names": sorted(server.get("headers", {}).keys()),
        }
        encoded = json.dumps(identity, sort_keys=True, separators=(",", ":"))
        fingerprint = hashlib.sha256(encoded.encode()).hexdigest()[:12]
        entries[fingerprint].append((str(path), name, identity))

for fingerprint, matches in sorted(entries.items()):
    if len(matches) < 2:
        continue
    print(f"DUPLICATE EXECUTION IDENTITY {fingerprint}")
    for path, name, identity in matches:
        print(f"  {path}: {name}")
        print(f"    {json.dumps(identity, sort_keys=True)}")
```

Ejecútalo sobre el `.mcp.json` de un proyecto y sobre una exportación o copia saneada de la configuración a nivel de usuario que utilice tu cliente:

```sh
python3 mcp_duplicates.py .mcp.json ~/tmp/user-mcp.json
```

La salida debería parecerse a esta:

```text
DUPLICATE EXECUTION IDENTITY 64e0e2509d8a
  .mcp.json: repository-git
    {"args":["tools/git_mcp.py"],"command":"python3","cwd":null,"env_names":["GIT_ACCOUNT"],"header_names":[],"type":"stdio","url":null}
  /Users/dev/tmp/user-mcp.json: personal-git
    {"args":["tools/git_mcp.py"],"command":"python3","cwd":null,"env_names":["GIT_ACCOUNT"],"header_names":[],"type":"stdio","url":null}
```

Esta comprobación es deliberadamente conservadora. Marca las entradas con la misma forma de ejecución declarada. No puede demostrar que dos comandos wrapper distintos no converjan en un solo proceso, ni que dos URL distintas no dirijan al mismo servicio. Trata su salida como una lista de revisión, no como una lista automática de elementos que deban eliminarse.

También debes esperar falsos negativos cuando una configuración usa una ruta relativa y otra una ruta absoluta. Normaliza las rutas antes de compararlas si tu equipo usa ambas formas. Hazlo mediante un script controlado que conozca la raíz del repositorio. No hagas una búsqueda y sustitución amplia en los archivos de configuración.

## Dos instancias independientes pueden discrepar sobre el estado

Los fallos más costosos no siempre son las llamadas API duplicadas. Dos instancias pueden discrepar sobre el estado local aunque cada una se comporte exactamente como esperaba su autor.

Piensa en un servidor que mantiene una caché local de metadatos del repositorio. La instancia A se inicia con una copia de trabajo antigua y escribe registros de caché en un directorio predeterminado compartido. La instancia B se inicia después de cambiar de rama, lee el mismo directorio y decide que la caché es válida porque el archivo existe. Ahora una llamada a una herramienta devuelve datos que no corresponden a la vista actual de ninguno de los dos procesos. El agente puede realizar entonces una llamada perfectamente válida sobre un objeto obsoleto.

Otro fallo habitual afecta a un consumidor de colas. Ambas instancias se autentican como el mismo principal y consultan el mismo flujo de trabajos. Si la cola ofrece entrega «al menos una vez», es posible que ya se espere cierta gestión de duplicados. Si el autor de la herramienta añade un mapa local de deduplicación, cada proceso obtiene su propio mapa. El mapa evita duplicados dentro de un proceso y no hace nada entre ambos.

La mala recomendación en este caso es «haz que el servidor no tenga estado». Es popular porque suena segura y porque los servicios HTTP sin estado gestionan bien muchas conexiones. Es incorrecta para herramientas locales que mantienen intencionadamente cachés, estado de renovación OAuth, observadores de archivos o identificadores de operaciones. El requisito correcto es más concreto: documentar si se admiten instancias simultáneas, qué recursos comparten y qué ocurre cuando dos instancias usan la misma identidad.

Pide a los responsables del servidor que respondan estas preguntas en el README o en la salida de inicio:

- ¿El inicio escribe archivos locales, renueva credenciales o inicia una tarea en segundo plano?
- ¿Pueden dos procesos usar el mismo espacio de trabajo, cuenta y directorio de caché?
- ¿Cada llamada a una herramienta incluye una clave de idempotencia cuando modifica un sistema externo?
- ¿Puede un operador identificar el proceso o la sesión del cliente que produjo un registro?

Si la respuesta a la segunda pregunta es no, haz explícito el conflicto. Usa un bloqueo del sistema operativo, un directorio de ejecución único por proceso o un lease del lado del servidor. No dependas de que las personas recuerden que deben configurarlo una sola vez.

## La duplicación de herramientas y la duplicación de acciones son incidentes distintos

Un cliente puede exponer dos herramientas parecidas sin ejecutar ninguna dos veces. También puede realizar una acción externa repetida mediante una sola herramienta. Las investigaciones se desvían cuando alguien llama «MCP duplicado» a ambos resultados.

**Exposición duplicada de herramientas** significa que dos registraciones anuncian capacidades superpuestas. El agente puede ver `billing_get_invoice` en dos servidores. Es un riesgo de configuración y de instrucciones. Corrige las registraciones y las descripciones.

**Ejecución duplicada** significa que existen dos procesos locales o dos sesiones remotas. Es un riesgo de conexión y de ciclo de vida. Corrige la ruta de registro, el comportamiento de concurrencia del servidor o ambos.

**Acción externa repetida** significa que el sistema de destino recibió más de una solicitud significativa. Puede deberse a una exposición duplicada, lógica de reintento, tiempos de espera, intervención del usuario, comportamiento del servidor o un error del cliente. Demuéstralo con un identificador de operación en el destino, no deduciéndolo del número de procesos MCP.

Conserva juntos estos registros durante un incidente:

```text
Agent run ID:          run-7f3a
Configured name:       repository-git
Server process ID:     91204
MCP connection start:  2026-07-21T10:14:07Z
Tool request ID:       58
Target operation ID:   commit-3a8b
```

Los identificadores no tienen que usar exactamente esos nombres. Lo importante es que permitan relacionar el agente, el servidor y el servicio de destino. Si una capa no puede producir un valor de correlación, indícalo en el registro del incidente en lugar de rellenar el vacío con suposiciones basadas en el tiempo.

La separación de Sallyport entre un journal de Sessions para las ejecuciones del agente y un journal de Activity para las llamadas individuales resulta útil porque conserva esa distinción. Una segunda ejecución o conexión no demuestra automáticamente una segunda acción externa; los registros de llamadas aún deben demostrarlo.

## Elimina una registración sin crear un punto ciego

Cuando hayas identificado un duplicado real, elige una registración canónica antes de eliminar nada. La entrada canónica debe tener un responsable claro, un ámbito predecible, un comando o endpoint revisado y una fuente de credenciales definida. «Esta funcionaba en mi máquina» no es un criterio de selección.

Para una integración propiedad del equipo, la entrada del proyecto suele ser la mejor opción porque puede revisarse junto con la base de código. Mantén las credenciales fuera de ese archivo compartido. Claude Code admite la expansión de variables de entorno en `.mcp.json`, incluidos valores en comandos, argumentos, campos de entorno, URL y encabezados. Así es posible compartir definiciones sin incluir un token en el repositorio.

Para una utilidad personal que deba funcionar en varios proyectos, el ámbito de usuario puede ser el lugar correcto. En ese caso, elimina la entrada del proyecto solo si el repositorio no necesita una definición común para los demás colaboradores. No conviertas una dependencia del equipo en un requisito personal sin documentar.

Usa esta secuencia de validación silenciosa después del cambio:

1. Guarda la entrada eliminada fuera de la configuración activa durante la prueba.
2. Inicia un proceso nuevo del agente. Los procesos existentes pueden conservar conexiones antiguas.
3. Ejecuta `claude mcp list` e inspecciona la entrada restante con `claude mcp get <name>`.
4. Realiza una llamada segura de solo lectura y registra una conexión y una solicitud al destino.
5. Reinicia una vez más y confirma que la registración eliminada no vuelve a aparecer.

Si la eliminación rompe un flujo de trabajo, restaura solo la definición canónica y corrige la ruta, la variable de entorno o los permisos que falten. No restaures ambas entradas como solución rápida. Eso recrearía la ambigüedad que acabas de dedicar tiempo a diagnosticar.

## Convierte la detección de duplicados en parte de la revisión de configuración

El mejor control es una pequeña regla de revisión: cada registración MCP necesita un responsable, un ámbito y una identidad de ejecución única para su propósito. Con eso basta para detectar la mayoría de los accidentes antes de que se ejecuten los agentes.

Incluye la comprobación de huellas en un script del repositorio si el equipo mantiene `.mcp.json` bajo control de versiones. Ejecútala durante la revisión local y en la integración continua sobre la configuración compartida. No verá las entradas del ámbito de usuario de un desarrollador, así que incluye también `claude mcp list` en la lista de preparación de quienes informen de un comportamiento extraño de las herramientas.

Para la configuración del ámbito de usuario, mantén un inventario breve fuera de la propia configuración. Basta con una línea por servidor:

```text
personal-git | user | git tooling across repositories | owner: developer
repository-git | project | repository release workflow | owner: platform team
```

Si ambas líneas apuntan al mismo ejecutable y a la misma cuenta, una de ellas debe desaparecer o sus destinos deben diferenciarse de forma intencionada. No aceptes dos etiquetas solo porque una suene más amable en una instrucción.

La disciplina es sencilla: una ruta para cada propósito, responsabilidad visible y pruebas de que una sola acción solicitada produjo un solo registro externo. Con eso, un proceso duplicado se convierte en un defecto observable y deja de ser un misterio nocturno oculto detrás de dos nombres de herramientas casi idénticos.
