Skip to main content
Glama
bsmahi

LQABR HubSpot MCP Server

by bsmahi

LQABR_MCP

Сервер LQABR central HubSpot MCP. Построен на FastMCP и является автономным — не зависит от моно-репозитория LQABR.

RUNNING.md — как запускать: настройка, учётные данные, оба транспорта, agentgateway, устранение неполадок. CONSUMING.md — как его вызывают агенты электронной почты, голосовые и планировщики: ADK McpToolset, аутентификация, контракт проводки, область видимости инструментов. Этот файл — обоснование дизайна; те два — процедуры.

LQABR_MCP/
├── hubspot-crm-mcp-server/
│   ├── __init__.py              (empty, per mcp.odt Step 4)
│   ├── hubspot_crm_server.py    the launcher — mcp.odt Step 5
│   ├── test_server.py           remote smoke test — handoff S8
│   ├── hubspot_mcp/             THE IMPLEMENTATION (vendored)
│   │   ├── __init__.py          provenance + drift warning — read this
│   │   ├── server.py            the FastMCP object + the two tools
│   │   ├── secrets.py           Secret Manager access
│   │   ├── obs/                 the four logs: system/process/audit/tokens
│   │   │   ├── __init__.py
│   │   │   ├── context.py       RunContext, run_id, lead_ref_id
│   │   │   └── loggers.py
│   │   └── hubspot/
│   │       ├── __init__.py
│   │       ├── crm.py           upsert_lead_profiles / get_lead_profile
│   │       ├── auth.py          get_hubspot_token(), short-lived M2M
│   │       ├── schema.py        LeadProfile, PushResult, property mapping
│   │       └── failures.py      failure taxonomy + CircuitBreaker
│   └── tests/                   61 tests ported from the mono-repo
├── .vscode/hubspot_mcp.json     stdio config — mcp.odt Steps 7–8
├── Dockerfile                   Cloud Run image — handoff S4
├── .dockerignore
├── pytest.ini
├── .python-version              3.12, matching the Dockerfile
├── .env.example                 mode switches + secret IDs (no values)
├── pyproject.toml
├── RUNNING.md                   step-by-step runbook — start here
├── CONSUMING.md                 client integration guide for other agents
└── README.md

Это форк, а не перенос

hubspot_mcp/копия реализации из моно-репозитория. Переписаны только строки импортов; никакая логика, имена полей или свойства HubSpot не изменились. Проверено с помощью diff — единственные строки, которые различаются во всех девяти файлах:

- from lqabr_core.obs import get_obs, utc_now_iso
+ from ..obs import get_obs, utc_now_iso
- from lqabr_core.leadgen.secrets import ...
+ from ..secrets import ...

Моно-репозиторий по-прежнему нуждается в своей копии. Не удаляйте её.

Модуль

Также используется

lqabr_core.obs

8 файлов в lead_profile/src, 2 в text_voice/src, lqabr_core/crm

lqabr_core.leadgen.*

12 файлов lead_profile, включая call_mcp.py, плюс evals/run_eval.py

lqabr_core.leadgen.secrets

lead_profile model.py, 2 тестовых файла

Следствие: теперь существует два пути записи в HubSpot, несущие одинаковые имена полей и одинаковые имена свойств HubSpot — и контракт данных гласит, что эти имена являются контрактом. Любое исправление в crm.py, любое изменение аутентификации, любое переименование свойства HubSpot должно быть вручную применено в обоих местах. Ничто это не обеспечивает. Закладывайте бюджет на это или планируйте отказаться от одной из сторон.

Related MCP server: hubspot-mcp-server

Почему это находится вне моно-репозитория

В репозитории LQABR есть пакет верхнего уровня с буквальным именем mcp в его корне, который затеняет SDK mcp, от которого зависит FastMCP. Запуск изнутри репозитория ломает собственные импорты FastMCP.

Не добавляйте корень репозитория LQABR в PYTHONPATH.

Библиотека: FastMCP, не официальный SDK

Зависимость — fastmcp>=3.4.7. Не добавляйте mcp>=2.0 — они взаимоисключающи:

  • fastmcp 3.4.7 транзитивно закрепляет mcp<2.0,>=1.24.0 (он устанавливает mcp 1.29).

  • MCPServer, класс сервера официального SDK, существует только в mcp>=2.0.

Таким образом, проект может использовать либо FastMCP, либо MCPServer, но не оба. Этот использует FastMCP. Ничто здесь не импортирует MCPServer.

Два следствия, о которых стоит знать:

  • Имена транспортов различаются. HTTP-транспорт FastMCP — это "http" ("streamable-http" принимается как псевдоним), а аргумент пути конечной точки — path=. Официальный SDK пишет это как streamable_http_path=.

  • Поля типов различаются. Поскольку mcp зафиксирован на версии 1.x, встроенные типы используют camelCase: Tool.inputSchema, а не input_schema.

Моно-репозиторий lqabr_core/leadgen/server.py по-прежнему использует MCPServer. Теперь это второе расхождение между двумя кодовыми базами, помимо форка.

Запуск

uv sync

# stdio — local ADK MCPToolset, or the VSCode config in .vscode/
uv run python hubspot-crm-mcp-server/hubspot_crm_server.py

# HTTP — what Cloud Run runs
uv run python hubspot-crm-mcp-server/hubspot_crm_server.py \
    --transport http --host 0.0.0.0 --port 8080

Учётные данные

tools/list ничего не требует. Вызов инструмента требует токен HubSpot, и он поступает из Secret Manager — контекст §7.6 / CLAUDE.md §5: секреты никогда не жёстко кодируются и никогда не фиксируются.

cp .env.example .env                    # holds mode switches + secret IDs only
gcloud auth application-default login
uv sync --extra gcp --extra test        # both extras; --extra gcp alone drops pytest
export UV_ENV_FILE=.env

uv sync --extra <x> синхронизирует именно этот дополнительный набор, так что --extra gcp само по себе удаляет pytest. test_server.py запускается как CLI без pytest, но uv run pytest, очевидно, требует его.

.env не содержит секретных значений — только HUBSPOT_AUTH_MODE, LQABR_SECRET_PROJECT и ID секрета. Сам токен извлекается через API Secret Manager во время выполнения, хранится в памяти, никогда не регистрируется (строка аудита записывает только длину и последние четыре символа) и кэшируется на 900 секунд, так что ротация не требует повторного развёртывания.

auth.py и secrets.py оба закрываются при ошибке — неустановленное значение означает явную ошибку, а не молчаливое значение по умолчанию.

LQABR_SECRET_BACKEND=env существует как крайнее средство для автономной работы или CI. secrets.py ограничивает его «только локальной разработкой, CI и тестами», и нет автоматического перехода на него — вы должны ввести его явно. Он помещает живые учётные данные в файл на диске. Никогда не устанавливайте его в Cloud Run.

Каждый флаг имеет значение по умолчанию из переменной окружения (MCP_TRANSPORT, MCP_HOST, PORT, MCP_PATH), поэтому контейнер запускается без аргументов — Cloud Run внедряет PORT.

Тесты

uv run pytest              # 61 tests, all passing, none touch real HubSpot

pytest.ini добавляет hubspot-crm-mcp-server/ в путь, чтобы разрешался import hubspot_mcp — сама папка не может быть пакетом из-за дефиса.

Контракт — это ДЕСЯТЬ полей, а не девять

LeadProfile содержит десять: девять, которые все документируют, плюс contact_name (добавлен для отображения firstname/lastname). Моно-репозиторий test_wrapper_shape_is_the_nine_fields_plus_ids по-прежнему утверждал 9 и не проходил в этой части — schema.py байт-идентичен, поэтому этот проект унаследовал его.

Исправлено 2026-08-18: код был правильным, число было устаревшим. Тест переименован в test_wrapper_shape_is_the_contract_fields_plus_ids и теперь утверждает имена полей, а не количество, так что следующее добавление завершится читаемой ошибкой.

Такое же исправление всё ещё требуется в моно-репозитории — это утверждение там не изменилось и всё ещё красное. Документы, которые говорят «9 полей», также должны быть исправлены.

Тестирование развёрнутого сервера

# local
uv run python hubspot-crm-mcp-server/test_server.py

# Cloud Run — mints a Google ID token via ADC
uv run python hubspot-crm-mcp-server/test_server.py \
    --url https://lqabr-mcp-server-xxxx.a.run.app/mcp --auth google

# one real read against HubSpot — writes nothing
... --auth google --employee-id EMP-00042

Только для чтения по дизайну: он никогда не вызывает upsert_lead_profile. Отправляет X-LQABR-Run-Id, чтобы журналы аудита сервера приписывали вызов (исправление B10).

Аудитория токена ID — это базовый URL службы без /mcp. Скрипт удаляет его за вас.

Клиент использует fastmcp.Client, который выполняет рукопожатие инициализации, поэтому в этом файле нет инфраструктуры сеанса. Заголовки передаются через StreamableHttpTransport. Теперь это соответствует библиотеке эталонного образца.

Развёртывание на Cloud Run

Двухэтапная сборка uv, не-root пользователь mcp, PID 1 — это Python, так что SIGTERM очищается корректно. Самодостаточный: нет соседней папки, нет зависимости от git.

docker build -t lqabr-mcp-server .

Зафиксируйте uv.lock и переключите синхронизацию на --frozen перед продакшеном.

Затем S5–S8: создайте mcp-server-sa, дайте secretmanager.secretAccessor на lqabr-hubspot-ccess-token, разверните --no-allow-unauhenticated, дайте трем сервисым агента roles/run.invoker, направьте test_server.py на него.

Предоставленные инструменты

Инструмент

Направление

Примечания

upser_lead_profile

запись

Upsert компании → Upsert контакта → ассоциация. Идемпотентно.

get_lead_profile

чтение

поля контракта + contat_hs_id + company_hs_id.

Дедупликация: Контакт по emloyee_id, Компания по company_id. Email живёт в пользовательском свойстве email_id.

Открытый вопрос. Зарегистрированное имя инструмента — upser_lead_profile (единственное число), но дизайн-документы, хендшоф сессии и инструкции проекта все говорят upser_lead_profiles (множественное число), и лежащая в основе функция — множентвенная. Это контракт провода — уладьте это, пок клиент не подключится.

Учётные данные

Два, никогда не путаются:

  • Google ID token доказывает агент → этот сервер (Cloud Run служба-к-службе).

  • HubSpot M2M token доказывает этот сервер → HubSpot, создаётся за один вызов внутри сервера. Вызывающие его никогда не видят.

Эта служба является единственным держателем учётных данных HubSpot.

Проверено

В чистом окружении venv, содержащем только fastmcp 3.4.7 (который принёс mcp 1.29), requests и pytest — с отсутствующим lqabr_core (подтверждено ModuleNotFoundError) и отсутствующим MCPServer (подтверждено ImportError):

  • ноль импортов lqabr_core где-либо в проекте

  • портированный набор тестов: 61 пройдено, 0 не пройдено

  • stdio — tools/list возвращает оба инструмента, PYTHONPATH полностью удалён

  • --transport http — привязывается к host/port/path, полная сессия fastmcp.Client

  • --transport streamable-http — псевдоним принят, обслуживает ту же конечную точку

  • test_server.py — PASS против работающего сервера, выход 0

  • test_server.py под pytest — 3 пройдено с сервером, 3 пропущено без

  • живой call_tool достиг реальной цепочки: транспорт → инструмент → crm.pyauth.pysecrets.py, отказав только на преднамеренных защитах AuthConfigError / SecretConfigError (нет конфигурации GCP в тестовом окружении). Проводка доказана от начала до конца.

Не проверено: образ Docker никогда не собирался — не было доступно демона Docker в месте, где были собраны эти файлы. Ничто не касалось реального HubSpot; каждый тест использует имитацию.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    Enables comprehensive HubSpot CRM integration through the Model Context Protocol with 15+ tools for managing contacts, companies, and deals. Supports multiple transport protocols (HTTP, SSE, STDIO) with session management and real-time access to CRM data.
    3
    MIT
  • F
    license
    -
    quality
    D
    maintenance
    Implements Universal Commerce Protocol (UCP) primitives backed by HubSpot CRM, enabling buyer profile, product catalog, cart, and order operations via MCP tools.

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bsmahi/LQABR_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server