Skip to main content
Glama
bsmahi

LQABR HubSpot MCP Server

by bsmahi

LQABR_MCP

LQABR 中心 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

lead_profile/src 中的 8 个文件,text_voice/src 中的 2 个文件,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,这与 FastMCP 依赖的 mcp SDK 冲突。在仓库内部运行会破坏 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)。

  • 官方 SDK 的服务器类 MCPServer 仅存在于 mcp>=2.0

因此,一个项目只能使用 FastMCPMCPServer,不能同时使用。本项目使用 FastMCP。此处未导入 MCPServer

有两个值得注意的后果:

  • 传输名称不同。 FastMCP 的 HTTP 传输称为 "http""streamable-http" 可作为别名接受),端点路径参数为 path=。官方 SDK 则写作 streamable_http_path=

  • 类型字段不同。 由于 mcp 被固定为 1.x,捆绑的类型使用驼峰命名: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_MODELQABR_SECRET_PROJECT 和秘密的 ID。令牌本身在运行时通过 Secret Manager API 获取,保存在内存中,从不记录(审计行仅记录长度和最后四个字符),并缓存 900 秒,因此轮换无需重新部署。

auth.pysecrets.py 均采用失败关闭策略 —— 未设置意味着显式错误,而非静默默认值。

LQABR_SECRET_BACKEND=env 作为离线工作或 CI 的最后手段存在。secrets.py 将其限定为“仅限本地开发、CI 和测试”,并且不会自动回退到它 —— 你必须手动输入。它会将实时凭据写入磁盘上的文件。切勿在 Cloud Run 中设置。

每个标志都有环境变量默认值(MCP_TRANSPORTMCP_HOSTPORTMCP_PATH),因此容器启动时无需参数 —— Cloud Run 会注入 PORT

测试

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

pytest.inihubspot-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-access-token,部署 --no-allow-unauthenticated,授予三个代理服务账号 roles/run.invoker,将 test_server.py 指向它。

暴露的工具

工具

方向

备注

upsert_lead_profile

写入

公司 upsert → 联系人 upsert → 关联。幂等。

get_lead_profile

读取

契约字段 + contact_hs_id + company_hs_id

去重:联系人基于 employee_id,公司基于 company_id。电子邮件位于自定义属性 email_id 中。

待办事项。 注册的工具名称为 upsert_lead_profile(单数),但设计文档、会话交接和项目说明均使用 upsert_lead_profiles(复数),且底层函数也是复数。这是通信契约 —— 在任何客户端连接之前解决它。

凭据

两种,从不混淆:

  • Google ID 令牌 证明 代理 → 此服务器(Cloud Run 服务间通信)。

  • HubSpot M2M 令牌 证明 此服务器 → HubSpot,在服务器内部每次调用时生成。调用者永远看不到它。

此服务是 HubSpot 凭据的唯一持有者。

已验证

在一个干净的虚拟环境中,仅包含 fastmcp 3.4.7(它带来了 mcp 1.29)、requestspytest —— 缺少 lqabr_core(确认 ModuleNotFoundError)和 MCPServer(确认 ImportError):

  • 项目中任何地方均无 lqabr_core 导入

  • 移植的测试套件:61 通过,0 失败

  • stdio —— tools/list 返回两个工具,PYTHONPATH 完全剥离

  • --transport http —— 绑定主机/端口/路径,完整的 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