Skip to main content
HubSpot CMS is a read + guardrailed write connector, called via tools/call over POST /hubspot/mcp. The connector is connected through OAuth (HubSpot login; see Forbind HubSpot CMS).
Endpoint: https://mcp.consile.ai/hubspot/mcp. HubSpot CMS is its own named connector with only its own tools.
The connected HubSpot portal must be on CMS Hub, Content Hub, or Marketing Hub Professional/Enterprise. The OAuth scopes content, hubdb, and business-intelligence are gated at consent: a Starter or Free portal cannot grant them and the connection attempt fails at the HubSpot consent screen. See the tier requirement note below.

Write safety: guardrail model

The nine write tools split into two tiers based on reversibility:
  1. Draft operations run freely. Creating a page, editing a blog post draft, or upserting a HubDB row all target the unpublished draft buffer. Nothing reaches the live site and the change can be discarded. No confirmation required.
  2. Publishing and destructive actions require confirm: true. These are: pushing a page draft live (publish_page), publishing a blog post draft (publish_blog_post), publishing a HubDB table draft to live pages (publish_hubdb_table), and discarding a HubDB draft (reset_hubdb_draft). Without confirm: true, the tool returns a WritePreview dry-run and calls no HubSpot API.
This is a designed-in safety default (connector-local application logic), not an unbreakable platform gate. See Handlinger & guardrails for the full cross-connector policy.

WritePreview shape

When a write action requires confirmation but confirm is omitted or false, the tool returns this shape instead of calling HubSpot:
To apply, re-run the identical call with confirm: true added.

Draft-first default on create

New pages and blog posts are always created with state: "DRAFT". The connector enforces this regardless of what the caller sends, so nothing is accidentally published on first create.

Portal tier requirement

The OAuth scopes this connector requests (content, hubdb, business-intelligence) require the connected HubSpot portal to be on one of:
  • CMS Hub or Content Hub
  • Marketing Hub Professional or Enterprise
A Starter or Free portal cannot grant these scopes. The connection attempt fails at the HubSpot OAuth consent screen with a scope-permission error. If your customer hits that error, they need to upgrade their HubSpot subscription before connecting.

Authentication

HubSpot CMS uses the standard OAuth authorization-code flow with refresh token. Each tenant authorizes Consile’s HubSpot app and their refresh_token is vaulted under (tenantId, "hubspot"). Access tokens expire after roughly 30 minutes and are renewed silently by the platform’s refreshing vault. The AI never sees the token. OAuth scopes: content, hubdb, business-intelligence, oauth (the last is auto-injected by HubSpot on every grant and is not a functional scope). Revoking access. There is no upstream revoke in the portal in v1. To pull access back from HubSpot, uninstall the Consile app in HubSpot’s connected apps settings. Disconnecting in the Consile portal deletes the vaulted token immediately.

Shared parameters

Paging

List tools use cursor-based paging. Pass after from paging.next.after in the previous response to retrieve the next page.
integer
default:"20"
Max items to return in this page (1-100).
string
Cursor from a previous response’s paging.next.after.

Page type

string
default:"site"
Which page tree: "site" (website pages) or "landing" (landing pages).

HubDB table reference

string
required
HubDB table numeric id (preferred) or unique table name.

Read tools: pages (4 tools)

list_pages

List website or landing pages with paging. Pass archived: true to list archived pages instead of live ones.
string
default:"site"
integer
default:"20"
string
boolean
List archived pages instead of live ones.
Returns: { results, paging } where each result includes id, name, slug, state, htmlTitle, metaDescription, publishDate, archivedAt, createdAt, updatedAt.

get_page

Retrieve a single site or landing page (the live/published version) by id.
string
default:"site"
string
required
The page id.
Returns: Full page object.

get_page_draft

Read the unpublished draft buffer of a page to inspect pending edits before publishing.
string
default:"site"
string
required
The page id.
Returns: Draft page object (same shape as get_page but reflects staged edits).

list_page_revisions

List prior published revisions of a page for review or rollback planning.
string
default:"site"
string
required
The page id.
integer
default:"20"
string
Returns: { results, paging } where each revision includes id, createdAt, createdBy.

Read tools: blog (4 tools)

list_blog_posts

List blog posts with paging. Pass archived: true to list archived posts.
integer
default:"20"
string
boolean
List archived posts instead of live ones.
Returns: { results, paging } where each result includes id, name, slug, state, contentGroupId, htmlTitle, metaDescription, publishDate, tagIds, authorName, createdAt, updatedAt.

get_blog_post

Get a single blog post by id. Set draft: true to read the draft buffer instead of the live version.
string
required
The blog post id.
boolean
Read the draft buffer instead of the live version.
Returns: Full blog post object including postBody (HTML content).

list_blog_tags

List blog tags with paging.
integer
default:"20"
string
Returns: { results, paging } where each tag includes id, name, slug, createdAt, updatedAt.

list_blog_authors

List blog authors with paging. Authors are referenced by posts; this is a read-through of the author registry.
integer
default:"20"
string
Returns: { results, paging } where each author includes id, displayName, email, slug, avatar.

Read tools: HubDB (5 tools)

list_hubdb_tables

List the account’s HubDB tables with id, name, label, column metadata, row count, and publish state.
integer
default:"20"
string
Returns: { results, paging } where each table includes id, name, label, columns (array with type, name, label, options), rowCount, publishedAt.

get_hubdb_table

Get a single HubDB table’s full definition including column schema: types, options, and foreignTableId/foreignColumnId for FOREIGN_ID columns. Call this before writing rows to understand the expected value types.
string
required
Returns: Full table definition including the columns array.

list_hubdb_rows

List rows of a published HubDB table with paging. Returns each row’s cell values per column.
string
required
integer
default:"20"
string
Returns: { results, paging } where each row includes id, values (map of column name to cell value).

get_hubdb_row

Get a single published HubDB row by rowId.
string
required
string
required
The row id.
Returns: { id, values }.

export_hubdb_table

Export a HubDB table’s data as a CSV or Excel file. Returns the raw file content.
string
required
string
default:"CSV"
Export format: "CSV" or "EXCEL".
Returns: { tableIdOrName, format, data } where data is the raw file content as a string.

Read tools: analytics (1 tool)

get_traffic_analytics

Read traffic and page-view analytics over a date range. Optionally broken down by content, source, geolocation, or UTM campaign.
Requires Marketing Hub Professional/Enterprise on the connected portal. The business-intelligence scope gates this endpoint.
string
default:"totals"
How to break the traffic down. One of: totals, sessions, sources, geolocation, pages, landing-pages, utm-campaigns.
string
default:"total"
Aggregation period. One of: total, daily, weekly, monthly, summarize/daily, summarize/weekly, summarize/monthly.
string
required
Start date in YYYYMMDD format (inclusive).
string
required
End date in YYYYMMDD format (inclusive).
integer
Max breakdown rows to return (up to 1000).
Returns: HubSpot analytics report for the requested breakdown and period.

Write tools (9 guardrailed)

Write tools that push content to the live site or discard draft work require confirm: true. Without it the tool returns a WritePreview and calls no HubSpot API. Draft-only operations (create, edit) run freely. See Handlinger & guardrails.

create_page_draft

Create a new site or landing page as an unpublished draft. State is forced to DRAFT regardless of what you pass; nothing goes live on create. Publish later with publish_page. Does not require confirmation (reversible draft operation).
string
default:"site"
string
required
Internal page name.
string
Path to the template/theme file the page uses.
string
URL slug (path) for the page.
string
SEO <title> for the page.
string
SEO meta description.
Returns: The created draft page object.

update_page_draft

Edit a page’s draft buffer (name, slug, SEO title, SEO description) without touching the live page. Publish the change later with publish_page. Does not require confirmation (draft buffer only, live page untouched).
string
default:"site"
string
required
The page id.
string
string
string
string
Returns: The updated draft page object.

publish_page

Push a page’s unpublished draft changes live to the public website. This is a push-live of the draft: it updates an already-published page, not a first-time publish of a brand-new page. Requires confirm: true. Without it, returns a WritePreview dry-run describing exactly what would be published.
string
default:"site"
string
required
The page id.
boolean
Set true to apply. Omit or false to receive a WritePreview.
Returns: HubSpot’s push-live response on success; WritePreview if not confirmed.

create_blog_post_draft

Create a new blog post as an unpublished draft. State is forced to DRAFT. Requires the target blog’s contentGroupId (read it from an existing post via list_blog_posts or find it in the HubSpot UI). Publish later with publish_blog_post. Does not require confirmation (reversible draft operation).
string
required
The blog (content group) id this post belongs to.
string
required
Post title / internal name.
string
URL slug for the post.
string
Post body HTML.
string
SEO <title>.
string
SEO meta description.
Returns: The created draft blog post object.

update_blog_post_draft

Edit a blog post’s draft buffer (title, slug, body, SEO) without touching the live post. Publish later with publish_blog_post. Does not require confirmation (draft buffer only, live post untouched).
string
required
The blog post id.
string
string
string
Post body HTML.
string
string
Returns: The updated draft blog post object.

publish_blog_post

Push a blog post’s draft changes live to the public site. Requires confirm: true. Without it, returns a WritePreview dry-run.
string
required
The blog post id.
boolean
Set true to apply. Omit or false to receive a WritePreview.
Returns: HubSpot’s push-live response on success; WritePreview if not confirmed.

upsert_hubdb_row_draft

Create a new row (omit rowId) or update an existing one (pass rowId) in a HubDB table’s draft. Changes are staged and invisible on live pages until publish_hubdb_table is called. Call get_hubdb_table first to inspect column types. Cell values must match each column’s type (for example: FOREIGN_ID columns expect arrays, select columns expect option objects, date columns expect epoch milliseconds). Does not require confirmation (staged in draft, nothing live until publish).
string
required
string
Existing row id to update. Omit to create a new row.
object
required
Map of columnName to cell value, typed per the column schema.
Returns: The created or updated draft row.

publish_hubdb_table

Publish a HubDB table’s draft (schema and rows) so the changes appear on live pages. Any prior HubDB row write is invisible until this is called. Requires confirm: true. Without it, returns a WritePreview dry-run.
string
required
boolean
Set true to apply. Omit or false to receive a WritePreview.
Returns: HubSpot’s publish-table response on success; WritePreview if not confirmed.

reset_hubdb_draft

Discard all unpublished draft changes on a HubDB table, reverting the draft to the current live/published version. Destructive: all staged row and schema edits are lost. Requires confirm: true. Without it, returns a WritePreview dry-run.
string
required
boolean
Set true to apply. Omit or false to receive a WritePreview.
Returns: HubSpot’s reset-draft response on success; WritePreview if not confirmed.

Code examples

Tool names are namespaced hubspot__<tool> in Claude/ChatGPT (the AI invokes these automatically). The bare names (e.g. list_pages) are used here for readability.
An expired or revoked access token returns AccessTokenExpiredError. Because HubSpot uses a refresh_token flow, silent renewal normally prevents this. If it does surface, reconnect via the portal to issue a fresh token pair. See Errors & limits.
HubSpot’s public-app rate limit is 110 requests per 10 seconds per connected account. On a 429, the client retries up to 3 times with backoff (honoring Retry-After when present), so a chatty session self-heals. Per-request timeout is 20s. get_traffic_analytics uses HubSpot’s legacy v2 analytics API with dates in YYYYMMDD format. A 403 almost always means the connected portal’s plan or granted scopes do not cover that surface. See Errors & limits.
This connector is multi-instance: a tenant can connect several HubSpot portals, each as its own connector instance with a separately vaulted refresh_token.