·

What Is HubSpot MCP

Learn what HubSpot MCP is and how it lets your AI agent manage contacts, deals, and tickets.

HubSpot's Model Context Protocol server puts your CRM inside the same context window as your code editor. Instead of tabbing over to the HubSpot web app, filtering a deals board, and copy-pasting notes into a ticket description, you point Claude Code, Cursor, Gemini CLI, or OpenCode at the HubSpot MCP server and ask it directly: "which open deals in the Enterprise pipeline mention SSO as a blocker?" The agent calls the CRM API on your behalf, reads structured objects (contacts, companies, deals, tickets, notes), and returns an answer grounded in your actual account data — not a hallucinated guess.

This matters for a specific and underrated use case: turning customer signal into engineering requirements. Product and engineering teams already have the data they need to prioritize correctly — it just lives in a CRM that developers rarely open. HubSpot MCP closes that gap by letting an AI agent read CRM state as part of a normal development session, right next to the codebase it's about to change.

This module treats HubSpot MCP as a read-heavy research and requirements tool, not a marketing automation replacement. We'll cover the object model, the auth model, what's safe to automate, and — because this is customer data — how to keep PII handling defensible.


Core HubSpot MCP Tools: Contacts, Companies, Deals, Tickets, and Notes

HubSpot's official MCP server (distributed as @hubspot/mcp-server and also available as a HubSpot-hosted remote endpoint) exposes a small set of generic, object-agnostic tools rather than one tool per CRM object type. That's a deliberate design choice: HubSpot's CRM is schema-driven (standard objects like contacts, companies, deals, tickets, and notes, plus custom objects your org may have defined), so the MCP server wraps the same generic /crm/v3/objects/{objectType} API surface instead of hardcoding a tool per object.

The tools you'll actually call, in practice, look like this:

  • hubspot-list-objects — paginated listing of a given object type (contacts, deals, tickets, etc.) with optional property selection.
  • hubspot-search-objects — filtered search using HubSpot's CRM search syntax (equivalent to POST /crm/v3/objects/{objectType}/search), supporting filter groups, operators (EQ, GT, CONTAINS_TOKEN, IN), and sorting.
  • hubspot-batch-read-objects — fetch a known set of object IDs in one call, useful once you've narrowed a search.
  • hubspot-list-associations — walk relationships between objects, e.g. which contacts and notes are associated with a given deal.
  • hubspot-get-schemas / hubspot-list-properties — introspect custom properties defined on an object type, which matters a lot once your HubSpot instance has org-specific fields like churn_risk_score or product_area.
  • hubspot-create-engagement — write a note, task, or logged call. This is the one write-capable tool most teams should gate behind explicit confirmation (see the Tips below).
  • hubspot-get-user-details — resolve the owner/user context of the connected private app.

A realistic search call for tickets tagged with a specific product area looks like this:

{
  "tool": "hubspot-search-objects",
  "arguments": {
    "objectType": "tickets",
    "filterGroups": [
      {
        "filters": [
          { "propertyName": "hs_pipeline_stage", "operator": "EQ", "value": "1" },
          { "propertyName": "subject", "operator": "CONTAINS_TOKEN", "value": "export" }
        ]
      }
    ],
    "properties": ["subject", "content", "hs_ticket_priority", "createdate"],
    "limit": 25
  }
}

Notes deserve a callout because they're the object most useful for requirement extraction and most often overlooked. In HubSpot's data model, a "note" is an engagement (objectType: "notes", internally hs_engagement_type = NOTE) associated to a contact, company, deal, or ticket — sales reps write free-text notes after calls, and support agents log context that never makes it into a ticket field. That's where "customer wants bulk export before renewing" actually lives, three weeks before the churn shows up in a report.

Tips
- Always call hubspot-list-properties for an object type before writing a search filter — custom properties won't match the docs, and guessing property internal names (amount vs deal_amount) wastes a round trip.
- hubspot-search-objects caps results per page (typically 100, sometimes 200 by contract) and paginates via after cursors — don't assume one call returns "all" tickets.
- Treat hubspot-create-engagement as a write operation requiring the same review discipline as a code change — an agent that logs a wrong note against a deal pollutes sales history permanently.


HubSpot MCP Authentication: Private App Tokens and Required Scopes

HubSpot MCP authenticates via a private app access token, not OAuth user login and not the legacy API key (deprecated by HubSpot in November 2022). You create a private app inside the target HubSpot account: Settings → Integrations → Private Apps → Create a private app. That token is a bearer credential scoped to exactly the CRM objects and permission levels you grant — read-only, in almost every case you'd want for this module.

Minimum scope set for the read-heavy workflows in this course:

crm.objects.contacts.read
crm.objects.companies.read
crm.objects.deals.read
crm.objects.owners.read
tickets
crm.objects.notes.read
crm.schemas.deals.read
crm.schemas.tickets.read

tickets is HubSpot's legacy scope name that still gates ticket object read/write in the API and in most MCP integrations as of the 2025 CRM scope model — don't be surprised it doesn't follow the crm.objects.tickets.read naming pattern used elsewhere. If you also want the agent to log notes back into HubSpot (e.g., "record that engineering triaged this as a P2"), add:

crm.objects.notes.write

Configure the running MCP server with the token as an environment variable, never as a CLI argument (arguments land in shell history and process lists):

export HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

A typical client config (Claude Code, Cursor, and OpenCode all use variations of this shape) references the token by env var rather than inlining it:

{
  "mcpServers": {
    "hubspot": {
      "command": "npx",
      "args": ["-y", "@hubspot/mcp-server"],
      "env": {
        "HUBSPOT_ACCESS_TOKEN": "${HUBSPOT_ACCESS_TOKEN}"
      }
    }
  }
}

Two auth details that trip people up in practice: private app tokens are tied to a single HubSpot account (portal), so a multi-brand org running separate portals needs one MCP server config per portal, not one token shared across them. And scopes are enforced server-side per API call — an under-scoped token doesn't fail at connection time, it fails silently-ish with a 403 the first time the agent tries to read an object type you didn't grant, which reads to the agent (and to you) as "no data found" unless you check the raw error.

Tips
- Rotate private app tokens the same way you'd rotate a database credential — HubSpot lets you regenerate without deleting the app, so update the env var and restart the MCP server, no re-authorization flow needed.
- Request the narrowest scope set first and add scopes only when a specific workflow needs them; HubSpot's private app UI shows exactly which scopes are granted, which makes access reviews far easier than an OAuth app with a broad consent screen.
- A 403 on a specific object type almost always means a missing scope, not a broken token — check crm.schemas.{object}.read too, since schema introspection has its own scope separate from object read access.


What AI Can Automate: CRM Research, Summaries, and Requirement Extraction

The strongest use case for HubSpot MCP in a developer's hands isn't dashboarding — HubSpot already has reporting for that — it's synthesis across objects that HubSpot's own UI doesn't correlate well. A support ticket, a sales note, and a deal stage are three separate views in the HubSpot app. An agent with MCP access can join them in one pass and hand you a paragraph instead of three browser tabs.

Concrete automatable tasks that hold up well:

  • Requirement extraction from ticket backlog. "List the top 10 open tickets by priority in the Enterprise pipeline, and for each, quote the customer's literal wording of the problem." This turns unstructured ticket text into a defensible input for a PRD.
  • Deal-blocker research before a sprint planning session. "Which deals over $50k ACV in the 'Contract Sent' stage have notes mentioning a missing feature? Group by feature mentioned."
  • Cross-referencing churn signal with product usage complaints. Pull closed-lost deals from the last quarter, read the close reason and associated notes, and cluster by theme.
  • Drafting a customer-facing changelog entry grounded in the actual ticket that triggered the fix, so the language matches what the customer originally asked for.

What doesn't hold up well, and where teams get burned: letting the agent draw statistical conclusions from a partial page of results. If you ask "what percentage of tickets mention slow exports" and the search only returned the first 25 of 340 matching tickets, the agent will confidently divide by 25 unless you explicitly instruct it to check the total count first. Always have the agent report total from the search response, not just what it read.

Prompt: "Search open deals with amount > 50000, paginate through ALL
results (check the `total` field and keep calling with `after` until
exhausted), then summarize how many mention 'API rate limit' in their
notes. State the total deal count you searched against."

Tips
- Ask the agent to state its data boundary explicitly ("based on 340 tickets created between March and June") — CRM-grounded answers still need visible scope, or stakeholders will over-trust a partial sample.
- For requirement extraction, ask for verbatim customer quotes with the source ticket ID attached — paraphrased "customer wants X" loses the nuance a PM needs to judge severity.
- Don't ask the agent to compute revenue totals from deal amount fields without confirming currency and whether the field reflects annual or total contract value — HubSpot's default amount property doesn't normalize this for you.


Handling Customer PII Responsibly in Agent Workflows

Contacts, notes, and tickets are full of personally identifiable information: names, emails, phone numbers, sometimes billing details pasted into a support thread by a well-meaning rep. The moment an AI agent reads that data into its context window, you've extended your PII handling surface to include whatever logging, telemetry, or model-provider retention policy applies to that agent — and that's a real compliance question, not a hypothetical one, under GDPR and CCPA alike.

Practical guardrails that work without gutting the usefulness of the workflow:

Scope requests to the object level you actually need. If the task is "summarize ticket themes," you don't need the email or phone property on the associated contact — request only subject, content, hs_ticket_priority. Most agents will happily fetch every default property unless the prompt or a system instruction constrains the property list.

{
  "tool": "hubspot-search-objects",
  "arguments": {
    "objectType": "tickets",
    "properties": ["subject", "content", "hs_pipeline_stage"],
    "limit": 50
  }
}

Note what's absent: no contact email, no phone, no billing note. That's the property allowlist doing the redaction before data ever reaches the agent's context, which is a stronger control than asking the agent to "not mention" PII after it's already read it.

Instruct the agent to redact before it echoes. When you do need contact-level detail (e.g., mapping a ticket to an account for internal prioritization), tell it explicitly:

Prompt: "When quoting ticket content, replace any email addresses,
phone numbers, or full names with [REDACTED-EMAIL], [REDACTED-PHONE],
[REDACTED-NAME]. Keep company names since we need those for account
mapping. Never output raw contact records."

Never persist raw CRM exports in your repo. A common mistake: an agent pulls 200 contacts into a markdown file "for reference" and that file gets committed. Treat any file containing CRM output the same as you'd treat a .env file — it doesn't belong in git history. Add a repo-level pattern if your team does this kind of research often:

/research/hubspot-export-*.md
/tmp/crm-*.json

Respect data subject rights in derived artifacts. If a contact has been deleted or suppressed in HubSpot for a GDPR erasure request, a cached agent summary referencing that person by name is a compliance gap you've created outside HubSpot's own audit trail. Don't let "the AI's notes" become a shadow copy of data your legal team thinks was deleted.

Tips
- Default every CRM read to the minimum property set the task needs; add fields only when the task explicitly requires them, not "just in case."
- Put PII-redaction instructions in a persistent system prompt or project-level agent config, not a one-off chat message — it's the difference between a policy and a suggestion.
- Audit where agent output lands. A Slack message with a redacted ticket summary is fine; a checked-in markdown file with the same summary — even redacted — should still go through your normal data-retention review.


Tips

Tips
- Start every HubSpot MCP session with a scope check: ask the agent to call hubspot-get-user-details and confirm which portal and which scopes the token has before trusting any "no results found" answer.
- Treat HubSpot MCP as read-first tooling for this course's use case — requirement extraction and CRM research don't need write access, and removing write scopes removes an entire class of accidental-mutation risk.
- Keep a portal ID and environment label (prod vs. sandbox) visible in your MCP config naming (hubspot-prod, hubspot-sandbox) — HubSpot sandboxes are separate portals with separate tokens, and it's easy to query the wrong one.