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
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.
| Herramienta | Qué hace | Scope |
|---|---|---|
crm_search_contacts | Busca contactos por término de texto libre, una página a la vez | crm.objects.contacts.read |
crm_list_contacts | Lista contactos en orden de id con paginación por cursor | crm.objects.contacts.read |
crm_get_contact | Obtiene un contacto por id, servido desde la caché local | crm.objects.contacts.read |
crm_create_contact | Crea un contacto, idempotente sobre una clave suministrada o derivada | crm.objects.contacts.write |
crm_update_contact | Sobrescribe las propiedades pasadas e invalida la entrada de caché | crm.objects.contacts.write |
crm_delete_contact | Archiva (borrado lógico) un contacto | crm.objects.contacts.write |
crm_batch_create_contacts | Crea hasta 100 contactos e informa de los fallos por fila | crm.objects.contacts.write |
crm_search_deals | Busca deals por término de texto libre con paginación por cursor | crm.objects.deals.read |
crm_get_deal | Obtiene un deal por id, servido desde la caché local | crm.objects.deals.read |
crm_create_deal | Crea un deal, idempotente sobre una clave suministrada o derivada | crm.objects.deals.write |
crm_update_deal | Actualiza propiedades de un deal, incluido moverlo a otra etapa | crm.objects.deals.write |
crm_delete_deal | Archiva (borrado lógico) un deal | crm.objects.deals.write |
crm_list_pipelines | Lista los pipelines de deals con sus etapas en orden de visualización (en caché) | crm.schemas.deals.read |
crm_get_pipeline | Obtiene un pipeline con sus etapas ordenadas | crm.schemas.deals.read |
crm_export_audit_log | Exporta la traza de auditoría de la sesión como JSON Lines | ninguna |
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, unWebhookProcessorverificado 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_contactsdeliberadamente 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_SCOPES403del 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.