Claude Code gives HubSpot MCP the best ergonomics of the four clients in this course, mostly because its permission model and project-scoped config were built with exactly this kind of "read external data, then act on the codebase" workflow in mind. This topic walks through wiring the server into both the CLI and the VS Code extension, then building a query workflow you'd actually run before a sprint planning meeting.
Installing and Connecting HubSpot MCP to Claude Code
Claude Code reads MCP server definitions from .mcp.json at the project root (shared with the team, checked into git) or from user-level config for personal servers. For a CRM credential, use the project file for the server definition but keep the token itself in an environment variable that's never committed.
Add the server with the CLI:
claude mcp add hubspot -- npx -y @hubspot/mcp-server
This writes an entry to .mcp.json:
{
"mcpServers": {
"hubspot": {
"command": "npx",
"args": ["-y", "@hubspot/mcp-server"],
"env": {
"HUBSPOT_ACCESS_TOKEN": "${HUBSPOT_ACCESS_TOKEN}"
}
}
}
}
Set the actual token in your shell profile or a project-local .env that's gitignored:
HUBSPOT_ACCESS_TOKEN=pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
source .env
claude
Inside a Claude Code session, verify the connection with /mcp — it lists connected servers and their tool counts. If hubspot shows zero tools or an error state, the most common cause is the token env var not being exported in the shell that launched claude, not a bad token.
For VS Code, install the Claude Code extension, then open the Command Palette and run Claude Code: Open MCP Settings, or simply open a project that already has .mcp.json — the extension picks up project-scoped servers automatically the same way the CLI does. The extension surfaces the same /mcp panel inside its chat sidebar, and tool-call approvals appear as inline cards you can expand to see the exact JSON arguments before approving.
One VS Code-specific detail worth knowing: the extension runs the MCP server process as a child of the VS Code extension host, not your integrated terminal shell. If you rely on direnv or a shell-specific .env loader, the extension may not inherit it — set HUBSPOT_ACCESS_TOKEN in your OS-level environment or in VS Code's settings.json under terminal.integrated.env.* to be safe.
{
"mcpServers": {
"hubspot": {
"command": "npx",
"args": ["-y", "@hubspot/mcp-server"],
"env": {
"HUBSPOT_ACCESS_TOKEN": "pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
If you inline the token directly in .mcp.json for a personal, uncommitted config (using .mcp.local.json or a user-scoped ~/.claude.json entry instead of the project file), that avoids the env-inheritance quirk entirely — just never do this in the project-scoped file that gets committed.
Tips
- Runclaude mcp listafter adding the server to confirm it's registered before opening a session — catching a typo in the command here is faster than debugging a silent tool-call failure later.
- Use a project-scoped.mcp.jsonfor the server definition (safe to commit — it has no secret in it once you use${HUBSPOT_ACCESS_TOKEN}) and a user-scoped or.env-based token for the actual credential.
- If VS Code's tool-call approval card shows unexpected properties in ahubspot-search-objectscall (like every default field on a contact), reject it and re-prompt with an explicitpropertiesallowlist rather than approving and hoping the agent redacts later.
Querying Deals, Tickets, and Contact Activity from the Terminal
The CLI's plain-text back-and-forth is well suited to iterative CRM querying — you refine a filter, see the count, refine again. Start broad, then narrow, exactly like you would with SQL.
> List all deals in the "Enterprise" pipeline with a close date in Q3,
show deal name, amount, stage, and owner.
Claude Code will call hubspot-search-objects with a filter on pipeline and closedate, then render a table. If the pipeline name doesn't match an internal ID, expect a follow-up tool call to hubspot-get-schemas to resolve the pipeline label to its hs_pipeline ID — this is normal and worth watching in the transcript so you learn your account's internal IDs over time.
A more targeted, multi-object query that's genuinely hard to do fast in the HubSpot UI:
> For every open ticket with priority HIGH, find the associated
company, then check if that company has any open deal over $20k.
List tickets where that's true — these are support issues affecting
active sales opportunities.
This requires three chained tool calls: search tickets, hubspot-list-associations from ticket to company, then search deals filtered by company ID and amount. Claude Code handles the chaining itself, but it's worth reading the tool-call trace (visible with /mcp or by scrolling) to confirm it associated correctly — association direction (ticket→company vs. company→ticket) is a common source of empty results when the agent guesses the wrong association type ID.
For contact activity specifically, notes are the richest source and the easiest to under-fetch:
> Pull all notes associated with contact "jane.doe@acmecorp.com" from
the last 90 days, sorted newest first. Quote each note's content
verbatim with its creation date.
{
"tool": "hubspot-list-associations",
"arguments": {
"fromObjectType": "contacts",
"fromObjectId": "51234567890",
"toObjectType": "notes"
}
}
Tips
- When a query spans pipeline names, ask the agent to print the resolved internal pipeline/stage IDs alongside the labels once — cache that mapping mentally, it saves a round trip on every future query in the session.
- If association-based queries return empty unexpectedly, ask the agent to try both association directions explicitly rather than assuming the first attempt was exhaustive.
- Break three-hop queries (ticket → company → deal) into two prompts when precision matters more than speed — you get a chance to sanity-check the intermediate company list before the final filter runs.
Turning Sales and Support Signals into Product Requirements
The payoff of this whole module is compressing a research task that used to take an afternoon of cross-referencing spreadsheets into one Claude Code session that ends with a draft requirements doc, ready to paste into Jira or Linear.
A representative workflow:
> Search all closed-lost deals from the last 2 quarters where the loss
reason mentions "missing feature" or "integration". For each, pull
the associated notes and summarize what specific capability was
missing. Then group the results into themes and rank by total lost
ACV per theme.
The agent runs a filtered hubspot-search-objects on deals (dealstage = closedlost, with a date range filter), follows associations to notes, and produces something like:
## Lost-Deal Themes (Q1–Q2 2026)
### 1. Missing SSO/SAML support — $340K lost ACV across 6 deals
- Deal "Northwind Renewal" ($85K): note quotes "security team blocked
procurement without SAML"
- Deal "Ashcroft Expansion" ($62K): ...
### 2. No bulk CSV export — $190K lost ACV across 4 deals
- ...
That output is already 80% of a well-formed requirements ticket — it has quantified impact, direct customer language, and a count of affected accounts. The remaining step is human: deciding if "6 deals blocked on SSO" clears your bar for a roadmap slot, which is a business call the agent shouldn't make for you.
For requirements grounded in open tickets rather than lost deals, ask for verbatim language plus frequency:
> From open tickets tagged "billing", extract the specific complaint
in each customer's own words. Cluster similar complaints and count
occurrences. Output as a table: theme | occurrence count | one
representative quote | ticket IDs.
Feeding this directly into a PRD template turns the "why are we building this" section from an assertion into an evidenced claim with a link back to primary sources — auditable by anyone on the team, not just whoever remembers the customer call.
Tips
- Ask for ticket/deal IDs alongside every claim in a requirements summary — a PM reviewing the doc should be able to click through to the source record in HubSpot, not just trust the AI's paraphrase.
- Rank by quantified impact (lost ACV, ticket count, affected account count) rather than letting the agent's prose order stand in for prioritization — LLMs default to recency and verbosity, not business weight.
- Re-run the same extraction prompt a week later before finalizing a roadmap decision — CRM data changes, and a stale snapshot baked into a requirements doc ages badly.
Prompting Patterns for Bounded, Privacy-Safe CRM Queries
Unbounded prompts against a CRM MCP server produce two failure modes: runaway pagination (the agent tries to read every object of a type with no filter) and PII overreach (fetching every default property when only two are needed). Both are fixable with prompt discipline, and Claude Code's project-level CLAUDE.md is the right place to encode the pattern once rather than repeating it per prompt.
A project CLAUDE.md snippet worth adding to any repo where HubSpot MCP is in regular use:
## HubSpot MCP Query Rules
- Always pass an explicit `properties` list to `hubspot-search-objects`
and `hubspot-list-objects` — never fetch default/all properties.
- Never request `email`, `phone`, or billing-related contact properties
unless the task explicitly requires contact-level outreach mapping.
- Cap searches at `limit: 50` per call; if more data is needed, paginate
explicitly and report the running total, don't silently loop past 500
records without confirming with the user first.
- When quoting note or ticket content that may include a customer's
name, email, or phone number, redact it as [REDACTED-X] unless the
user explicitly asks for the raw record.
With that in place, a bounded, privacy-safe prompt looks like:
> Search tickets created in the last 30 days with priority HIGH or
URGENT. Properties: subject, content, hs_ticket_priority, createdate
only. Limit 50. Redact any customer names or emails in your summary.
Report the total ticket count from the search response.
Compare that to the naive version — "show me all recent high-priority tickets" — which leaves property scope, pagination limits, and redaction entirely up to model defaults. The bounded version is barely longer and removes three separate categories of risk.
Tips
- Encode CRM query rules once inCLAUDE.mdrather than repeating them per prompt — consistency across the team matters more than any single prompt's cleverness.
- Always ask for the search response'stotalcount explicitly when the analysis depends on completeness (percentages, "how many," rankings) — an agent silently working off page one of results is the single most common cause of wrong conclusions in this workflow.
- Review the tool-call arguments in Claude Code's approval UI for the first few queries in any new project — once you trust theCLAUDE.mdrules are being followed, you can move to auto-approve for read-only HubSpot tools specifically.
Tips
Tips
- Keep the HubSpot MCP server definition in project-scoped.mcp.jsonbut the token in an untracked.env— this lets the whole team share the same query patterns without ever sharing a credential.
- Use/mcpliberally during a session to confirm which tools are actually available — a scope mismatch shows up here as a tool present but erroring, which is a faster diagnosis than reading a wall of failed-call text.
- For anything destined for a requirements doc or ticket, always keep object IDs in the output — traceability back to the source CRM record is what makes AI-assisted CRM research defensible to a skeptical stakeholder.