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:
{
  "preview": {
    "action": "publish_page",
    "summary": "Publish site page 12345678 draft to the LIVE site.",
    "request": {
      "method": "POST",
      "path": "/cms/v3/pages/site-pages/12345678/draft/push-live",
      "body": {}
    },
    "confirmHint": "Re-run the same call with confirm: true to apply this change."
  }
}
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 (any paid tier)
  • 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.
limit
integer
default:"20"
Max items to return in this page (1-100).
after
string
Cursor from a previous response’s paging.next.after.

Page type

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

HubDB table reference

tableIdOrName
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.
type
string
default:"site"
limit
integer
default:"20"
after
string
archived
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.
type
string
default:"site"
objectId
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.
type
string
default:"site"
objectId
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.
type
string
default:"site"
objectId
string
required
The page id.
limit
integer
default:"20"
after
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.
limit
integer
default:"20"
after
string
archived
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.
objectId
string
required
The blog post id.
draft
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.
limit
integer
default:"20"
after
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.
limit
integer
default:"20"
after
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.
limit
integer
default:"20"
after
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.
tableIdOrName
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.
tableIdOrName
string
required
limit
integer
default:"20"
after
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.
tableIdOrName
string
required
rowId
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.
tableIdOrName
string
required
format
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.
breakdown
string
default:"totals"
How to break the traffic down. One of: totals, sessions, sources, geolocation, pages, landing-pages, utm-campaigns.
timePeriod
string
default:"total"
Aggregation period. One of: total, daily, weekly, monthly, summarize/daily, summarize/weekly, summarize/monthly.
start
string
required
Start date in YYYYMMDD format (inclusive).
end
string
required
End date in YYYYMMDD format (inclusive).
limit
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).
type
string
default:"site"
name
string
required
Internal page name.
templatePath
string
Path to the template/theme file the page uses.
slug
string
URL slug (path) for the page.
htmlTitle
string
SEO <title> for the page.
metaDescription
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).
type
string
default:"site"
objectId
string
required
The page id.
name
string
slug
string
htmlTitle
string
metaDescription
string
Returns: The updated draft page object.

publish_page

Push a page’s unpublished draft changes live to the public website. Requires confirm: true. Without it, returns a WritePreview dry-run describing exactly what would be published.
type
string
default:"site"
objectId
string
required
The page id.
confirm
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).
contentGroupId
string
required
The blog (content group) id this post belongs to.
name
string
required
Post title / internal name.
slug
string
URL slug for the post.
postBody
string
Post body HTML.
htmlTitle
string
SEO <title>.
metaDescription
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).
objectId
string
required
The blog post id.
name
string
slug
string
postBody
string
Post body HTML.
htmlTitle
string
metaDescription
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.
objectId
string
required
The blog post id.
confirm
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).
tableIdOrName
string
required
rowId
string
Existing row id to update. Omit to create a new row.
values
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.
tableIdOrName
string
required
confirm
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.
tableIdOrName
string
required
confirm
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

{
  "name": "hubspot__list_pages",
  "arguments": {
    "type": "site",
    "limit": 20
  }
}
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.