Provider REST API

CRM

Connect Claude to HubSpot

Search and update contacts, companies, deals, and notes in the CRM. Toolspoke puts 15 of its actions behind one MCP endpoint that Claude, Cursor, and Codex all speak.

Connection
Provider REST API
Authentication
Private app access token
Actions exposed
15
Cost per call (typical)
1 credit
Adapter
Maintained by Toolspoke

Connected in three steps

  1. 1

    Install HubSpot

    Open the marketplace in your workspace, add HubSpot to the project your agents work in, and it appears on the gateway immediately.

  2. 2

    Connect the credential

    Authenticate with private app access token. Where to get one, and what it has to be able to reach, is the next section.

  3. 3

    Point your agent at the gateway

    Give your client one address, https://toolspoke.com/mcp. Claude Code takes it as a command, Claude and Claude Desktop add it as a custom connector, and Cursor, Codex and VS Code each read it from a config file of their own.

.mcp.json
{
  "mcpServers": {
    "toolspoke": {
      "type": "http",
      "url": "https://toolspoke.com/mcp"
    }
  }
}

One block covers every tool you have installed. HubSpot shows up in the client as soon as your policy allows it, and so does everything else you install later.

Where the address goes, per client

Claude Code

Run it in your project, then /mcp to sign in

claude mcp add --transport http toolspoke https://toolspoke.com/mcp
Claude and Claude Desktop

Settings, then Connectors, then Add custom connector

https://toolspoke.com/mcp
Cursor

~/.cursor/mcp.json, or .cursor/mcp.json for one project

{ "mcpServers": { "toolspoke": { "url": "https://toolspoke.com/mcp" } } }
Codex

~/.codex/config.toml

[mcp_servers.toolspoke]
url = "https://toolspoke.com/mcp"
VS Code

.vscode/mcp.json, or the MCP: Add Server command

{ "servers": { "toolspoke": { "type": "http", "url": "https://toolspoke.com/mcp" } } }

What HubSpot asks for

Private app access token. You provide it once, when you install the connector. Toolspoke encrypts it at rest and decrypts it only for the length of a single call, and the gateway attaches it to the outbound request itself, so it is never part of the arguments an agent sends.

Private app access tokenRequired
HubSpot → Settings → Integrations → Private Apps → Create a private app. Grant it the CRM scopes the actions you plan to use need (crm.objects.contacts, crm.objects.companies, crm.objects.deals, crm.objects.notes, crm.schemas.*, plus crm.objects.owners.read), then copy the access token from the Auth tab. This is not an OAuth app and needs no developer account.
pat-na1-…

What Claude can do in HubSpot

15 actions, each one declared and named by the connector rather than discovered at runtime. A workspace policy grants a person all of them, a hand-picked selection, everything on the read side, everything on the write side, or none.

Reads
10Reads
Writes
5Writes
Destructive
0Destructive

Reads

10

Fetches data and changes nothing.

  • search_contacts

    Search contact records. `query` matches HubSpot's default text properties (name, email, company, phone) the way the CRM search box does; `filters` is the precise form, one filter per property, and every filter in the list must match. Returns the properties you name plus each record's id - pass an id to get_contact for the whole record. Prefer this over get_contact whenever you are starting from a name, an email address or a company rather than from an id. Call list_properties for the property names this account actually has before filtering on anything beyond the standard ones.

  • get_contact

    Fetch one contact record in full. Takes the HubSpot record id that search_contacts returns; set id_property to "email" to look one up by email address instead, which saves a search when you already have the address. Ask for the properties you need - HubSpot returns a small default set otherwise - and use associations to see which companies, deals and tickets the contact is attached to.

  • search_companies

    Search company records, with the same body shape as search_contacts: `query` for free text over HubSpot's default text properties, `filters` for exact property conditions. Use it to turn a company name or domain into a record id before calling get_company, or before filtering deals by associations.company.

  • get_company

    Fetch one company record in full, by the record id search_companies returns. Set id_property to "domain" to look one up by web domain instead. Use associations to list the contacts and deals attached to it.

  • search_deals

    Search deal records. Beyond ordinary property filters this is the operation that answers pipeline questions: filter on dealstage, pipeline, closedate or amount, and sort by hs_lastmodifieddate to see what moved. HubSpot also accepts the association pseudo-properties here - {"propertyName": "associations.company", "operator": "EQ", "value": "<company id>"} returns that company's deals. Call list_pipelines first for the stage ids this account uses, since dealstage holds an internal id rather than the label shown in the UI.

  • get_deal

    Fetch one deal record in full, by the record id search_deals returns. Use associations to see which contacts and companies it is attached to; the returned dealstage is an internal stage id, which list_pipelines translates into a label.

  • list_notes

    List the notes logged against one record, newest first - the written history of what people have said about a contact, company, deal or ticket. Give the record's own id and say which kind of record it is; the ids come from search_contacts, search_companies or search_deals. Returns the note bodies as HTML, since that is how HubSpot stores them.

  • list_owners

    List the account's owners - the HubSpot users a record can be assigned to - with their ids, names and email addresses. Call it to turn a person's name into the hubspot_owner_id that create_deal, update_deal and create_note take. This is also the cheapest read in the connector, which is why it is the connection check.

  • list_pipelines

    List the pipelines defined for an object type, each with its stages in display order. This is the translation table between what a person says and what the API takes: "Closed Won" is a label, closedwon is the stage id, and create_deal and update_deal only accept the id. Defaults to deals; pass tickets for the support pipelines.

  • list_properties

    List every property defined on an object type, with its internal name, label, type and any dropdown options. Call this before filtering or writing anything non-standard: HubSpot accounts add custom properties freely, and a filter naming a property that does not exist comes back as an error rather than as an empty result. The internal name is what search_contacts, create_contact and update_deal take.

Writes

5

Creates or updates something on the other side.

  • create_contact

    Create a contact. `properties` is a flat map of HubSpot property names to values - email is the one HubSpot treats as the unique key, so include it unless you have an external id scheme. HubSpot rejects a create whose email already exists; search_contacts first, and use update_contact when the record is already there. Call list_properties for the property names this account defines.

  • update_contact

    Change properties on an existing contact. Only the properties you send are touched; everything else is left alone, and sending an empty string clears a property. Get the record id from search_contacts or get_contact first. This cannot delete the record.

  • create_deal

    Create a deal. HubSpot needs dealname, and needs pipeline and dealstage to be internal ids rather than the labels shown in the UI - call list_pipelines first and copy the ids from there. hubspot_owner_id comes from list_owners. This creates the deal on its own; attaching it to a contact or company is a separate association HubSpot's UI usually does, and this connector does not expose the associations API.

  • update_deal

    Change properties on an existing deal - this is how a deal is moved to another stage, re-priced, or given a new close date. Only the properties you send are touched. dealstage must be an internal stage id from list_pipelines, not a label. This cannot delete the deal.

  • create_note

    Log a note on a CRM record. The note appears on that record's activity timeline for the account's own users; it is not sent to the contact and nobody outside the portal sees it, which is why this is a write rather than a destructive action. `body` is HTML, so line breaks want <br> rather than \n. `timestamp` is when the note should be dated - pass the current time in ISO 8601 unless you are back-dating something. Attach the note by giving the record's id and the HubSpot-defined association type for its object: 202 for a contact, 190 for a company, 214 for a deal, 228 for a ticket.

What it will not do

Enforced by the gateway rather than left to convention, which is why each of these can be stated flatly.

It cannot call anything else
The 15 actions above are the whole of it. A call to any other name is refused before it reaches HubSpot rather than forwarded on, and connecting your account does not add to the list: it is fixed by the connector, not discovered at run time.
Nothing here deletes
This connector writes to HubSpot, but nothing in it deletes or permanently alters anything.
It reaches no further than your credential
Toolspoke holds no access to HubSpot of its own. Every call carries the credential you stored and nothing besides, so whatever that credential cannot reach, this connector cannot reach either.
It never hears from HubSpot
Nothing is pushed to it. There is no webhook, no subscription and no polling, so this connector cannot notice by itself that something changed in HubSpot. An agent has to ask.
It does not smooth over provider limits
Toolspoke does not retry, queue or back off around HubSpot's own rate limits. A call that HubSpot refuses comes back to the agent as a failed call.

Before you connect it

What can Claude do in HubSpot?

15 named actions: 10 that only read and 5 that write. They include search_contacts, get_contact and create_contact. Nothing outside that list is reachable: the connector declares each operation by name rather than proxying whatever an agent asks for.

What credentials does the HubSpot connector need?

Private app access token. The connector asks for private app access token. Values are encrypted at rest and attached to the outbound request by the gateway, so they are never part of the arguments an agent sends and never reach the audit log.

Does the HubSpot connector work with Cursor and Codex, or only Claude?

Any client that speaks MCP, and every one of them gets the same 15 actions. There is a single address, https://toolspoke.com/mcp. Claude Code adds it with claude mcp add --transport http, Claude and Claude Desktop take it as a custom connector in settings, Cursor reads it from .cursor/mcp.json, Codex from ~/.codex/config.toml, and VS Code from .vscode/mcp.json. Each of them signs in to the gateway itself, so there is no key to paste.

What does the HubSpot connector not do?

The 15 actions above are the whole of it. A call to any other name is refused before it reaches HubSpot rather than forwarded on, and connecting your account does not add to the list: it is fixed by the connector, not discovered at run time. This connector writes to HubSpot, but nothing in it deletes or permanently alters anything. Toolspoke holds no access to HubSpot of its own. Every call carries the credential you stored and nothing besides, so whatever that credential cannot reach, this connector cannot reach either. Nothing is pushed to it. There is no webhook, no subscription and no polling, so this connector cannot notice by itself that something changed in HubSpot. An agent has to ask. Toolspoke does not retry, queue or back off around HubSpot's own rate limits. A call that HubSpot refuses comes back to the agent as a failed call.

Can I limit which actions an agent can call?

Yes, in two places. The project switches HubSpot's actions on and off one at a time, for everyone in the project at once, and the screen groups them by read, write and destructive so turning off everything that deletes is one click. An individual agent key can then be narrowed further, to particular toolkits in a project and to particular actions in a toolkit. Whatever it was granted, a key never reaches a project its owner cannot.

What gets recorded when an agent calls HubSpot?

Every attempt, with the agent that made it and the person that agent belongs to, the full request payload, the response payload, the status, the duration, and the credits spent. Values whose key names a secret are masked out before the record is shown to anyone. An operation the connector marks as not retained never has its response body written at all, so the gateway keeps no second copy of what was read.