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. Additional HubSpot portals get their own endpoints; see Multiple portals.
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 only available on those plans, and Consile rejects a connection where HubSpot did not grant all three. 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 dry-run preview and calls no HubSpot API.
This is a safety default built into the connector’s tools, not an unbreakable platform gate. See Write tools & guardrails for the full cross-connector policy.

Preview shape

When a write action requires confirmation but confirm is omitted or false, the tool returns this shape instead of calling HubSpot:
summary describes the change, and request shows the write that would be sent to HubSpot. To apply, re-run the identical call with confirm: true added. The preview is a normal successful tool result (isError is not set).

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.

Tool annotations

Every tool in tools/list carries MCP annotations: openWorldHint: true marks the three tools that change the public website. Draft writes are marked destructive because they are writes; they do not touch the live site. Some clients ask the user to approve each call of a tool marked destructiveHint: true; that prompt comes from the client and is separate from the confirm guardrail.

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
If HubSpot does not grant all three, Consile rejects the connection and the customer sees a failed-connection page in the portal. If a granted portal’s plan does not cover a surface (for example traffic analytics without Marketing Hub Professional/Enterprise), HubSpot answers those calls with 403, which reaches the client as a generic tool error (see Errors). The customer needs to upgrade their HubSpot subscription to use that surface.

Authentication

HubSpot CMS uses HubSpot’s OAuth login. The customer authorizes Consile’s HubSpot app (shown in HubSpot as Consile HubSpot CMS) for one HubSpot portal from the Consile portal. Consile stores the access encrypted per customer and renews it automatically. The MCP client never sees the HubSpot 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. Disconnecting in the Consile portal (Afbryd connector / Disconnect connector) deletes the stored access immediately; on a multi-portal subscription it applies to portal 1. To remove the access at HubSpot as well, the customer uninstalls the app Consile HubSpot CMS in HubSpot’s connected apps settings. To reconnect after an uninstall or revoke, the customer disconnects and connects again in the portal, or calls connect_account. For an extra portal, call connect_account on that portal’s endpoint (for example /hubspot-2/mcp).

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.

Responses

Tools return HubSpot’s own response bodies unchanged (export_hubdb_table wraps its export, see below). List tools return HubSpot’s { results, paging } envelope. The field lists below are examples, not a fixed schema. When HubSpot answers with an empty body, the tool result is null.

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 }; page objects include fields such as 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 }; revisions include fields such as 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 }; post objects include fields such as 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 }; tags include fields such as 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 }; authors include fields such as 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 }; tables include fields such as 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. Draft rows are not included.
string
required
integer
default:"20"
string
Returns: { results, paging }; rows include id and 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: The published row, including id and values.

export_hubdb_table

Export a HubDB table’s data. Returns { tableIdOrName, format, data }, where data is the export as a text string. Use CSV (the default). EXCEL output is binary and is not usable when returned as text.
string
required
string
default:"CSV"
Export format: "CSV" or "EXCEL". Only "CSV" produces usable output.
Returns: { tableIdOrName, format, data } where data is the 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 and the business-intelligence scope.
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 preview and calls no HubSpot API. Draft-only operations (create, edit) run freely. See Write tools & guardrails.
Not supported by this connector: editing page body content (modules, widgets), setting blog tags, author or featured image, creating HubDB tables or columns, and deleting or archiving pages, posts or rows.

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_page only pushes changes on an already-published page, so the first publish of a new page happens in HubSpot. 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. Page body content (modules, widgets) cannot be edited through this connector. 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 dry-run preview 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 preview.
Returns: HubSpot’s response on success, passed through unchanged (null if HubSpot returns an empty body); a preview 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 dry-run preview.
string
required
The blog post id.
boolean
Set true to apply. Omit or false to receive a preview.
Returns: HubSpot’s response on success, passed through unchanged (null if HubSpot returns an empty body); a preview 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). There is no tool for reading a table’s draft rows: list_hubdb_rows and get_hubdb_row read the published table, so a row written here is not visible to them until publish_hubdb_table has run. The publish_hubdb_table preview names the table but does not list the pending row changes, so check the draft in HubSpot before confirming. 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 dry-run preview.
string
required
boolean
Set true to apply. Omit or false to receive a preview.
Returns: HubSpot’s response on success, passed through unchanged (null if HubSpot returns an empty body); a preview 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 dry-run preview.
string
required
boolean
Set true to apply. Omit or false to receive a preview.
Returns: HubSpot’s response on success, passed through unchanged (null if HubSpot returns an empty body); a preview 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.

Errors

If the stored HubSpot access has expired or been revoked (for example because the HubSpot app was uninstalled), tools/call returns a result with isError: true and one of these messages:
  • HubSpot access token has expired; reconnect required
  • HubSpot rejected the access token (401); reconnect required
The customer then reconnects (see Authentication). HubSpot’s own error details are not passed through to the client. If a HubSpot call fails for another reason, tools/call returns a result with isError: true and the text Tool invocation failed. The usual causes are:
  • the connected portal’s plan does not include the surface (CMS/Content Hub or Marketing Hub Professional/Enterprise; traffic analytics needs Marketing Hub Professional/Enterprise),
  • an argument that does not match the schema (for example a date not in YYYYMMDD).
If the connector is bought but not connected, the call instead returns a link to connect it. A signed-in user who has not been assigned a seat on HubSpot CMS still sees the tools in tools/list, but every call returns a message asking them to have an administrator assign them under Team in the portal. See Errors & limits. HubSpot limits public apps to about 110 requests per 10 seconds per connected HubSpot account. A call that exceeds HubSpot’s rate limit can fail with Tool invocation failed.; wait a few seconds and try again.

Multiple portals

This connector is multi-instance. A customer can add more HubSpot portals in the Consile portal (Tilføj portal / Add portal, administrators only). Portal 1 is served at https://mcp.consile.ai/hubspot/mcp with tools named hubspot__<tool>. Portal N (N ≥ 2) is served at https://mcp.consile.ai/hubspot-N/mcp with tools named hubspot-N__<tool> and the server name Consile · HubSpot CMS #N. Each portal is connected and authorized separately. One user seat covers every portal of the connector. After an extra portal is removed, the endpoint URL of a remaining portal can change. Check the connector configuration in your MCP clients after a removal. Every endpoint’s tools/list also includes the unprefixed system tool connect_account, which takes no arguments and returns a link (valid for 10 minutes) for connecting, or reconnecting, the HubSpot account behind that endpoint.