HubSpot CRM MCP Server

Servidor MCP de HubSpot CRM: contactos, negocios, pipelines. Escrituras idempotentes y un registro de auditoría completo.

Documentación

Servidor MCP de HubSpot CRM

CI License: MIT

Servidor MCP de HubSpot CRM para Claude Desktop y cualquier cliente MCP, compatible con el nivel gratuito. 15 herramientas para contactos, deals y pipelines, autenticado con un token de aplicación privada de HubSpot o con una app OAuth completa. Las escrituras son idempotentes, de modo que una llamada de herramienta reintentada reproduce su primer resultado en lugar de crear un registro duplicado, y cada llamada de herramienta se registra en una traza de auditoría con PII redactada, incluidas las llamadas denegadas por falta de scope y las llamadas que terminaron en error.

Relacionado: Servidor MCP de QuickBooks Online · Puerta de enlace de auditoría MCP · Lo que un MCP de producción realmente exige

HubSpot ejecuta un servidor MCP propio alojado, con una cobertura de objetos más amplia que este. Este está pensado para autoalojarse: tú ejecutas el proceso, el código fuente es lo bastante corto como para leerse de una sentada, y la traza de auditoría, la caché y las credenciales nunca salen de tu infraestructura.

El resto es lo que un contenedor REST normalmente omite. Solicita el scope exacto que falta en lugar de filtrar un 403 en crudo, reintenta los límites de frecuencia con retroceso exponencial, sirve las lecturas de registros desde una caché local (TTL más invalidación por escritura, con un manejador de webhook verificado por firma que puedes conectar a tu propio punto de entrada HTTP para cambios fuera de banda), recorre la paginación por cursor y devuelve resultados por elemento cuando un lote falla parcialmente.

Consulta docs/COMPARISON.md para ver transcripciones lado a lado de un MCP que envuelve la API de forma ingenua frente a este. Cada transcripción se genera ejecutando ambos contra el conjunto de pruebas simulado.

Arquitectura

flowchart TD
    Agent["MCP client / agent"] -->|"stdio (JSON-RPC)"| Server["FastMCP server<br/>server.py"]
    Server --> Service["CrmService<br/>scope checks · audit · orchestration"]

    Service --> Cache["LocalCache<br/>TTL + write invalidation"]
    Service --> Idem["Idempotency store"]
    Service --> Audit["Audit log<br/>PII redaction"]
    Service --> Client["HubSpotClient<br/>retries · pagination · error mapping"]

    Client --> Auth["Token provider<br/>private-app · OAuth refresh"]
    Client -->|HTTPS| HubSpot["HubSpot CRM API"]

    Ingress["Your HTTP ingress<br/>(optional, host-provided)"] -->|"signed v3 payload"| Processor["WebhookProcessor<br/>verify_signature"]
    Processor -->|"invalidate(object)"| Cache

El servidor stdio habla únicamente JSON-RPC; no escucha webhooks. WebhookProcessor y verify_signature se distribuyen como un componente probado que montas en tu propio punto de entrada HTTP (consulta Invalidación de caché por webhook).

Herramientas

Cada herramienta lleva anotaciones MCP (readOnlyHint, destructiveHint, idempotentHint, openWorldHint), un esquema de entrada descrito y un esquema de salida.

HerramientaQué haceScope
crm_search_contactsBusca contactos por término de texto libre, una página a la vezcrm.objects.contacts.read
crm_list_contactsLista contactos en orden de id con paginación por cursorcrm.objects.contacts.read
crm_get_contactObtiene un contacto por id, servido desde la caché localcrm.objects.contacts.read
crm_create_contactCrea un contacto, idempotente sobre una clave suministrada o derivadacrm.objects.contacts.write
crm_update_contactSobrescribe las propiedades pasadas e invalida la entrada de cachécrm.objects.contacts.write
crm_delete_contactArchiva (borrado lógico) un contactocrm.objects.contacts.write
crm_batch_create_contactsCrea hasta 100 contactos e informa de los fallos por filacrm.objects.contacts.write
crm_search_dealsBusca deals por término de texto libre con paginación por cursorcrm.objects.deals.read
crm_get_dealObtiene un deal por id, servido desde la caché localcrm.objects.deals.read
crm_create_dealCrea un deal, idempotente sobre una clave suministrada o derivadacrm.objects.deals.write
crm_update_dealActualiza propiedades de un deal, incluido moverlo a otra etapacrm.objects.deals.write
crm_delete_dealArchiva (borrado lógico) un dealcrm.objects.deals.write
crm_list_pipelinesLista los pipelines de deals con sus etapas en orden de visualización (en caché)crm.schemas.deals.read
crm_get_pipelineObtiene un pipeline con sus etapas ordenadascrm.schemas.deals.read
crm_export_audit_logExporta la traza de auditoría de la sesión como JSON Linesninguna

Inicio rápido

Ejecútalo sin instalar nada permanente:

uvx mcp-hubspot

O instálalo:

pip install mcp-hubspot
mcp-hubspot

Cualquiera de las dos formas habla MCP sobre stdio en stdin y stdout. Autentícate con cualquiera de las dos: un token de aplicación privada (HUBSPOT_PRIVATE_APP_TOKEN) o una app OAuth (HUBSPOT_CLIENT_ID + HUBSPOT_CLIENT_SECRET + HUBSPOT_REFRESH_TOKEN). El servidor detecta automáticamente cuál está presente.

Las credenciales se resuelven de forma diferida. El servidor arranca y responde a initialize y tools/list sin nada configurado, que es lo que permite que un directorio o un sandbox lo inspeccionen; es en la primera llamada a una herramienta donde una credencial ausente se convierte en un error accionable.

Conexión a un cliente MCP

Añádelo a la configuración de tu host MCP (por ejemplo, el claude_desktop_config.json de Claude Desktop):

{
  "mcpServers": {
    "hubspot": {
      "command": "uvx",
      "args": ["mcp-hubspot"],
      "env": { "HUBSPOT_PRIVATE_APP_TOKEN": "pat-na1-..." }
    }
  }
}

Docker

docker build -t mcp-hubspot .
docker run --rm -i -e HUBSPOT_PRIVATE_APP_TOKEN=pat-na1-... mcp-hubspot

Desde el código fuente

Requiere Python 3.12+ y uv.

uv venv
uv pip install -e ".[dev]"

cp .env.example .env   # then fill in your HubSpot credentials
uv run mcp-hubspot

URL de autorización OAuth

Para el flujo OAuth, mcp_crm.auth.build_authorization_url(...) construye la URL de consentimiento con los scopes que necesitan las herramientas; intercambia el código devuelto por un token de actualización y establece HUBSPOT_REFRESH_TOKEN.

Invalidación de caché por webhook

El servidor stdio no recibe webhooks. Para invalidar la caché ante cambios realizados fuera de este proceso (ediciones en la interfaz de HubSpot, otras integraciones), monta WebhookProcessor en tu propio punto de entrada HTTP y verifica la firma v3 de HubSpot con verify_signature (usando el HUBSPOT_WEBHOOK_SECRET que configures). Apúntalo al mismo LocalCache que usa tu CrmService:

from mcp_crm.webhooks import WebhookProcessor, verify_signature

processor = WebhookProcessor(cache)

def handle_hubspot_webhook(request):
    ok = verify_signature(
        secret=webhook_signing_secret,
        method="POST",
        uri=request.url,
        body=request.raw_body,
        signature=request.headers["X-HubSpot-Signature-v3"],
        timestamp=request.headers["X-HubSpot-Request-Timestamp"],
    )
    if not ok:
        return 401
    processor.process(request.json())
    return 200

verify_signature rechaza cuerpos manipulados, secretos incorrectos y marcas de tiempo obsoletas; WebhookProcessor.process asigna cada suscripción al objeto que invalida e informa de lo que tocó.

Decisiones de diseño

  • Alcance de la caché. Solo las lecturas de detalle de objetos (crm_get_contact, crm_get_deal) y la lista de pipelines se almacenan en caché; los resultados de listas/búsquedas dependen de la consulta y se dejan sin caché para evitar servir conjuntos de resultados obsoletos. Las escrituras invalidan el objeto correspondiente de inmediato. Para cambios fuera de banda, un WebhookProcessor verificado por firma se distribuye como un componente que montas en tu propio punto de entrada HTTP (consulta Invalidación de caché por webhook); el propio servidor stdio no escucha webhooks.
  • La idempotencia está en el cliente. Los endpoints de creación de HubSpot no son nativamente idempotentes, así que se almacena y se reproduce una clave (suministrada o derivada de la carga útil). Esto hace que los reintentos de herramientas al menos una vez sean seguros sin duplicar registros. El almacén vive en el proceso, por lo que cubre reintentos dentro de una sesión y no entre reinicios, y crm_batch_create_contacts deliberadamente no lo usa; ambos hechos se indican en las descripciones de las herramientas.
  • La solicitud de scope ocurre dos veces. El servicio precomprueba los scopes concedidos (mediante introspección de token) para obtener un error rápido y accionable, y el cliente HTTP también asigna un MISSING_SCOPES 403 del lado del servidor al mismo error tipado (cinturón y tirantes).
  • Las credenciales se cargan de forma diferida. Nada lee un token en la importación ni en el arranque. Una credencial ausente aparece como un error tipado en la primera llamada a una herramienta, y ese fallo se audita como cualquier otro, así que el servidor sigue siendo inspeccionable en un sandbox con un entorno vacío.
  • El retroceso exponencial y los relojes son inyectables. La espera de reintento, el jitter aleatorio (RNG) y las fuentes de tiempo son parámetros del constructor, que es por lo que toda la suite se ejecuta sin conexión en menos de un segundo.

Pruebas

Cada llamada externa a HubSpot la atiende un simulacro en memoria (tests/fake_hubspot.py) respaldado por fixtures JSON (tests/fixtures/), conectado mediante httpx.MockTransport. Sin red, sin credenciales, determinista.

uv run pytest -q

Regenera el documento comparativo (CI también comprueba que se mantenga sincronizado):

uv run python scripts/generate_comparison.py

Metadatos del registro

server.json describe este paquete para el registro del Model Context Protocol (io.github.amin-ale/hubspot-crm-mcp, PyPI mcp-hubspot, transporte stdio). .mcp.json es la configuración mínima de cliente para herramientas que detectan automáticamente servidores MCP desde la raíz de un repositorio.

Alcance y seguridad

Esto es un cliente para datos de HubSpot que posees o para los que estás autorizado a acceder. Apúntalo únicamente a cuentas de HubSpot que controles o sobre las que tengas permiso por escrito para operar. El registro de auditoría redacta correos electrónicos y números de teléfono antes de escribir los registros; trata los registros de auditoría exportados como sensibles en cualquier caso. El comportamiento documentado aquí es puntual con respecto a los fixtures incluidos, no una garantía sobre ninguna cuenta de HubSpot en vivo.

Contrátame

Construyo servidores MCP e integraciones de API que sobreviven a una revisión de código de un desarrollador senior: autenticación, reintentos, idempotencia y trazas de auditoría incluidas desde el principio, no añadidas después. Portafolio y contacto: https://amin-ale.github.io/portfolio-site · amin.ale.business@gmail.com.