LQABR HubSpot MCP Server
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 ...Моно-репозиторий по-прежнему нуждается в своей копии. Не удаляйте её.
Модуль | Также используется |
| 8 файлов в lead_profile/src, 2 в text_voice/src, |
| 12 файлов lead_profile, включая |
| lead_profile |
Следствие: теперь существует два пути записи в 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 — они взаимоисключающи:
fastmcp3.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 HubSpotpytest.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 на него.
Предоставленные инструменты
Инструмент | Направление | Примечания |
| запись | Upsert компании → Upsert контакта → ассоциация. Идемпотентно. |
| чтение | поля контракта + |
Дедупликация: Контакт по 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 против работающего сервера, выход 0test_server.pyпод pytest — 3 пройдено с сервером, 3 пропущено безживой
call_toolдостиг реальной цепочки: транспорт → инструмент →crm.py→auth.py→secrets.py, отказав только на преднамеренных защитахAuthConfigError/SecretConfigError(нет конфигурации GCP в тестовом окружении). Проводка доказана от начала до конца.
Не проверено: образ Docker никогда не собирался — не было доступно демона Docker в месте, где были собраны эти файлы. Ничто не касалось реального HubSpot; каждый тест использует имитацию.
This server cannot be installed
Maintenance
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
- Alicense-qualityDmaintenanceEnables 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.3MIT
- AlicenseAqualityBmaintenanceEnables interaction with HubSpot CRM through MCP, providing tools to manage contacts, companies, deals, and search/associations via natural language.18182MIT
- Flicense-qualityDmaintenanceImplements Universal Commerce Protocol (UCP) primitives backed by HubSpot CRM, enabling buyer profile, product catalog, cart, and order operations via MCP tools.
- AlicenseAqualityBmaintenanceEnables AI agents to safely operate HubSpot CRM contacts, deals, and pipelines via MCP, with caching, idempotency, audit trails, and robust error handling.15MIT
Related MCP Connectors
LeadConnector / GoHighLevel MCP Pack — wraps the GoHighLevel CRM for AI agents.
Operator-as-agent MCP hub. 6 tools. First $5 free, then $0.001/call.
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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