An MCP server tuned for customer success teams using HubSpot. Exposes contacts, companies, tickets, and deals as Claude tools, with CS-specific helpers for renewal date queries, ticket aging, and health-score reads. The CSM asks “what is at risk this month,” Claude pulls the actual data.
What you’ll need
HubSpot Sales Hub Pro or higher with Service Hub for tickets
A HubSpot Private App token with read scopes for contacts, companies, deals, and tickets
Claude Desktop or Claude Code as the MCP client
A health-score field convention agreed with your CS leadership
Setup
Run the server. Reference implementation is Python, FastAPI-style. Clone, install, set HUBSPOT_TOKEN and HUBSPOT_PORTAL_ID. The server starts on stdio for local Claude Desktop or HTTP for hosted clients.
Configure the helper tools. Three CS-specific helpers ship by default: at_risk_renewals, aging_tickets, accounts_needing_qbr. Each maps to a parametrized HubSpot query. Edit the query templates to match your fields.
Add to MCP client config. Point Claude Desktop at the server. On startup, you should see roughly twelve tools registered.
Set the health-score field. Most teams use a custom number property. Pass its internal name to the server so the helpers can filter on it.
Run “show me at-risk renewals in the next ninety days.” Sanity-check the output against the same query in HubSpot’s UI.
How it works
The server is read-mostly by design. CS workflows are about knowing what is happening, not bulk-mutating records. The default tool surface includes object reads, association traversals (contact to company to deals to tickets), and the three CS-specific helpers.
Writes are limited to ticket creation and notes. No deal stage changes, no contact merges, no property updates on companies. The principle: Claude can ask, summarize, and document, but the CSM still drives the actual customer-facing changes.
Watch-outs
Health-score field drift. Teams change the formula every quarter. The server caches nothing; queries hit the live property. Update the helper docs when the formula changes so prompts stay accurate.
Ticket volume. Aging-tickets queries can return thousands of rows on busy portals. The helper paginates and caps at five hundred by default. Tune for your volume.
Cross-object joins. HubSpot’s association API is slower than direct queries. The server batches associations but a deal-to-tickets traversal across a thousand deals takes minutes, not seconds.
Permissions. Private App tokens bypass user-level permissions. Anyone with access to the MCP client sees all data. Document this clearly with your security team.
Stack
HubSpot — CRM and ticketing source of truth
MCP server — read layer, helper tools, light write surface
Claude — natural-language interface for the CS team
# mcp-server-hubspot-cs
An MCP server tuned for customer success teams using HubSpot. Exposes contacts, companies, tickets, and deals as Claude tools, plus three CS-specific helpers: `at_risk_renewals`, `aging_tickets`, `accounts_needing_qbr`.
> **STATUS: scaffold — not runtime-tested.** The code below is structurally
> complete and follows the official `mcp` Python SDK conventions, but it
> has not been executed against a live HubSpot portal. Treat it as a
> starting point you adapt to your portal's field conventions, not as a
> deployable binary. Health-score field names, custom property paths, and
> association labels vary by portal.
## What it exposes
### Object-read tools (read-only)
- `get_contact(contact_id)` — full contact properties
- `get_company(company_id)` — full company properties + associated contacts
- `get_deal(deal_id)` — full deal properties + associated contacts/company
- `get_ticket(ticket_id)` — full ticket properties + associated company
### Search tools (read-only)
- `search_contacts(query, limit?)`
- `search_companies(query, limit?)`
- `search_deals(filters, limit?)`
- `search_tickets(filters, limit?)`
### CS-specific helpers (read-only)
- `at_risk_renewals(window_days=90)` — deals in renewal stages closing in the window, filtered by configured health-score threshold
- `aging_tickets(min_age_hours=48, limit=500)` — open tickets older than the threshold, grouped by company
- `accounts_needing_qbr(months_since_last=3)` — companies with no recorded QBR activity in the window
### Light-write tools (CSM-driven)
- `create_ticket(subject, body, company_id?)` — open a new ticket
- `add_note(object_type, object_id, body)` — append a note to a record
The server **does not** expose deal-stage changes, contact merges, or company property updates. The principle: Claude can ask, summarize, and document; the CSM drives every customer-facing change.
## Setup
### 1. Install
```bash
git clone <wherever you put this>
cd mcp-server-hubspot-cs
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e .
```
### 2. Create a HubSpot Private App token
In HubSpot: Settings → Integrations → Private Apps → Create. Grant these scopes:
- `crm.objects.contacts.read`
- `crm.objects.companies.read`
- `crm.objects.deals.read`
- `tickets` (read)
- `crm.objects.contacts.write` (only for `add_note`)
- `tickets` (write — only for `create_ticket`)
Copy the access token.
### 3. Configure environment
```bash
export HUBSPOT_TOKEN="pat-na1-..."
export HUBSPOT_PORTAL_ID="12345678"
export HUBSPOT_HEALTH_SCORE_PROPERTY="health_score" # your custom field
export HUBSPOT_RENEWAL_STAGE_IDS="appointmentscheduled,qualifiedtobuy"
export HUBSPOT_RENEWAL_HEALTH_THRESHOLD="60" # below = at risk
```
The renewal stage IDs are pipeline-specific. Look them up in HubSpot → Settings → Pipelines.
### 4. Register with Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"hubspot-cs": {
"command": "python",
"args": ["-m", "hubspot_cs_mcp.server"],
"env": {
"HUBSPOT_TOKEN": "pat-na1-...",
"HUBSPOT_PORTAL_ID": "12345678",
"HUBSPOT_HEALTH_SCORE_PROPERTY": "health_score",
"HUBSPOT_RENEWAL_STAGE_IDS": "appointmentscheduled,qualifiedtobuy",
"HUBSPOT_RENEWAL_HEALTH_THRESHOLD": "60"
}
}
}
}
```
Restart Claude Desktop. You should see ~12 tools registered under `hubspot-cs`.
### 5. Sanity-check
Ask Claude: "Show me at-risk renewals in the next ninety days." Compare the output against the equivalent query in HubSpot's UI. Tune the `HUBSPOT_RENEWAL_HEALTH_THRESHOLD` and stage IDs until they match.
## Watch-outs
- **Private App tokens bypass user-level permissions.** Anyone with access to the MCP client sees every record the token can reach. Document this with your security team.
- **Health-score field drift.** Teams change the formula every quarter. Update `HUBSPOT_HEALTH_SCORE_PROPERTY` and the threshold when the formula changes.
- **Aging-ticket queries can return thousands of rows.** The helper paginates and caps at 500 by default. Tune for your portal volume.
- **Cross-object joins are slow.** A deal-to-tickets traversal across a thousand deals takes minutes — HubSpot's association API is the bottleneck.
## Limits and TODOs (before production use)
- [ ] Add request-level retries with exponential backoff (HubSpot returns 429 readily under sustained load).
- [ ] Write integration tests against a HubSpot sandbox portal.
- [ ] Add structured logging via `python-json-logger`.
- [ ] Wire optional Sentry / OpenTelemetry export.
- [ ] Validate every helper query against the actual portal's pipeline configuration on first run, fail loud if the configured stage IDs do not exist.