Before you start
- A HubSpot account where you are a super admin. HubSpot only lets super admins create private apps.
- Node.js 20.6 or newer to run the server. 18+ works if you pass the token through your MCP client config rather than a
.envfile. - Claude Desktop or Claude Code. Optional: the MCP Inspector tests the server on its own.
Have some test records ready. A brand-new test account holds very little data, so searches will come back empty. Add a few contacts, deals and tickets first, or import HubSpot's sample CSV files.
Choose an account
Pick where the private app will live. Either works for the server; they differ in how much you can trust the data.
A free HubSpot account
A regular free CRM account where you are super admin. Add a handful of dummy records and you can demo everything.
- You create the private app here as a super admin
- Use test data only, never real customer records
A developer test account
A sandbox created from a developer account, kept separate from real customer data.
- Development › Testing › Test accounts › Create developer test account
- Up to 10 per account; cannot sync with other accounts
- HubSpot's docs don't confirm legacy private apps are available inside them, so check before relying on it
Create the private app
HubSpot now files private apps under legacy apps. They are still supported, which is all this server needs.
- On the Basic Info tab, enter an app name such as
Claude CRM connectorand a short description. - Open the Scopes tab and click Add new scope.
- Select the scopes in the next step, using the Find a scope search box, then click Update.
- Click Create app and confirm in the dialog.
Grant the six scopes
Read and write for each object type. Nothing broader is requested.
| Object | Scopes | Used by |
|---|---|---|
| Contacts | crm.objects.contacts.read crm.objects.contacts.write | search_contacts, get_contact, create_contact, get_account_summary |
| Deals | crm.objects.deals.read crm.objects.deals.write | search_deals, get_deal, create_deal, update_deal_stage |
| Tickets | crm.objects.tickets.read crm.objects.tickets.write | search_tickets, create_ticket |
HubSpot's scope picker may label the ticket scopes simply tickets (read and write). Pick those if the longer names don't appear. If a call later fails with MISSING_SCOPES, add the scope named in the error.
Copy the token
Open the app you just created, choose the Auth tab, then Show token and Copy. It looks like this (masked example, format only):
pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Give the token to the server through the HUBSPOT_ACCESS_TOKEN environment variable. Which method depends on how you run it:
- From Claude: put it in the
envblock of the MCP config (step 5). Recommended. - From a terminal:
export HUBSPOT_ACCESS_TOKEN="pat-..."first, or runnode --env-file=.env dist/index.js(Node 20.6+). The server does not read.envon its own.
Treat the token like a password. Never commit it or paste it into chat. .env is already git-ignored. If it leaks, rotate it in the app's Auth tab.
Connect Claude
Build the server once with npm install && npm run build, then add it to your MCP config. In Claude Desktop on macOS the file is ~/Library/Application Support/Claude/claude_desktop_config.json. Claude Code takes the same block in its MCP settings.
{
"mcpServers": {
"hubspot-crm": {
"command": "node",
"args": ["/absolute/path/to/hubspot-crm-mcp-server/dist/index.js"],
"env": { "HUBSPOT_ACCESS_TOKEN": "pat-your-token" }
}
}
}
Restart Claude so it picks up the new server.
Verify it works
Test the server on its own first, so you know the token and scopes are right before involving Claude:
export HUBSPOT_ACCESS_TOKEN="pat-your-token" npm run inspector
In the Inspector, run search_contacts with any query. You should see either matching contacts or No contacts matched, both of which prove the connection works. Then ask Claude: "Search HubSpot for contacts at Acme".
Troubleshooting
| You see | Likely cause | Fix |
|---|---|---|
| HUBSPOT_ACCESS_TOKEN is not set | The variable never reached the server process. | Add it to the MCP config env block, or export it / use --env-file in a terminal. |
| Authentication error / 401 | Token missing, mistyped, or revoked. | Re-copy it from the app's Auth tab, with no spaces or quotes inside the value. |
| MISSING_SCOPES / 403 | The private app lacks a scope for that object. | Add the scope named in the error, then retry. |
| "No contacts matched" | The account has little or no data. | Create a few test records, then search again. |
| create_ticket fails on pipeline or stage | The portal uses a custom ticket pipeline. | Known limitation: the tool assumes the default pipeline (see README). |
| HubSpot's docs page loads blank | Their docs are a JavaScript app and can fail with some browsers or blockers. | Use this page instead; every step you need is here. |