mxAURA™Developer Center
Webhooks & MCPMCP guide

MCP guide

https://mcp.mxaura.ai/mcp is a remote Model Context Protocol server. Point an MCP-capable assistant at it, sign in, and the assistant can read (and, with the right scopes, act on) your mxAURA account through its tools (the list below).

It speaks MCP protocol 2025-11-25 over streamable HTTP, authenticates with OAuth 2.1 (PKCE, dynamic client registration), and every call goes through the same public API as an API key would, with the same scopes, rate limits and tenant boundary.

Connect

Claude.ai (web / desktop app)

Settings → Connectors → Add custom connector → URL https://mcp.mxaura.ai/mcp → Connect. A browser window opens to auth.mxaura.ai; sign in to mxAURA, choose the access level, Authorize. Done. (In the app, Account → MCP has these steps for each client, with copy buttons, and lists every connected assistant with a Revoke button.)

Claude Code

text
claude mcp add --transport http mxaura https://mcp.mxaura.ai/mcp

then /mcp inside a session to authenticate.

Cursor / other MCP clients

Add a server of type "streamable HTTP" (or "remote") with the URL above. If the client cannot do OAuth itself, use the mcp-remote bridge:

json
{ "mcpServers": { "mxaura": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.mxaura.ai/mcp"] } } }

What happens underneath

  1. The client fetches https://mcp.mxaura.ai/.well-known/oauth-protected-resource and learns the authorization server is https://auth.mxaura.ai.
  2. It reads https://auth.mxaura.ai/.well-known/oauth-authorization-server, registers itself at /register (RFC 7591), and opens /authorize with a PKCE S256 challenge.
  3. You sign in to mxAURA (2FA if enabled) and approve the scopes shown.
  4. The client exchanges the code at /token and gets an access token (mxa_oat_…, 1 hour) and a refresh token (30 days).

No secrets are typed anywhere. To disconnect, revoke it in Account → API and MCP (effective within seconds: every token for that assistant is revoked), or remove the connector in the client.

Scopes and the support profile

A client that asks for no specific scopes gets the support profile:

text
components.read brandings.read pools.read campaigns.read flows.read
hubs.read articles.read identity.read dispatch.read

Every read, nothing that sends, publishes or spends. A client may request it by name (scope=support) or ask for specific scopes.

The person approving picks an access level on the consent screen, which replaces what the client asked for:

Level Scopes
Read only (default) the support profile above
Content support + hubs.write articles.write publishing.read publishing.write media.read media.write folders.read folders.write ai.write
Campaigns support + components.write pools.write campaigns.write flows.write brandings.write folders.read folders.write ai.write
Full * (everything, including enrollments.write)

Delete scopes are never part of a level; deleting needs an API key that has one.

Step-up, the account ceiling and revoking

  • Step-up (MCP 2025-11-25). tools/list returns every tool. When a call needs a scope the token does not hold, the server answers HTTP 403 with WWW-Authenticate: Bearer error="insufficient_scope", scope="<held scopes and the one needed>", resource_metadata="…". The client asks the person to approve that and retries; the consent screen pre-selects the smallest level that covers it. A connection can start Read only and grow only as far as the work needs.
  • Ceiling. The account owner sets the highest level any assistant may be given (Account → API and MCP). The consent screen offers nothing above it, the authorization server refuses to approve above it (403 above_ceiling), and the public API cuts every OAuth token down to it on every request, so lowering it takes effect at once for connections already approved; connections above the new ceiling are also revoked. Content and Campaigns are separate: a ceiling of Content does not allow Campaigns. No ceiling set means Full.
  • Revoke. Account → API and MCP lists the API keys (with their MCP use) and the connected assistants; revoking one stops it within about 15 seconds, on the API and the MCP alike.
  • API keys on the MCP (v3.1.947). A key works on the MCP as it does on the API. Used through the MCP it is held to the account ceiling as well as its own scopes; used on the API directly it keeps its own scopes. Its MCP use is shown in Access with the assistant's name (clientInfo.name from initialize, else the client's User-Agent).
  • tools/list lists every tool, whatever the credential (the 2026 spec). A call the credential cannot make answers 403 insufficient_scope naming the permission it needs.

The tools

Tool Scope What it does
get_me any Who the token is: account, scopes
list_components, get_component components.read Messages, headers, summaries, signatures, senders, templates
list_pools, get_pool, get_pool_messages pools.read Pools and the ordered messages composed into them
compose_message pools.write Compose a message into a pool
list_campaigns, get_campaign campaigns.read Campaigns
simulate_campaign campaigns.read The full journey a contact would receive. Read-only, and the same engine dispatch uses. With contact_id, rows the contact's properties fail are skipped
preview_next_message campaigns.read The exact message a contact would get next: the production cascade with their real properties, lists and progress; the row, pool, message, sender, subject and blocks; every row with whether it matched and why. Writes nothing, so the next run gives the same answer. Field content and HTML come back as character counts (the API returns them in full)
dispatch_next_message dispatch.write A real dispatch for one contact: advances their journey and counts in the dispatch log and every stat. write_properties: true also writes the message to the HubSpot contact. Not a test
review_messages ai.write The Composer's three critics and nine lines on messages that already exist (up to 10 in one pool, read as they would send; the ones either side read as context, never scored) or on raw copy. Writes nothing to the messages; spends model budget; leaves a review stamp. Where a critic quoted the message, the words come back in quoted_tenant_text between « and », capped at 120 characters: the one place this server returns a message's own words. The message text itself comes back as character counts
list_campaign_pools campaigns.read A campaign's pools. Each row is an assignment with its own filter, sender, Brand Kit and blocks; a pool can appear twice, once per audience
attach_pool campaigns.write Attach a pool to a campaign. allow_duplicate: true attaches it again for another audience
get_assignment_blocks campaigns.read The Blocks chosen on a campaign pool row, per block definition
bind_assignment_blocks campaigns.write Choose the Block for each block definition on a campaign pool row (source: set, set_id) and how its entries go out: sequential (next entry each send to a contact), fixed (block_id: one entry) or rotational (next entry each time the same message repeats). Definitions left out keep theirs; source: inherit clears one
pin_block campaigns.write Advanced: pin one entry to one message of a campaign pool row
get_row_variants campaigns.read A row's block variants in the order they are checked, each with its filter and Blocks, the row's own filter, and warnings
radar_reads, radar_read campaigns.read Radar's reads as data, no model and no spend: list them with their inputs, then run one (performance, two periods compared, links clicked, what changed, results, hub traffic, DMARC sources, domain health, reputation, live HubSpot and Google reads). Your assistant does the reasoning
radar_ask ai.write Radar answers in plain words with every number cited to a read (T1, T2...). The reads come back as tool and arguments; radar_read gets their data. Uses the AI allowance
radar_review, radar_findings campaigns.read The week in review, and the findings from the last check run
radar_run_findings campaigns.read Run the checks now (rules, no model)
radar_dismiss_finding campaigns.write Quiet a finding for 30 days
radar_threads campaigns.read Ask threads, or one thread
radar_agency_review, radar_agency_learning campaigns.read Agency accounts: clients this week; what works across clients
architect_plans, architect_plan, architect_stage, architect_estimate campaigns.read The Architect's campaign plans: list them, open one (brief, every stage, the review, the build), read one stage in full with its reads, read the bill Go would spend
architect_start, architect_edit, architect_delete campaigns.write Start a plan; edit, confirm or unconfirm a stage (the form is the truth; confirming suggestion, templates or content runs the review, a refusal comes back with the lines); delete a plan
architect_chat, architect_run ai.write Talk to the Architect to fill the brief; run a stage (research, suggestion, templates, content, setup, automation). Uses the AI allowance
architect_build ai.write + campaigns.write Go: the review, then the campaign, rows, pools, Blocks, bindings, variants, the Composer per pool and the drafts, in mxAURA. Nothing enabled or published; calling it again fills gaps
architect_setup_apply, architect_automation_apply, architect_register_library campaigns.write The portal buttons: create the ticked properties and lists (create only); create the workflow, off, or the flow and form; create and register the library designs
set_row_variants campaigns.write Replace a row's variants (first match wins, no match gets the default). Refused when a variant can never apply: contradicts the row's filter, sits below a variant with no filter, or repeats a filter above it
list_flows, get_flow flows.read Flows with enrollment counts
enroll_contact enrollments.write Enroll a HubSpot contact. Sends real email, capped per hour
get_dispatch_log dispatch.read What was sent, to whom, with what result
list_hubs, get_hub, get_hub_stats hubs.read Hubs, their settings and embed details, traffic
list_articles, get_article articles.read Articles: metadata, status, placements, SEO, stats
get_identity identity.read Positioning (frameworks)
list_brandings brandings.read Brand Kits

Each tool's description and input schema are served by tools/list; the assistant reads them directly.

What the model can and cannot see

Bodies are never returned to the model. Message HTML, article markdown, layout templates, personal messages come back as their length (html_chars: 2140) instead of their text. Content a user wrote is untrusted input to a model: a hub article must never be able to instruct an assistant. A workflow that truly needs a body fetches it over the REST API.

Everything else comes back as structured JSON. Names, ids, statuses, positions, stats, reasons.

For the same reason there are no MCP tools for the evidence library, competitors or the market brief: what they hold is text read from other people's websites. Use the REST API for those (Resources). Radar's market read is left out for the same reason. DMARC and the Google connection are app-only; Radar reads them.

Limits

MCP calls are API calls. The token's burst (25 / 10 s), per-minute (120) and account (600 / min) limits apply, and tools that spend the AI allowance share the account's 30 a minute with the app. The assistant is told when it hits one (rate_limited (HTTP 429): ...) so it can back off rather than crash the session. A refusal from the AI allowance reads budget_exhausted (HTTP 402): ... [reason: ...]. Tool results are truncated at 100 KB.

Typical questions it can answer

  • "Why didn't Darren get Tuesday's email?" → get_dispatch_log, filter by contact; the entry says blocked / no_sender or filtered / ….
  • "What would this contact receive from the Q4 campaign?" → simulate_campaign.
  • "Send partners the partner closes from the same intro pool." → attach_pool (allow_duplicate), then bind_assignment_blocks on each row.
  • "Make a Partner Summary for MXA 2.0 and use it on the partner row." → update_manifest (Summary section reusable: true, if it is not a block definition yet), list_block_kinds, compose_block_set, compose_block (one per entry, set_id), then bind_assignment_blocks with source: set, the set_id and a mode (sequential, fixed with block_id, or rotational).
  • "Which hub is the pricing article in, and is it live?" → get_article.
  • "How much traffic did the Insights hub get this month?" → get_hub_stats.
  • "What's our positioning?" → get_identity.

Troubleshooting

Symptom Cause Fix
Client says "unauthorized" after connecting Access token expired and the client did not refresh Reconnect; most clients refresh silently
A tool is missing The token lacks its scope Reconnect and approve the scope, or accept that support is read-only
enroll_contact returns enrollment_cap Hourly cap reached Wait for Retry-After
A tool returns budget_exhausted (402) The AI allowance refused; [reason: ...] says why (exhausted, would_exceed, disabled, new_account, email_unverified) Wait for the month, ask support to raise it, connect HubSpot, or confirm the email in the app
"server did not respond" Network, or the client is using a stdio transport Use a streamable-HTTP transport or mcp-remote

https://mcp.mxaura.ai/health reports the server version (1.7.0) and the protocol version (2025-11-25) without authentication.

Hub tools (v3.1.720)

The idea-to-live path: a piece is composed in the library, drafted by AI, placed in a hub, published. Every call goes through the public API with the caller's own credential, so the hub sub-family scopes (articles, publishing, ai, hubs) are enforced again there.

Tool Scope Does
ask_help ai.write The documentation as a tool: a grounded answer with the help topics used and the app page to open. Answers questions about mxAURA only; anything else is refused. Metered against the account's AI allowance
list_content_types hubs.read Article templates and their fields. Read one before composing so the fields you pass exist
list_authors articles.read Bylines, with ids for author_id
list_hub_articles hubs.read What one hub's gallery shows: each placement's state, featured flag and position
compose_article articles.write Create an article from a content type: title, optional slug and author, data by field key. Importing: display_date, created_at, published_at carry the original dates (the last two set the record's own stamps; update_article takes them too)
update_article articles.write Change fields; only the keys passed change. Importing: created_at and published_at back-date the record's stamps (created_at never clears, published_at: null clears), display_date sets the date readers see. None of them publishes anything
bulk_ai_assist ai.write Fill what many articles are missing in one background job: seo, social, answer. Up to 300 articles, only what is empty, one run per account at a time. Poll get_ai_job; the label is the progress
draft_article_with_ai ai.write Start an AI draft (subtitle, body, title, reviewed) from a brief and optional source. A job: poll get_ai_job until done. Spends the AI budget
get_ai_job ai.read One job: status and result
generate_article_seo ai.write SEO title and description from the body
place_article_in_hub publishing.write Put an article into a hub's gallery as a draft; it gets its URL on every site the hub is on
publish_article publishing.write Live now, or scheduled when publish_at is given. Public: the assistant confirms first
unpublish_article publishing.write Off the gallery; the article stays in the library
generate_social_posts ai.write Queue the long and short social posts from the article (review slot by default)
get_article_faqs, save_article_faqs articles.read / articles.write Read the answer layer; save it and approve FAQs (approved: true). Applied AI FAQs arrive unapproved
list_guardrails, check_guardrails identity.read An identity's words and phrases, and the same check the generators run, for any text
set_guardrails identity.write Set the words (the AI does not use them) and the phrases (instructions; a phrase that names a word wins). Each list sent replaces that list
self_test_guardrails identity.read Every rule against its own examples
suggest_guardrails ai.write AI Assist suggests words and phrases to avoid; saves nothing
fix_guardrail_conflicts ai.write AI Assist repairs text that breaks the guardrails, changing only the sentence with the conflict; returns before and after; saves nothing
compose_guardrail, import_guardrails identity.write Add or import (load format) rules of a specific kind. Saving is the approval
generate_answer_layer ai.write Queue the lead answer and FAQs, grounded in the article (review slot; applied FAQs arrive unapproved)
delete_articles articles.delete Permanent; refused while an article is in a hub. Needs an API key with that scope
retire_article, restore_article publishing.write Reversible take-down, and bring back with the same id and slug
bulk_hub_placement publishing.write One action for up to 200 articles in a hub: publish, schedule (publish_at), unpublish or remove. Public: the assistant confirms first
apply_ai_draft articles.write Copy the AI draft from the review slot into the live article (all, or the fields named)
discard_ai_draft articles.write Throw away the AI draft; the live article is untouched

Typical run: list_content_types > compose_article (send the slug to keep an address exactly; for an import, created_at, published_at and display_date for its original dates) > place_article_in_hub (status: unpublished) > draft_article_with_ai or your own body > generate_article_seo (fields: ["tags"], write: "live" for tags only) > generate_social_posts and generate_answer_layer > get_ai_job > get_article (read data.ai_review) > apply_ai_draft > get_article_faqs / save_article_faqs (approve) > publish_article or bulk_hub_placement. Everything runs on mxAURA's AI with your identity and prompt packs; the packs are never returned. A draft never overwrites live content: it waits in the review slot until applied.

Many at once: list_hub_articles > pick the ids > bulk_hub_placement with action: publish (or schedule with publish_at, unpublish, remove). The response says how many were done and lists any that failed, with the reason.

Composition tools (v3.1.383)

Tool Scope What it does
import_template, get_manifest, update_manifest components Import a HubSpot email; read and correct its field manifest
list_sequence, compose_message, clone_message, reorder_messages, remove_message_from_pool pools The ordered messages in a pool
get_bindings, bind_fields components The typed values of each field of a message
list_block_kinds, list_block_sets, get_block_set, compose_block_set, update_block_set, clone_block_set, delete_block_sets, list_blocks, compose_block, update_block, delete_blocks, set_message_slots, block_clone_targets, clone_block components Block definitions (template sections), Blocks (named sets of entries on a definition: Client Summary, Partner Summary) and their entries, which block a message uses, and cloning a block to another template whose section matches exactly
generate_rows, generate_variants ai AI drafts; nothing is saved until you compose it
preview_message campaigns.read Signed link rendering the message for a contact

Vocabulary: tools compose and bind; nothing is "created". A typical run: import_template → update_manifest (fix any unknown types) → compose_message → bind_fields until summary.ready → preview_message.