Skip to main content
Glama
Gandrex87

holded-mcp-server

by Gandrex87
README.md
# Integración de MCP Server personalizado de Holded con n8n

## 📋 Contexto
Este documento describe el proceso para conectar un servidor MCP personalizado de Holded con n8n a través del Docker MCP Gateway, permitiendo usar las herramientas de Holded en workflows de n8n.

## 🎯 Objetivo
Exponer las herramientas del MCP Server de Holded (list_documents, get_document, get_document_pdf, create_document) en n8n para automatizar procesos de facturación y gestión de documentos.


## 📁 Configuración Inicial
```markdown
Estructura de archivos
C:\Users\andres\.docker\mcp\
├── catalogs/
│   ├── docker-mcp.yaml          # Catálogo oficial de Docker
│   └── mycustomcatalog.yaml      # Catálogo personalizado con Holded
├── config.yaml                   # Configuración de servidores
├── registry.yaml                 # Registro de servidores habilitados
└── tools.yaml                    # Configuración de herramientas
```
Catálogo personalizado de Holded
Archivo: C:\Users\andres\.docker\mcp\catalogs\mycustomcatalog.yaml
```markdown
name: custom
displayName: Custom MCP Servers
registry:
  holded:
    description: "Holded API integration for document management"
    title: "Holded MCP Server"
    type: server
    dateAdded: "2025-10-10T00:00:00Z"
    image: holded-mcp-server:latest
    ref: ""
    tools:
      - name: list_documents
        description: "List all documents of a specific type from Holded"
      - name: get_document
        description: "Get a specific document by ID from Holded"
      - name: get_document_pdf
        description: "Retrieve the PDF version of a specific document from Holded"
      - name: create_document
        description: "Create a new document in Holded with basic required fields"
    metadata:
      category: productivity
      tags:
        - holded
        - invoicing
        - documents
    secrets:
      - name: HOLDED_API_KEY
        env: HOLDED_API_KEY
        example: 4fb330a5b8c8d8692fd307568bdf2XXX

```
🚀 Proceso de Implementación

## Paso 1: Configuración inicial con Claude Desktop (stdio)
La configuración inicial funcionaba solo para Claude Desktop:
Archivo: claude_desktop_config.json
```markdown
json{
  "mcpServers": {
    "mcp-toolkit-gateway": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-v", "/var/run/docker.sock:/var/run/docker.sock",
        "-v", "C:\\Users\\andres\\.docker\\mcp:/mcp",
        "docker/mcp-gateway",
        "--catalog=/mcp/catalogs/docker-mcp.yaml",
        "--catalog=/mcp/catalogs/mycustomcatalog.yaml",
        "--config=/mcp/config.yaml",
        "--registry=/mcp/registry.yaml",
        "--tools-config=/mcp/tools.yaml",
        "--transport=stdio"
      ]
    }
  }
}
```
Limitación: El transporte stdio solo permite un cliente (Claude Desktop) y no funciona con n8n.

## Paso 2: Migración a HTTP Streaming para n8n

### 2.1 Detener contenedor anterior (si existe)
```markdown
powershelldocker ps  # Identificar el contenedor del gateway
docker stop <container-id>
docker rm <container-id>
```
### 2.2 Iniciar gateway en modo HTTP Streaming
PowerShell (Windows):
```PowerShell
powershelldocker run -d `
  --name mcp-gateway `
  --restart=unless-stopped `
  -p 8080:8080 `
  -v /var/run/docker.sock:/var/run/docker.sock `
  -v C:\Users\andres\.docker\mcp:/mcp `
  docker/mcp-gateway `
  --catalog=/mcp/catalogs/docker-mcp.yaml `
  --catalog=/mcp/catalogs/mycustomcatalog.yaml `
  --config=/mcp/config.yaml `
  --registry=/mcp/registry.yaml `
  --tools-config=/mcp/tools.yaml `
  --transport=streaming `
  --port=8080
```

```bash
Bash (Linux/Mac):
bashdocker run -d \
  --name mcp-gateway \
  --restart=unless-stopped \
  -p 8080:8080 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /home/user/.docker/mcp:/mcp \
  docker/mcp-gateway \
  --catalog=/mcp/catalogs/docker-mcp.yaml \
  --catalog=/mcp/catalogs/mycustomcatalog.yaml \
  --config=/mcp/config.yaml \
  --registry=/mcp/registry.yaml \
  --tools-config=/mcp/tools.yaml \
  --transport=streaming \
  --port=8080
  
```

### 2.3 Verificar que el gateway esté funcionando
powershell# Ver logs del contenedor

```bash
docker logs mcp-gateway
```
### Deberías ver esta línea al final:
### > Start streaming server on port 8080
Logs esperados:
```bash
- Reading configuration...
- Configuration read in 1.254530312s
- Using images:
  - holded-mcp-server:latest
  - mcp/obsidian@sha256:...
- Those servers are enabled: holded, obsidian
- Listing MCP tools...
  > holded: (4 tools)
  > obsidian: (12 tools)
> 16 tools listed in 1.719879359s
> Initialized in 3.070423919s
> Start streaming server on port 8080
```
### 2.4 Probar conectividad del gateway
powershell# Desde el navegador (esperarás ver este mensaje, que es correcto)
http://localhost:8080

### Respuesta esperada:
### Accept must contain 'text/event-stream' for GET requests
Esto confirma que el gateway está funcionando correctamente.

## Paso 3: Configuración en n8n
### 3.1 Agregar nodo MCP Client Tool
En tu workflow de n8n:

Busca y agrega el nodo "MCP Client Tool"
Conéctalo a tu nodo AI Agent

### 3.2 Configurar el nodo MCP Client Tool
```markdown
ParámetroValorServer TransportHTTP Streamable ✅Endpointhttp://host.docker.internal:8080AuthenticationNoneTools to IncludeAll (o selecciona específicamente las de Holded)

```
### 3.3 Verificar herramientas disponibles
Después de guardar la configuración, el nodo debería mostrar las 16 herramientas disponibles:
De Holded (4 herramientas):

list_documents
get_document
get_document_pdf
create_document

De Obsidian (12 herramientas):

obsidian_append_content
obsidian_batch_get_file_contents
obsidian_complex_search...


### 📊 Comparación: SSE vs HTTP Streamable
SSE (Server-Sent Events) - /sse
Características
AspectoDetalleComunicaciónUnidireccional (servidor → cliente)Endpointhttp://localhost:8080/sseUso típicoNotificaciones, actualizaciones en tiempo realComplejidadMás simpleOverheadBajo para eventos simples
```markdown
Ventajas

✅ Protocolo estándar ampliamente soportado
✅ Reconexión automática integrada
✅ Ideal para push de eventos
```

```markdown
Desventajas

❌ Solo servidor → cliente (unidireccional)
❌ Limitado para interacciones complejas
❌ No óptimo para agentes de n8n que requieren bidireccionalidad
```

HTTP Streamable (Recomendado para n8n)
Características
AspectoDetalleComunicaciónBidireccional (cliente ↔ servidor)Endpointhttp://localhost:8080Uso típicoAgentes AI, llamadas a herramientas, workflows complejosComplejidadModeradaOverheadOptimizado para interacciones activas

```markdown
Ventajas

✅ Bidireccional: Cliente y servidor pueden comunicarse activamente
✅ Múltiples clientes: Un gateway puede servir Claude Desktop, n8n y otros simultáneamente
✅ Mejor para agentes: Workflows de n8n requieren enviar comandos y recibir respuestas
✅ Más eficiente: Menos overhead para casos interactivos
✅ Escalable: Diseñado para producción
```

```markdown
Desventajas

⚠️ Requiere gestión de timeouts para operaciones largas
⚠️ Necesita restart policy para alta disponibilidad
```

### Tabla comparativa resumida

CaracterísticaSSEHTTP StreamableDirecciónUnidireccional ➡️Bidireccional ↔️Para n8n⚠️ Limitado✅ ÓptimoMúltiples clientes⚠️ Conexiones separadas✅ Un gateway centralAgentes AI❌ No ideal✅ Diseñado para estoComplejidad setupBajaModeradaRecomendado paraNotificaciones simplesWorkflows complejos
¿Cuándo usar cada uno?
EscenarioTransporte recomendadoSolo notificaciones/eventosSSEWorkflows con n8nHTTP Streamable ✅Múltiples clientes (Claude + n8n)HTTP Streamable ✅Agentes AI con llamadas a herramientasHTTP Streamable ✅Producción escalableHTTP Streamable ✅

### 🔧 Habilitar servidores
Si algún servidor no aparece en n8n, verifica que esté habilitado:
powershell# Listar servidores habilitados
docker mcp server ls

# Habilitar el servidor de Holded
docker mcp server enable holded

# Habilitar múltiples servidores
docker mcp server enable holded obsidian

# Ver detalles de un servidor
docker mcp server inspect holded

###🔐 Configuración de secretos
Verificar secretos existentes
powershelldocker mcp secret ls
Agregar secreto de Holded (si no existe)
powershelldocker mcp secret set HOLDED_API_KEY
### Ingresa tu API key cuando se solicite
Exportar secretos (para Docker Cloud)
powershelldocker mcp secret export holded

### 🐛 Troubleshooting
### Problema 1: "Could not connect to your MCP server" en n8n
Síntomas:
Error in sub-node 'MCP Client'
Could not connect to your MCP server
Solución:

Verifica que el gateway esté corriendo en modo streaming:

powershell   docker logs mcp-gateway | Select-String "streaming"
   # Debe mostrar: > Start streaming server on port 8080

Prueba conectividad:

powershell   curl http://localhost:8080
   # Debe responder: Accept must contain 'text/event-stream' for GET requests

Verifica el endpoint en n8n:

Usa: http://host.docker.internal:8080
Transport: HTTP Streamable

### Problema 2: Gateway arranca en modo stdio
Síntomas:
> Start stdio server
Causa: Falta el parámetro --transport=streaming
Solución: Recrea el contenedor con el comando correcto del Paso 2.2

### Problema 3: Las herramientas de Holded no aparecen
Verificar:
powershell# 1. Ver si el servidor está habilitado
docker mcp server ls

# 2. Ver si el catálogo está cargado
docker mcp catalog show custom

# 3. Ver herramientas disponibles
docker mcp tools ls | Select-String "holded"
Solución:
powershell# Habilitar el servidor
docker mcp server enable holded

# Reiniciar el gateway
docker restart mcp-gateway

### Problema 4: Error "Accept must contain 'text/event-stream'"
Esto NO es un error ✅
Es la respuesta esperada cuando accedes al gateway sin los headers correctos. Confirma que el gateway está funcionando.

✅ Verificación final
Checklist de configuración exitosa:

 Gateway corriendo en modo streaming (puerto 8080)
 Logs muestran: > Start streaming server on port 8080
 curl http://localhost:8080 responde con mensaje de Accept
 Servidor Holded habilitado: docker mcp server ls
 16 herramientas listadas: docker mcp tools count
 n8n configurado con HTTP Streamable en http://host.docker.internal:8080
 Herramientas de Holded visibles en n8n

Comando de diagnóstico completo:
powershell# Estado del gateway
docker ps | Select-String "mcp-gateway"

# Logs recientes
docker logs --tail 20 mcp-gateway

# Servidores habilitados
docker mcp server ls

# Herramientas disponibles
docker mcp tools ls

# Secretos configurados
docker mcp secret ls

## 🎯 Configuración final recomendada
Docker Compose (Opcional - para persistencia)
Si quieres gestionar todo con Docker Compose:
yamlversion: '3.8'

services:
  mcp-gateway:
    image: docker/mcp-gateway
    container_name: mcp-gateway
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - C:\Users\andres\.docker\mcp:/mcp
    command:
      - --catalog=/mcp/catalogs/docker-mcp.yaml
      - --catalog=/mcp/catalogs/mycustomcatalog.yaml
      - --config=/mcp/config.yaml
      - --registry=/mcp/registry.yaml
      - --tools-config=/mcp/tools.yaml
      - --transport=streaming
      - --port=8080
    networks:
      - n8n_network

  n8n:
    image: n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    networks:
      - n8n_network

networks:
  n8n_network:
    driver: bridge

### 📚 Referencias

Docker MCP Gateway Documentation
Model Context Protocol Specification
n8n MCP Client Tool Documentation


### 📝 Notas adicionales
Uso simultáneo de Claude Desktop y n8n
Ambos pueden coexistir:

Claude Desktop → Sigue usando stdio con su configuración original
n8n → Usa HTTP Streamable apuntando al gateway

No hay conflicto porque el gateway en modo streaming puede atender múltiples clientes simultáneamente.
Seguridad

Los secretos se gestionan a través de Docker Desktop
El gateway tiene privilegios mínimos (no-new-privileges)
Los contenedores MCP corren aislados con límites de CPU y memoria

Rendimiento

Límites por defecto: 1 CPU, 2GB RAM por servidor MCP
16 herramientas se listan en ~2 segundos
Inicialización completa en ~3 segundos


Fecha de documentación: 13 de octubre, 2025
Versión Docker MCP Gateway: 2.0.1
Versión n8n: (verificar con docker inspect n8n_local-n8n-1)