---
name: backend-mcp-servidor
description: >
  Diseño de servidores MCP (Model Context Protocol) para exponer capacidades
  a Claude y otros clientes compatibles. Cubre los cinco patrones de producción
  de Anthropic: servidores remotos first, agrupación de tools por intención,
  code orchestration en sandbox para APIs grandes, elicitation mid-tool-call,
  y autenticación con CIMD + Vaults. Cargar cuando se diseñe o revise un
  servidor MCP, cuando se integre una API externa a agentes vía MCP en lugar
  de tool use directo, o cuando un servicio deba ser alcanzable desde Claude,
  ChatGPT, Cursor o VS Code sin rehacer la integración por cliente.
version: "1.0.0"
herramientasPermitidas: [Read, Write, Edit, Bash]
exclusiones:
  - "No cargar para diseñar el consumo de un servidor MCP existente — eso es tool use estándar, no diseño de servidor; este skill cubre SOLO el lado del servidor."
  - "No cargar para integraciones locales vía CLI (stdio transport) con ~10 tools — el overhead de diseñar un servidor MCP solo vale para casos con alcance multi-cliente o >50 tools."
  - "No cargar para reemplazar un API REST existente — un servidor MCP complementa APIs, no las sustituye; diseña primero la API REST y luego decide si exponerla vía MCP."
  - "No cargar para flujos de auth simples (API key estática) — MCP se beneficia de OAuth+CIMD; si el caso admite API key fija, un endpoint REST es más simple que un servidor MCP completo."
evolvable: true
---

# backend-mcp-servidor — Diseño de servidores MCP para producción

Diseñas servidores MCP que puedan alcanzar múltiples clientes (Claude, ChatGPT,
Cursor, VS Code) sin rehacer la integración para cada uno. El enfoque viene del
artículo de Anthropic sobre agentes en producción con MCP (2026-04) y de las
convenciones del MCP SDK oficial.

## Cuándo cargar

- Al diseñar un servidor MCP nuevo que expondrá una API interna a agentes.
- Al revisar un servidor MCP existente buscando reducir tokens en contexto.
- Al evaluar si un caso amerita MCP vs tool use directo con la API de Anthropic.
- Al integrar una API externa grande (>50 endpoints) como Cloudflare, GitHub,
  Notion con agentes.
- Al decidir la estrategia de autenticación de un servidor MCP multi-tenant.

## Cuándo NO cargar

- Integraciones con pocas tools (≤10) y un solo cliente (Claude Code local) —
  stdio transport con tool use directo es más simple.
- Reemplazo de una API REST existente — MCP complementa, no sustituye.
- Flujos de autenticación con API key estática sin OAuth — un endpoint HTTP
  básico es más ligero.
- Diseño del lado del consumidor — este skill es para el servidor; el consumo
  usa tool use estándar.

## Reglas obligatorias

1. **Remote-first si hay más de un cliente objetivo** — porque un servidor
   local con stdio transport solo alcanza un cliente; remoto (SSE o HTTP) con
   TLS alcanza cualquier cliente compatible con MCP y se despliega una sola vez.
2. **Agrupar tools por intención, no por endpoints** — porque un agente razona
   mejor con una tool de alto nivel (`create_issue_from_thread`) que con tres
   primitivos encadenados (`get_thread` → `parse_messages` → `create_issue`);
   menos pasos implican menos alucinaciones y menos tokens.
3. **Code orchestration para APIs con más de 50 endpoints** — porque listar
   500+ tools en contexto costaría miles de tokens y el modelo se pierde entre
   alternativas; 2 tools (`search` + `execute`) ejecutando scripts en sandbox
   son la solución probada (referencia: Cloudflare MCP server con ~2,500
   endpoints reducidos a ~1K tokens).
4. **Elicitation solo para acciones destructivas o de pago** — porque pausar
   la ejecución para pedir confirmación quiebra el flujo del agente; usarlo
   únicamente cuando la reversibilidad o el costo justifican la fricción.
5. **Autenticación OAuth con CIMD; NUNCA embeber credenciales en tool_input** —
   porque los parámetros de una tool se registran en logs, se muestran al
   usuario y quedan en el historial del agente; los tokens OAuth deben vivir
   en el servidor vía Vaults o gestor de secretos, no en el protocolo MCP.

## Patrón 1 — Remote-first vs local

Un servidor MCP local (stdio) solo lo ejecuta un cliente a la vez. Un servidor
remoto (SSE/HTTP con TLS) atiende a todos los clientes de todos los usuarios.

| Transporte | Cuándo |
|---|---|
| `stdio` | Desarrollo local, integraciones con CLI tools, ≤10 tools, un cliente |
| `SSE` (Server-Sent Events) | Producción single-tenant con streaming bidireccional |
| `HTTP streamable` | Producción multi-tenant con autenticación OAuth |

**Heurística:** si el servicio tiene clientes humanos y agentes, diseñar
directamente HTTP streamable. Nunca "empezar local y migrar después" — el
costo de reescribir auth y state management supera el beneficio.

## Patrón 2 — Tool grouping por intención

MAL (expone endpoints):

```python
@mcp.tool()
def get_thread(thread_id: str) -> dict: ...

@mcp.tool()
def parse_messages(thread: dict) -> list: ...

@mcp.tool()
def create_issue(title: str, body: str, labels: list[str]) -> str: ...
```

BIEN (expone intenciones):

```python
@mcp.tool()
def create_issue_from_thread(
    thread_id: str,
    extra_labels: list[str] | None = None,
) -> IssueCreated:
    """Crea un issue a partir de un hilo de Slack/Discord/email.

    Extrae automáticamente el título, el cuerpo y las etiquetas
    relevantes del hilo, crea el issue en el tracker configurado
    y retorna su URL.
    """
```

Criterio: si un agente invocaría las 3 tools primitivas siempre en el mismo
orden y con los mismos parámetros derivados, eso es UNA intención. Exponer solo
la intención reduce tokens y decisiones del modelo.

## Patrón 3 — Code orchestration para APIs grandes

Cuando la API tiene >50 endpoints, exponer dos tools que permitan al agente
descubrir y ejecutar dinámicamente:

```python
@mcp.tool()
def search(query: str, limit: int = 10) -> list[ToolDescriptor]:
    """Busca endpoints disponibles por descripción."""

@mcp.tool()
def execute(script: str, inputs: dict) -> ExecutionResult:
    """Ejecuta un script Python que compone llamadas a la API
    real en un sandbox aislado. El script recibe un cliente
    pre-autenticado como variable `api` y retorna JSON."""
```

Ejecución en sandbox (obligatorio — porque código arbitrario del agente
sin aislamiento es inyección confirmada): `subprocess` con `timeout`,
`resource` limits de CPU/memoria, sin acceso a filesystem del host,
red filtrada al dominio del API destino únicamente.

Cloudflare usa este patrón con sus ~2,500 endpoints, reduciendo la huella
en contexto a ~1K tokens vs >50K con tools individuales.

## Patrón 4 — Elicitation mid-tool-call

Permite pausar la ejecución de una tool para pedir input estructurado al
usuario (formulario) o mostrar una URL (flujo OAuth, pago):

```python
@mcp.tool()
async def transfer_funds(
    from_account: str,
    to_account: str,
    amount: Decimal,
) -> TransferResult:
    # Pausa: pide confirmación antes de ejecutar
    confirmation = await ctx.elicit(
        mode="form",
        schema={
            "type": "object",
            "properties": {
                "confirm": {"type": "boolean", "title": "¿Confirmas la transferencia?"},
            },
            "required": ["confirm"],
        },
    )
    if not confirmation["confirm"]:
        return TransferResult(status="cancelled_by_user")
    # Ejecuta solo si el usuario confirmó
    return await _ejecutar_transferencia(from_account, to_account, amount)
```

Modos: `form` (JSON Schema → UI de formulario), `url` (abrir navegador para
OAuth o pago), `text` (texto libre).

Criterio de uso: solo para operaciones irreversibles o con costo económico.
Pausar cada tool rompe la autonomía del agente y frustra al usuario.

## Patrón 5 — Autenticación OAuth con CIMD + Vaults

CIMD (Client ID Metadata Documents) estandariza el OAuth entre clientes MCP
sin que cada uno registre su app por separado.

Flujo recomendado:

1. El cliente MCP descubre el servidor → obtiene el manifiesto con
   `authorization_servers`.
2. Redirige al usuario al authorization server con CIMD del cliente.
3. El token emitido se guarda en el Vault del servidor MCP (no en el cliente,
   no en los parámetros de tools).
4. Cada tool_call del cliente incluye un session_id; el servidor resuelve
   las credenciales desde el Vault usando ese session_id.

Never hacer:

- Embeber `api_key` en los parámetros de una tool — queda en logs y
  en el historial del agente.
- Devolver tokens en el response de una tool — el modelo puede filtrarlos en
  su razonamiento visible.
- Usar una API key compartida para múltiples usuarios — rompe auditoría
  y el principio de menor privilegio.

## Estructura mínima del servidor

```
mi-mcp-servidor/
├── pyproject.toml          # mcp>=1.0.0, fastmcp, httpx
├── src/
│   ├── server.py           # entry point con FastMCP
│   ├── tools/              # una tool de intención por archivo
│   ├── auth/               # manejo de CIMD + Vault
│   ├── sandbox/            # execute() si usa code orchestration
│   └── resources/          # MCP resources (si aplica)
├── tests/
│   ├── test_tools_integration.py   # contra un cliente MCP real
│   └── test_auth_flow.py
└── README.md
```

Dependencias mínimas: `mcp` (SDK oficial Python) o `@modelcontextprotocol/sdk`
(TypeScript). Evitar abstracciones propietarias sobre MCP — el estándar
evoluciona y los wrappers se rompen.

## Gotchas

- **Tool search defiere carga, no la elimina** — los tools siguen existiendo;
  el modelo los pide por descripción cuando los necesita. Si el conteo es
  absurdo (>1,000), rediseñar con code orchestration; tool search sola no
  arregla un catálogo mal agrupado.
- **Elicitation no suspende el timeout del agente** — si el usuario tarda 5
  minutos en confirmar, el agente sigue consumiendo el reloj del cliente.
  Configurar timeouts explícitos en el servidor para reiniciar el diálogo si
  el usuario abandona.
- **Programmatic tool calling requiere que la API destino sea idempotente** —
  si el script del sandbox falla a la mitad, el re-run duplica efectos. Para
  operaciones no idempotentes, combinar con elicitation.
- **Vaults no son gratis en latencia** — resolver el secreto por session_id
  agrega ~5-20ms por tool_call. Cachear con TTL corto (60s) dentro del
  servidor, nunca en el cliente.
- **SSE vs HTTP streamable** — SSE es más simple pero no soporta request
  body grande; para inputs de tool_call con payload >1MB (documentos,
  imágenes), usar HTTP streamable.
- **Cualquier herramienta que toma credenciales como parámetro es una vulnerabilidad** —
  porque quedan en logs de request, en transcripts del agente y en historial
  del cliente. Siempre resolver credenciales por identidad del usuario, nunca
  aceptarlas como input.
- **MCP Apps con interfaces ricas requieren capability negotiation** — no todo
  cliente soporta renderizado de formularios o gráficos. El servidor debe
  degradar a respuesta texto plano cuando `capabilities.ui` está ausente.

## Integración con SWL

- **`reglas/seguridad-agentes.md`** — sección "Gobernanza de MCP" define
  cómo evaluar un servidor MCP antes de conectarlo desde SWL; este skill cubre
  el lado servidor correspondiente.
- **`/swl:mcp-status`** — valida salud de servidores MCP conectados; útil
  cuando desarrolles tu propio servidor para probar descubrimiento.
- **`backend-api-swl`** — agente que diseña APIs; invocar antes de este skill
  si la API base aún no existe.
- **`arquitecto-swl`** — si la decisión "MCP vs REST vs GraphQL" aún no está
  tomada, producir un ADR antes de cargar este skill.

## Referencias

- Artículo Anthropic 2026-04: "Building agents that reach production systems with MCP".
- MCP SDK oficial Python: https://github.com/modelcontextprotocol/python-sdk
- MCP SDK oficial TypeScript: https://github.com/modelcontextprotocol/typescript-sdk
- Directorio público de servidores MCP: https://github.com/modelcontextprotocol/servers
- Cloudflare MCP server (referencia de code orchestration).
