hubspot-cli ships with a built-in MCP server that exposes every CRM operation as a strongly-typed tool over JSON-RPC 2.0 on stdio.
See also: hubspot mcp command reference.
| Claude Code skills | MCP server | |
|---|---|---|
| How it works | Markdown docs; Claude shells out to hubspot |
Typed JSON-RPC tools; client calls them directly |
| Setup | hubspot skill-install |
hubspot mcp install --client … |
| Best for | Claude Code where shell access is natural | Claude Desktop, programmatic MCP clients, sandboxed agents |
| Arguments | Free-form CLI | Structured JSON matching each tool's inputSchema |
You can run both in parallel — they aren't mutually exclusive.
┌─────────────────┐ stdin (JSON-RPC frames) ┌────────────────────────┐
│ MCP client │ ─────────────────────────► │ hubspot mcp serve │
│ (Claude Desktop │ │ (newline-delimited │
│ or Claude Code)│ ◄───────────────────────── │ JSON-RPC 2.0 loop) │
└─────────────────┘ stdout (JSON-RPC frames) └───────────┬────────────┘
│ spawns
▼
┌──────────────────┐
│ hubspot <args> │
│ (child process) │
└──────────────────┘
Each tools/call spawns a child hubspot process with --output json and the argv derived from the tool's arguments. The child's stdout (a {success, data, …} envelope) is wrapped in an MCP text content block and returned. isError is set when the child exits non-zero.
Self-exec keeps the MCP surface in lock-step with the CLI: every behavioural improvement in a CLI handler is instantly visible to MCP clients.
| Method | Status |
|---|---|
initialize |
✅ returns {protocolVersion: "2024-11-05", capabilities: {tools: {listChanged: false}}} |
notifications/initialized |
✅ silently acknowledged |
tools/list |
✅ returns the full catalog (60+ tools) |
tools/call |
✅ dispatches to the CLI via self-exec |
ping |
✅ returns {} |
resources/*, prompts/* |
not implemented (server advertises only tools capability) |
The server inherits the launcher's environment. Two ways to hand it a token:
- Shell export. Set
HUBSPOT_ACCESS_TOKENin the shell that launches the MCP client (e.g. your desktop session). - Client config
envblock. Both Claude Desktop and Claude Code support anenvmap alongsidecommand/args.
{
"mcpServers": {
"hubspot": {
"command": "hubspot",
"args": ["mcp", "serve"],
"env": { "HUBSPOT_ACCESS_TOKEN": "pat-na1-…" }
}
}
}Multi-portal users can override per-launch: "args": ["mcp", "serve"] plus "env": { "HUBSPOT_PROFILE": "sandbox" }.
- Names:
crm_<object>_<action>,discover_<x>,auth_<x>. Underscores only (MCP tool names must match[a-zA-Z0-9_-]+). - Schemas: every tool's
inputSchemais strict JSON Schema withadditionalProperties: false, so the client can validate before calling. - Properties payload: always a JSON object, never a stringified JSON blob. The server takes care of serialising it for the CLI.
- Pagination: list/search tools accept
limit(1–100) andafter(cursor from the previous response'spaging.next_cursor).
The MCP tools/call result always contains at least one content block with the child process's stdout. When isError: true:
- Parse the JSON envelope from
content[0].text— it follows the standard{success: false, error: {code, message, suggestions, retry}}shape. - A second
contentblock may be present with[stderr]prefix, containing any tracing output from the child. _meta.exitCodeechoes the CLI's exit code (see exit code reference).
- The server has no authentication of its own; anyone who can talk to its stdio can call any tool. Only launch it through a trusted MCP client.
- Destructive tools (
crm_*_delete) go straight through. If your portal has records the agent should not touch, use a scoped private-app token rather than a portal-admin token. - Tokens are never echoed in responses — the CLI masks them — but the
envblock of an MCP config file is on disk; treat it with the same care as any other secrets file.
printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| hubspot mcp serve \
| tee /tmp/mcp.log \
| jq -c '.result | keys'You should see two lines: the initialize response keys and the tools list keys. CI runs this exact flow on every PR (mcp-smoke job) and on every release (release-sanity job).