Agent Academy

Agent Academy is a free, source-linked library of skills, prompts, MCP servers and bot templates, organised into courses by profession, for people who hand real work to AI agents: 1,467 live resources, each with its original link, license and safety tags.

MCP server

HubSpot

HubSpot API: 1,000+ endpoints as safety-categorized, hub- and plan-aware MCP tools, stdio or HTTP.

Needs credentialsDoes not send

https://github.com/ohneben/Hubspot-MCP

Needs credentials

MCP_TRANSPORT, HUBSPOT_ACCESS_TOKEN, HUBSPOT_BASE_URL, HUBSPOT_TOOL_MODE, HUBSPOT_READ_ONLY, HUBSPOT_CAPABILITY_CHECK, MCP_AUTH_TOKEN

Install
docker run -i --rm ghcr.io/ohneben/hubspot-mcp:2.0.1

Original content

Full originalMITSource

Republished in full under its license, with attribution. The source is authoritative.

ohneben's HubSpot MCP


License & checks

MCP registries

The most complete HubSpot MCP server there is. Run your entire

HubSpot account in plain language from Claude,

Cursor, or any other MCP client.

This Model Context Protocol server exposes the

**whole public HubSpot API — all 1,076 endpoints across 102 APIs, in 687

tools** — CRM, CMS, Marketing, Automation, Conversations, Commerce, Files,

Settings, Webhooks and more, generated straight from HubSpot's own OpenAPI

definitions. Endpoints HubSpot repeats per object type are one tool each

(crm_objects_search with objectType: "deals" instead of 32 separate search

tools), and every tool description says when to use it. Every tool is

safety-categorized (🟢 read-only / 🟡 write / 🔴 destructive) and — unique to

this server — hub & plan aware: HubSpot publishes which hub and tier every

API needs (Free / Starter / Professional / Enterprise), and this server carries

that straight into each tool plus a live capability report for *your*

portal. It runs over stdio (Claude Desktop and other local launchers) or

Streamable HTTP (hosted in Docker), and ships with retries, client-side rate

limiting tuned to HubSpot's burst caps, and request timeouts so it holds up

against a live account.

Why you'll want this

Some MCP servers just forward a slice of the API. This one is built to be **safe

to hand to an LLM, complete, and easy to run for real**:

What you getWhy it matters
All 1,076 endpoints — the whole public APIContacts, companies, deals, tickets, every engagement type, associations v4, properties, pipelines, lists, imports/exports, marketing emails & events, campaigns, forms, transactional email, sequences, workflows/actions, conversations & custom channels, CMS pages/posts/HubDB/source code, files, commerce (invoices, orders, carts, payments, subscriptions), settings, webhooks — nothing hand-picked or left behind. Most servers stop at ~30 CRM tools.
687 tools, not 1,076 look-alikesHubSpot repeats the same endpoints for 32 CRM object types, for landing and site pages, and for blog posts, authors and tags. Those are one tool each with a selector argument (objectType, pageType, blogResource), so crm_objects_search covers contacts, deals, tickets and custom objects. Every call still goes to exactly the endpoint it went to before, with that endpoint's scopes and plan hints.
Descriptions that say when to use a toolEach description opens with what the tool does, then when to prefer a sibling (crm_objects_list points to crm_objects_search for filtering and crm_objects_batch_read for known IDs), what it changes, plan and scopes, and the endpoint it calls. Undocumented HubSpot parameters get a description.
Hub & plan awareness *(nobody else has this)*HubSpot gates APIs by hub and tier — HubDB needs Content/Marketing Hub Professional, custom-object schemas need Enterprise, sequences need Sales/Service Professional. Every tool states its requirement, straight from HubSpot's own API index.
Access check at startupWhen the server starts it reads the token's scopes and probes every paid-tier or beta API group with one cheap read. The model gets the result in the server instructions, search results say per tool whether this token can use it, and hubspot_get_capabilities explains each status with the scopes to add. No more walls of mystery 403s.
Curated safety categories 🟢 / 🟡 / 🔴Not naive "GET = safe": a POST …/search is a read-only query, merge is flagged irreversible, gdpr-delete is a permanent purge (vs. archive → recycle bin), list-membership calls are reversible links, POST /crm/v3/imports is a bulk import, and transactional email is sends messages.
Machine-readable MCP annotations (readOnlyHint, destructiveHint)Hosts that honor annotations (Claude included) can auto-trust reads and demand confirmation before anything destructive.
Actionable error hints403 with MISSING_SCOPES → the exact scopes to add and where; plain 403 on a gated API → the plan tier it needs; 401 → token type & expiry guidance; 429 → your limits. The model gets *how to fix it*, not just *what broke*.
Read-only mode & group filteringExpose only the 302 🟢 read-only tools (HUBSPOT_READ_ONLY=true), or narrow to the groups you use (HUBSPOT_INCLUDE_GROUPS=contacts,deals,cms:*). Area wildcards included.
Discovery mode by defaultOut of the box the model sees 6 tools (~2k tokens): search, inspect and invoke over all 687 endpoint tools, plus the capability, GraphQL and raw-request tools. HUBSPOT_TOOL_MODE=all exposes every endpoint tool directly. Read-only mode applies either way.
Real file uploadsThe multipart endpoints (Files, CRM imports, HubDB import, CMS source code) actually work — pass file content inline or as base64. Most generated servers can't do multipart at all.
Automatic retries with backoffTransient 429 / 5xx responses are retried with jittered exponential backoff, honoring HubSpot's Retry-After header.
Built-in rate limitingSelf-throttles under HubSpot's burst caps (default 100 req / 10 s) with a separate limiter for the /search endpoints (~5 req/s cap). A burst of tool calls won't trip a 429.
Response-size guardOptionally cap huge list responses (HUBSPOT_MAX_RESPONSE_CHARS) so one call can't blow the model's context window.
CRM GraphQL passthroughHubSpot's GraphQL endpoint is query-only, so it's a 🟢 tool here — fetch a contact, its company and that company's deals in one round-trip.
Raw-request escape hatchhubspot_api_request reaches brand-new or beta endpoints the moment HubSpot ships them — auth, throttling and retries still handled server-side.
**Two transports: stdio *and* Streamable HTTP**Use it locally in Claude Desktop, or run one always-on server that any number of MCP clients reach over HTTP.
Docker + docker-compose, health check, auto-restartProduction-style deployment out of the box: docker compose up and it stays up.
Optional bearer-token auth on the HTTP endpointPut the server behind a shared secret the moment it's reachable beyond localhost.
Your token never reaches the modelThe access token lives in the server's environment and is injected on every request — the assistant only ever sees tool inputs and API responses.
Drop-in spec updatesnpm run fetch-specs pulls HubSpot's latest OpenAPI definitions (and their hub/tier metadata) from HubSpot's public index — new endpoints become new tools on rebuild, no code changes.

How it compares

There are a few ways to reach HubSpot from an AI assistant today. Here's how this

server stacks up against the alternatives:

This serverOfficial HubSpot MCPshinzo-labs hubspot-mcpmcp-hubspot (buryhuang)CData MCP
Approx. tools~690 (all 1,076 endpoints)~7 curated (remote)100+~73 (generic SQL)
Whole public API (CRM and CMS · Marketing · Automation · Commerce · Files · Settings · Webhooks)✅➖ CRM + some content reads➖ CRM-centric❌❌
Hub & plan-tier awareness per tool✅❌❌❌❌
Account capability report (scopes · usage · unlocks)✅❌❌❌❌
Reads and writes✅✅✅➖ partial❌ read-only
Curated 🟢 / 🟡 / 🔴 safety categories✅➖➖➖n/a
readOnlyHint / destructiveHint annotations✅➖➖➖➖
Read-only mode + group filtering✅❌❌❌always read-only
File uploads (multipart)✅❌❌❌❌
GraphQL passthrough✅❌❌❌❌
Rate limiting + auto-retry (429/5xx)✅➖➖❌❌
stdio transport✅✅ local package✅✅✅
Streamable-HTTP transport✅✅ hosted remote✅❌❌
Docker + compose + health check✅❌➖ Dockerfile➖ image only❌
Self-hosted on your own infra✅➖ vendor cloud✅✅✅
LanguageTypeScriptTypeScriptTypeScriptPythonJava
LicenseMIT—MITMITMIT

✅ = yes · ➖ = partial / not documented · ❌ = no. Compiled from each

project's public documentation; this is an unofficial project, not affiliated

with HubSpot or the projects listed. Tool counts are approximate and move as

APIs evolve.

The short version: the official remote server is a great managed on-ramp

but covers a handful of curated tools. The community servers are solid but

CRM-centric, without plan awareness or safety guardrails. This one gives you

the entire public API, hub/plan awareness no one else has, **both

transports, a production Docker deployment, and curated safety

categories** — self-hosted, on your token, MIT.

What you can do

Once it's connected, ask your assistant things like:

  • "Find jane@example.com, show her company, open deals and the last emails we exchanged."
  • "Create a $12,000 deal 'Acme expansion' in the Sales pipeline, stage Qualified, associated with Acme Corp."
  • "Search contacts created this month with lifecycle stage MQL and add them to the 'Q3 nurture' list."
  • "Which HubDB tables exist? Add a row to 'pricing' and publish the table."
  • "Clone last month's newsletter as a draft — don't publish."
  • "Upload this CSV and start a contact import mapped to email + first name."
  • "What can my token do? Check my API usage and which scopes are missing for HubDB." *(→ hubspot_get_capabilities)*
  • "Merge these two duplicate contacts — after showing me both records first."

Tools are generated automatically from HubSpot's specs and grouped into

🟢 read-only, 🟡 write and 🔴 destructive — so a well-behaved host can treat each

group differently.

How it works

Claude / Cursor / any MCP client  ──MCP──►  this server  ──HTTPS──►  api.hubapi.com (your portal)

The server parses 104 bundled OpenAPI definitions (fetched from HubSpot's public

API index, which also publishes per-API hub/tier requirements) into MCP

tools — resolving $refs, guarding against recursive schemas, merging

endpoints that only differ by object type into one tool, deriving clean names

like crm_objects_search and hubdb_tables_create_table, and tagging each

tool with a curated safety category, its plan requirement and its OAuth scopes.

Your access token is injected server-side on every request; the model never

sees or handles it.

Hub & plan awareness

HubSpot isn't one API — what you can call depends on which hubs (Marketing,

Sales, Service, Content, Commerce, Operations) and which tier (Free,

Starter, Professional, Enterprise) the portal has, plus the scopes granted

to your token. This server is built around that reality:

  1. Every tool description carries the requirement, from HubSpot's own index:

```

🟢 READ-ONLY · Get all published tables.

Use to page through tables: pass limit, then the after cursor from paging.next.after for the next page.

…

Plan: Professional tier of Marketing Hub / Content Hub. Scopes: hubdb.

Endpoint: GET /cms/v3/hubdb/tables (Hubdb API, CMS)

```

Consolidated tools state it per value: crm_objects_list lists which

object types need a paid tier in its objectType parameter.

  1. An access check runs when the server starts. HubSpot has no API that

returns a portal's subscription, so the server combines what it can

observe: the token's granted scopes, and one cheap read per paid-tier or

beta group (up to about 30 API calls; plan gates only surface as 403s).

Every group gets a status: available, missing_scopes, blocked (plan

tier, user permission, or a 401 from an API that does not accept this kind

of token) or unverified. HubSpot's error details count: a 403 that names

scopes the token lacks is a missing scope, while MISSING_SCOPES for an

object whose scopes the token already holds is how HubSpot answers a plan

gate, so that is reported as blocked. A read

that succeeds proves read access only; a paid-tier API can still refuse

writes, and the report says so. The model receives a summary in

the server instructions, hubspot_search_endpoints marks each result (and

takes usable_only: true), and hubspot_get_capabilities returns the full

report with reasons, "unlockedByScopes": "13/16" counts and the scopes to

add; refresh: true runs it again. Turn it off with

HUBSPOT_CAPABILITY_CHECK=false.

  1. 403s come back with the fix: missing scope → the exact scope name and

where to grant it; plan-gated API → the tier HubSpot requires.

Requirements

  • A HubSpot account and a service key (or an existing legacy private

app token); see [Get your API credentials](#get-your-api-credentials).

  • Docker (Docker Desktop on macOS/Windows) for the quick start below — or

Node.js ≥ 18 to [run from source](#run-from-source-stdio-no-docker).

Quick start (Docker)

1. Add your credentials. Copy the example config and fill it in:

cp .env.example .env
# edit .env → set HUBSPOT_ACCESS_TOKEN
#           → set MCP_AUTH_TOKEN. REQUIRED unless HOST is a loopback address,
#             otherwise the server refuses to start: MCP_AUTH_TOKEN=$(openssl rand -hex 32)

2. Start the server:

docker compose up -d --build

The bundled docker-compose.yml binds to 127.0.0.1:8765 only, so the server is

reachable from your machine but not the network.

3. Confirm it's running:

curl -s http://localhost:8765/health
# → {"status":"ok","server":"hubspot-mcp"}

4. Connect your MCP client. The MCP endpoint is http://localhost:8765/mcp.

  • Claude Desktop — add a custom connector (Settings → Connectors) pointing

at the URL, or bridge it locally with

mcp-remote. Add this under

mcpServers in your config, then fully quit and reopen the app:

```json

{

"mcpServers": {

"hubspot": {

"command": "npx",

"args": [

"mcp-remote",

"http://localhost:8765/mcp",

"--header", "Authorization: Bearer YOUR_MCP_AUTH_TOKEN"

]

}

}

}

```

(Drop the --header line only if the server runs without a token, which it

allows on a loopback bind alone.)

  • Claude Code — one command:

```bash

claude mcp add --transport http hubspot http://localhost:8765/mcp

```

  • Claude Cowork — shares Claude Code's MCP config, so the command above makes

the tools available there too.

> Tip: by default the model sees 6 tools and reaches every endpoint through

> search → inspect → invoke. For one tool per endpoint set

> HUBSPOT_TOOL_MODE=all (690 tools) and trim it with HUBSPOT_INCLUDE_GROUPS

> (e.g. contacts,companies,deals,tickets,lists) — see

> [Context footprint](#context-footprint) for measured numbers per

> configuration.

Prefer a prebuilt image?

Every push to main publishes a ready-to-run image to the GitHub Container

Registry, so you can skip the local build entirely:

docker run -d --name hubspot-mcp -p 127.0.0.1:8765:8765 --env-file .env \
  ghcr.io/ohneben/hubspot-mcp:latest

Get your API credentials

The recommended way is a service key, HubSpot's replacement for legacy

private apps:

  1. In HubSpot, open Settings → Integrations → Service Keys (or

Development → Keys → Service Keys) and create a key. You need super

admin rights or the *Developer tools access* permission.

  1. Select the scopes you want the assistant to reach. Scopes map 1:1 to tool

groups: grant read scopes (crm.objects.contacts.read, …) for a reporting

setup, and add write scopes only where you want changes.

  1. Copy the key (pat-…) → HUBSPOT_ACCESS_TOKEN in .env. It is sent as a

bearer token exactly like a private app token, so nothing else changes.

Notes:

  • Service keys are in public beta and differ from private apps in three

ways that matter here: they do not support GraphQL (set

HUBSPOT_ENABLE_GRAPHQL=false), rotation keeps the old key valid for a

7-day grace period, and the scope and usage figures in the capability report

may be incomplete for them.

  • Existing private app tokens keep working. HubSpot stops the creation of

new legacy private apps (existing portals from 26 October 2026, new portals

from 28 September 2026); apps already created are not affected. A private

app is still the only option here if you rely on the GraphQL tool.

  • OAuth access tokens work too (for apps you've built) — but they expire

after ~30 minutes and this server does not refresh them; a service key or

private app token is the right fit for a long-running server.

  • The token determines the portal — no portal ID needed.
  • EU data residency: if your portal lives in HubSpot's EU data center, set

HUBSPOT_BASE_URL=https://api-eu1.hubapi.com.

Configuration

Everything is set in .env (copied from .env.example):

VariableRequiredDefaultDescription
HUBSPOT_ACCESS_TOKEN✅—Service key or legacy private app token (both pat-…), or an OAuth access token. Without it the server still starts and lists its tools (for registries and inspectors), but every call returns setup instructions
HUBSPOT_BASE_URL—https://api.hubapi.comUse https://api-eu1.hubapi.com for EU data residency
MCP_TRANSPORT—stdiostdio or http (the Docker image defaults to http)
PORT—8765HTTP listen port
HOST—0.0.0.0HTTP bind address
MCP_HTTP_PATH—/mcpHTTP MCP route
MCP_AUTH_TOKEN⚠️_(off)_Require Authorization: Bearer <token> on /mcp. Required when HOST is not a loopback address, otherwise the server refuses to start. Renamed from MCP_SHARED_TOKEN in 1.1.0
MCP_ALLOWED_HOSTS—_(derived)_Comma-separated hostnames the Host header may carry. Defaults to the loopback names on a loopback bind, and to no check behind a reverse proxy
MCP_ALLOW_INSECURE—0Start without a token on a non-loopback bind. Only for a port that genuinely is not reachable by anyone else
MCP_BODY_LIMIT—25mbLargest accepted request body
MCP_SESSION_TTL—1800Seconds an idle session is kept before it is swept
MCP_MAX_SESSIONS—256Concurrent sessions before the least recently used one is evicted
HUBSPOT_MAX_REQUESTS—100Client-side requests per window (0 disables throttling)
HUBSPOT_RATE_WINDOW_MS—10000Rate-limit window in ms (default: 100 req / 10 s)
HUBSPOT_SEARCH_MAX_REQUESTS—4Extra throttle for /search endpoints (0 disables)
HUBSPOT_SEARCH_RATE_WINDOW_MS—1000Search throttle window (default: 4 req / 1 s)
HUBSPOT_MAX_RETRIES—3Retries on 429 / 5xx / network errors
HUBSPOT_TIMEOUT_MS—30000Per-attempt request timeout
HUBSPOT_READ_ONLY—falseExpose only 🟢 read-only tools
HUBSPOT_INCLUDE_GROUPS—_(all)_Only expose these groups — keys like contacts,deals and area wildcards like cms:*
HUBSPOT_EXCLUDE_GROUPS—_(none)_Hide these groups (same syntax)
HUBSPOT_INCLUDE_BETA—trueInclude beta / developer-preview APIs
HUBSPOT_TOOL_MODE—discoverydiscovery (3 meta-tools over the whole catalog) or all (one tool per endpoint or endpoint family)
HUBSPOT_CAPABILITY_CHECK—trueRead the token's scopes and probe paid-tier or beta groups at startup (up to about 30 API calls)
HUBSPOT_ENABLE_GRAPHQL—trueExpose the CRM GraphQL query tool
HUBSPOT_GRAPHQL_URL—_(derived)_Override the GraphQL endpoint
HUBSPOT_ENABLE_RAW_REQUEST—trueExpose the raw-request escape hatch (auto-hidden in read-only mode)
HUBSPOT_MAX_RESPONSE_CHARS—0Truncate responses longer than N chars (0 = never)
HUBSPOT_SPEC_DIR—_(bundled)_Load OpenAPI files + catalog.json from a different directory

After changing .env, reload with docker compose up -d --force-recreate.

Run npm run list-tools (no credentials needed) to print the full catalog, the

per-category counts, and every group key you can filter on — add --names to

list all 687 tool names with the endpoint each one calls.

Tool safety categories

Each tool's description starts with one of these banners and carries the matching

MCP annotations:

BannerToolsEndpointsreadOnlyHintdestructiveHintMeaning
🟢 READ-ONLY274348truefalseGET — fetches data only. Safe.
🟢 READ-ONLY · query2895truefalseA POST that *searches/reads* (object search, batch read, export start, token introspection) — changes no records.
🟡 WRITE · creates data133198falsefalseCreates records (not idempotent; may duplicate).
🟡 WRITE · creates or updates736falsefalseIdempotent upserts (batch upsert, marketing-event upsert).
🟡 WRITE · updates data131215falsefalseModifies records/settings in place, including publishing, scheduling and restoring existing content.
🟡 WRITE · links records1819falsefalseAssociates records (list memberships, v4 associations). Reversible.
🟡 WRITE · unlinks records1112falsefalseRemoves associations. Reversible — records survive.
🟡 WRITE · sends messages44falsefalseMarketing/transactional email, sequence enrollment, conversation replies.
🟡 WRITE · bulk import11falsefalsePOST /crm/v3/imports — can create/update thousands of records.
🔴 DESTRUCTIVE · deletes data6496falsetrueDeletes/archives a record (CRM archives are restorable ~90 days).
🔴 DESTRUCTIVE · bulk delete1042falsetrueBatch archive — many records in one call.
🔴 DESTRUCTIVE · merges records37falsetrueHubSpot cannot un-merge. Confirm both IDs first.
🔴 DESTRUCTIVE · permanent GDPR purge33falsetrueSkips the recycle bin; gone forever.

That's 302 read-only · 305 write · 80 destructive = 687 endpoint tools

(443 · 485 · 148 = 1,076 endpoints), plus the three power tools below. Hosts

that respect annotations (Claude included) can require confirmation for

destructiveHint tools and trust readOnlyHint tools automatically. Prefer to

lock it down further? Set HUBSPOT_READ_ONLY=true to expose *only* the 302

read-only tools (plus the 🟢 capability and GraphQL tools).

Coverage by area (APIs / endpoints by 🟢 read / 🟡 write / 🔴 destructive / tools)

AreaAPIs🟢 Read🟡 Write🔴 DeleteEndpointsTools
CRM5821624592553211
CMS137412322219172
Marketing74246119999
Conversations3181133232
Automation3171043131
Webhooks Journal120332626
Settings314912424
Files110742121
Communication Preferences16801414
Events34721313
Webhooks134299
Data Studio116188
Auth (OAuth)132277
Account250055
Commerce122155
Scheduler132055
Meta140044
Business Units110011
Total1024434851481,076687

Context footprint

What does the tool list cost in model context? Measured on the bundled specs

(tools/list JSON payload; tokens ≈ chars ÷ 3.6):

ConfigurationToolsPayload≈ Tokens
Discovery mode (default)60.01 MB~2k
HUBSPOT_TOOL_MODE=all6901.33 MB~370k
all + read-only3040.38 MB~104k
all + CRM core preset¹850.13 MB~36k
all + CRM core preset¹ + read-only330.04 MB~12k

In discovery mode each hubspot_get_endpoint lookup adds only the schema the

model asked for: about 350 tokens for a typical endpoint, up to ~12k for the

largest CMS page bodies.

¹ HUBSPOT_INCLUDE_GROUPS=contacts,companies,deals,tickets,lists,properties,associations,pipelines,crm-owners,notes,tasks,calls,emails,meetings

How to read that:

  • Discovery mode loses no coverage: all 1,076 endpoints stay callable

through search → inspect → invoke, at the cost of one or two extra calls the

first time the model uses an endpoint.

  • Clients with tool search / deferred loading (Claude Code, claude.ai

connectors) load tool definitions on demand, so HUBSPOT_TOOL_MODE=all works

well there; narrow it with a HUBSPOT_INCLUDE_GROUPS preset and/or

HUBSPOT_READ_ONLY=true.

  • Responses consume context too. Cap outliers with

HUBSPOT_MAX_RESPONSE_CHARS (e.g. 40000) and request only the

properties you need on CRM reads.

  • Oversized inline schemas are already handled: the four pathological

recursive schemas (list filters, workflow definitions — ~1.7 MB *each*

fully inlined) are budget-pruned to ≤24 KB with their top levels intact.

Input schemas also drop examples and any prose below the body's own fields;

every field, type, enum and required list stays.

The power tools

Besides the generated endpoint tools, the server ships four hand-built ones:

  • hubspot_get_capabilities 🟢 — the access report described

[above](#hub--plan-awareness). Returns the startup check without new API

calls; refresh: true runs it again.

  • hubspot_graphql_query 🟢 — HubSpot's CRM GraphQL API

(POST /collector/graphql). Query-only by design (HubSpot exposes no

mutations), so it stays available even in read-only mode. Requires the

collector.graphql_query.execute scope and Marketing/Content Hub Pro+.

  • hubspot_api_request 🔴 — raw escape hatch for any path on the HubSpot

host (new betas, undocumented corners). Same auth injection, throttling and

retries. Hidden in read-only mode.

  • Discovery mode (the default) — replaces the 687

endpoint tools with hubspot_search_endpoints →

hubspot_get_endpoint → hubspot_invoke_endpoint over the same registry.

All filters (groups, beta, read-only) still apply; in read-only mode the

invoke tool physically cannot reach a write because writes aren't in the

registry.

Upgrading from 1.x

2.0 merges endpoints that HubSpot repeats per object type into one tool with a

selector argument. Every endpoint is still reachable and sends the same

request; only tool names and a few argument names change. Update saved

prompts, client allowlists and permission rules that name tools. Group keys

for HUBSPOT_INCLUDE_GROUPS / HUBSPOT_EXCLUDE_GROUPS (contacts, deals,

pages, …) are unchanged.

1.x tools2.0 toolArguments
contacts_list, deals_list, tickets_list, … custom_objects_listcrm_objects_listobjectType: "contacts" (or "deals", "0-3", a custom "2-12345", …)
<type>_get, <type>_update, <type>_archive (some were <type>_delete)crm_objects_get, crm_objects_update, crm_objects_archiveobjectType; the record ID is objectId instead of contactId, dealId, …
<type>_create, <type>_search, <type>_merge, <type>_batch_read / _create / _update / _upsert / _archivecrm_objects_create, crm_objects_search, crm_objects_merge, crm_objects_batch_*objectType
partner_clients_associations_*, partner_services_associations_*crm_objects_associations_list / _create / _deleteobjectType: "partner_clients" or "partner_services", objectId
pages_landing_*, pages_site_*cms_pages_*, e.g. cms_pages_draft_push_livepageType: "landing" or "site"
posts_blogs_*, authors_blogs_*, tags_blogs_* for list, get, archive, batch read/update/archive and multi-languagecms_blog_*, e.g. cms_blog_listblogResource: "posts", "authors" or "tags"

Unchanged: endpoints only one type has (contacts_gdpr_delete, landing page

folders, blog post drafts and revisions, blog create/update, whose bodies differ

per resource). npm run list-tools -- --names prints every tool with the endpoint

it calls. POST endpoints that publish, schedule or restore existing content are

now labelled 🟡 *updates data* instead of *creates data*.

Two defaults change as well: HUBSPOT_TOOL_MODE is now discovery (set it to

all to expose the endpoint tools themselves), and the server runs the access

check at startup, which costs up to about 30 API calls per start

(HUBSPOT_CAPABILITY_CHECK=false turns it off).

Run from source (stdio, no Docker)

Prefer the classic stdio mode for Claude Desktop? Build it locally:

npm install
npm run build

Then point Claude Desktop at the compiled entrypoint in

claude_desktop_config.json:

{
  "mcpServers": {
    "hubspot": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/Hubspot-MCP/dist/index.js"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "HUBSPOT_ACCESS_TOKEN": "pat-na1-…"
      }
    }
  }
}

Keeping the specs current

The bundled files under spec/ are the source of truth for the tools. They are

fetched from HubSpot's own public API index

(https://api.hubspot.com/public/api/spec/v1/specs), which lists every public

API with per-version OpenAPI documents and the hub/tier requirements this

server bakes into tool descriptions. spec/catalog.json records what was

fetched (API, version, stage, beta flag, requirements, docs links).

To refresh against HubSpot's latest:

npm run fetch-specs   # rewrites spec/*.json + spec/catalog.json
npm run build && npm run list-tools

New endpoints become new tools automatically — no code changes. The fetch

script prefers each API's stable classic version (v1/v3/v4) and falls back

to dated or beta versions when that's all HubSpot offers; a couple of APIs are

pinned to bundle multiple genuinely-different versions (OAuth v1 + v3,

Communication Preferences v3 + v4).

Development

npm install
npm run build       # compile TypeScript → dist/
npm test            # run the Vitest suite (160 tests)
npm run list-tools  # print the categorized tool catalog (no credentials needed)
npm run fetch-specs # refresh spec/ from HubSpot's public API index

CI builds and tests every push across Node 20 and 22; pushes to main also

publish a Docker image to the GitHub Container Registry.

Notes & conventions

  • Transports: MCP_TRANSPORT=stdio (default) for local launchers;

MCP_TRANSPORT=http for the always-on Streamable-HTTP server the Docker image

runs.

  • Paging: CRM list tools use cursor paging — pass limit and the after

cursor from paging.next.after. Ask for the properties you need via the

properties parameter (arrays become repeated query params).

  • Selectors: consolidated tools take objectType (crm_objects_*),

pageType (cms_pages_*) or blogResource (cms_blog_*). objectType

accepts names (contacts, line_items), objectTypeIds (0-3) and custom

object types (2-12345).

  • Search: crm_objects_search and the other *_search tools take a JSON

body with filterGroups, sorts, query, properties, limit and

after. HubSpot caps search at ~5 req/s per token — the built-in search

throttle keeps you under it.

  • Batch tools (crm_objects_batch_read, crm_objects_batch_create, …) are

the efficient way to touch many records — prefer them over loops of single

calls.

  • Associations: use the v4 association tools (associations_*) to link

records, with optional labels via the association-schema tools.

  • File uploads: multipart tools accept file fields as

{"fileName": "report.pdf", "contentBase64": "…"} (or content for plain

text); other fields are sent as regular form fields, objects as JSON strings.

  • Deletes are archives for CRM objects (recycle bin, ~90 days) — the truly

permanent ones are the 🔴 gdpr_delete purge tools and record merges.

  • Rate limits: HubSpot enforces burst caps per 10 s (private apps:

100–200/10 s depending on plan; OAuth apps ~110/10 s) plus daily caps. The

server self-throttles at HUBSPOT_MAX_REQUESTS per window and retries any

429 it still receives, honoring Retry-After. Check real usage anytime via

hubspot_get_capabilities.

  • Request bodies: write tools take a body argument; its schema is resolved

from the spec and shown to the model (e.g. crm_objects_create expects

{"properties": {…}}).

  • Beta APIs (developer preview / public beta) are included by default and

labelled ⚠️ in descriptions; hide them with HUBSPOT_INCLUDE_BETA=false.

Security

  • Your access token lives only in .env, which is git-ignored. **Never commit

real secrets.** The token grants whatever its scopes allow — if it leaks,

expire it in Settings → Integrations → Service Keys (or Private Apps

for a legacy token).

  • The HTTP endpoint refuses to start unauthenticated once it is bound beyond

this machine. Set MCP_AUTH_TOKEN (openssl rand -hex 32) and send it as an

Authorization: Bearer <token> header, ideally behind TLS. A loopback bind

still needs no token, and is additionally protected against DNS rebinding by

a Host header check.

  • Destructive tools (delete / merge / GDPR purge) and send tools

(marketing & transactional email, sequences) carry the right annotations so a

well-behaved host prompts before acting — keep that confirmation on, or run

with HUBSPOT_READ_ONLY=true.

  • Scope the blast radius at the source: grant the service key only the scopes

you actually need — the startup access check and hubspot_get_capabilities

tell you what's missing when you want more. The check itself only reads.

See [SECURITY.md](./SECURITY.md) for the full policy and how to report a

vulnerability.

Credits & license

An unofficial community integration for HubSpot;

not affiliated with or endorsed by HubSpot. Built on the

Model Context Protocol. Tools are generated

from HubSpot's public OpenAPI definitions. Licensed under the

[MIT License](./LICENSE.md).

DescriptionHubSpot API: 1,000+ endpoints as safety-categorized, hub- and plan-aware MCP tools, stdio or HTTP.
Version2.0.1
Packagesoci · ghcr.io/ohneben/hubspot-mcp:2.0.1 · stdio
Environment variablesMCP_TRANSPORT required
Server transport. Set to 'stdio' for local MCP clients; the image otherwise starts the Streamable HTTP transport.
HUBSPOT_ACCESS_TOKEN required secret
HubSpot service key or legacy private app token (pat-...), from Settings -> Integrations. An OAuth access token also works.
HUBSPOT_BASE_URL
API host. Default https://api.hubapi.com; use https://api-eu1.hubapi.com for EU data residency portals.
HUBSPOT_TOOL_MODE
'discovery' (default) exposes 3 meta-tools that search, inspect and call all 1,076 endpoints; 'all' exposes the ~690 endpoint tools directly.
HUBSPOT_READ_ONLY
Set to 'true' to expose only read-only tools.
HUBSPOT_CAPABILITY_CHECK
Read the token's scopes and probe paid-tier or beta API groups at startup (read-only, up to about 30 API calls). Set to 'false' to skip.
MCP_AUTH_TOKEN secret
Only for MCP_TRANSPORT=http: bearer token required on /mcp. Without it a non-loopback bind refuses to start.
Websitehttps://github.com/ohneben/Hubspot-MCP#readme

Related

TutorialCustomer service

2_1_support_ticket_agent

A structured output agent demonstrating Pydantic schema-based responses for customer support ticket creation.

Sourcegithub.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_framework_crash_course/openai_sdk_crash_course/2_structured_output_agent/2_1_support_ticket_agent

No credentialsDoes not sendApache-2.0
TutorialCustomer service

3_1_customer_support_ticket_agent

A tutorial demonstrating how to implement a structured customer support ticketing system using Google's ADK (Agent Development Kit) framework. This example shows how to create type-safe, structured support tickets with priority levels, categories, and resolution estimates using Pydantic schemas and

Sourcegithub.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_framework_crash_course/google_adk_crash_course/3_structured_output_agent/3_1_customer_support_ticket_agent

No credentialsDoes not sendApache-2.0
CaseCustomer service

ai_customer_support_agent

This Streamlit app implements an AI-powered customer support agent for synthetic data generated using GPT-4o. The agent uses OpenAI's GPT-4o model and maintains a memory of past interactions using the Mem0 library with Qdrant as the vector store.

Sourcegithub.com/Shubhamsaboo/awesome-llm-apps/tree/main/advanced_ai_agents/single_agent_apps/ai_customer_support_agent

No credentialsDoes not sendApache-2.0
TutorialCustomer service

Using tool_choice=required for customer service (OpenAI cookbook)

Using Tool Required for Customer Service The ChatCompletion endpoint now includes the ability to specify whether a tool must be called every time, by adding tool_choice='required' as a parameter. This adds an element of determinism to how you build your wrapping application, as you can count on a to

Sourcegithub.com/openai/openai-cookbook/blob/main/examples/Using_tool_required_for_customer_service.ipynb

No credentialsDoes not sendMIT
TutorialCustomer service

Customer service agent with tool use (Claude cookbook)

Creating a Customer Service Agent with Client-Side Tools In this recipe, we'll demonstrate how to create a customer service chatbot using Claude 3 plus client-side tools. The chatbot will be able to look up customer information, retrieve order details, and cancel orders on behalf of the customer. We

Sourcegithub.com/anthropics/claude-cookbooks/blob/main/tool_use/customer_service_agent.ipynb

No credentialsDoes not sendMIT
Awesome listCustomer service

500 AI Agents Projects (includes customer service use cases)

Curated collection of AI agent use cases across industries (incl. Customer Service, E-commerce) with links to open-source implementations, e.g. a LangGraph customer-support agent tutorial.

Sourcegithub.com/ashishpatel26/500-AI-Agents-Projects

No credentialsDoes not sendMIT