https://mcp.contenthero.ai and by the @contenthero/mcp npm package. A write tool changes your account; a read tool does not.
list_brand_kits
read
List the account’s brand kits (default first). Excludes archived kits unless archived=true; set favorited=true for only favorites. Call get_brand_kit for one kit’s full brand context (voice, visual identity, audience, sections, accounts, knowledge) to write on-brand content.
boolean
Only archived brand kits (default excludes archived).
string
The previous page’s cursor.
boolean
Only favorited brand kits.
integer
How many to return.
get_brand_kit
read
Get one brand kit. A brand kit is a set of named sections, each one Markdown document, plus its visual identity. THREE WAYS TO READ, chosen by what you pass: (1) detail:‘summary’ lists every section with its key, role, version, length and outline (its own headings) but NO body, the cheap first read, so start there and decide what the task needs; (2) any of keys, roles or tabs returns just those sections with their bodies (the filters combine with AND), and history:true adds each one’s earlier versions; (3) neither returns the whole kit: every section, visual identity (logos, colors, typography), linked brand and inspiration accounts, and a knowledge-base summary. A section’s key is stable and is how you address it; its role says what a starter section means and is the same in every kit, whatever the user named it. Every section carries a version: pass it back as expectedVersion when you change it with update_brand_kit.
string
required
The brand kit id from list_brand_kits.
`summary` \| `full`
‘summary’ lists every section without its body; ‘full’, the default, returns the whole kit. Cannot be combined with a filter.
boolean
With a filter, also return each section’s earlier versions, newest first.
string[]
Section keys, as the summary lists them.
string[]
Section roles, as the summary lists them.
string[]
‘overview’, ‘voice’ or ‘visual’.
create_brand_kit
write
Create a brand kit. THREE SOURCES, chosen by what you pass: (1) EMPTY, just a name, then fill it in with update_brand_kit; or FROM A SOCIAL PROFILE, pass its url in brandAccounts (your own) or inspirationAccounts (a creator you watch) with no name at all, and the kit is named after the handle and starts ingesting that account’s posts if it is YouTube or Instagram; (2) IMPORTED, pass websiteUrls (primary first) and/or the brand’s own YouTube or Instagram in brandAccounts, with extract:true, and ContentHero takes the colors, typography, logos and assets from the first website and analyzes every website and those accounts’ posts to write the kit’s empty sections, which is by far the fastest way to get a real kit; (3) A COPY, pass duplicateFrom with an existing kit id, which copies its sections and brand media (assets re-link rather than duplicate, so a copy costs no storage). Every new kit starts with its starter sections, empty. A brand with NO WEBSITE (so nothing to extract) is built by passing its content directly: sections, written by key into those starter sections, and logos, whose entries may name outputId to bring in a generation you just made rather than a url. With extract it RETURNS IMMEDIATELY, before the kit has any content: that empty kit is the handle, and the sections fill in over the next few minutes (longer while a newly linked account’s posts arrive), so poll analysisStatus and extractionStatus with get_brand_kit rather than assuming it failed. name is OPTIONAL when websiteUrls or a social profile url is given: it defaults to the first site’s hostname or the @handle, a placeholder extraction or you overwrite later. Brand kits are capped by plan, so this fails with a limit error near the cap, and a duplicate counts against it like any other kit. Requires the brand:write scope.
object[]
The kit’s brand assets, each { url | outputId, name? }.
value[]
The account owner’s OWN profiles. A tracked-account id, or { platform?, handleOrUrl } to ADD one and start ingesting it.
object[]
The kit’s palette, each { hex, name?, role? }.
string
Copy an existing brand kit id instead of starting empty.
boolean
Import the kit from websiteUrls and its own accounts in brandAccounts. Returns at once; poll analysisStatus and extractionStatus.
value[]
Competitor/creator profiles they watch. Same entry shape as brandAccounts.
object[]
The kit’s logos, each { url | outputId, name?, isPrimary?, isDisplay?, layout?, colorMode? }. Use outputId to bring in a generation.
string
The kit’s name. Optional when websiteUrls or a social profile is given.
object[]
Section content, each { key, body } to fill a starter section, or { sectionName, tab, body? } to add your own.
object
The kit’s fonts, { titleFont?, bodyFont? }.
string[]
The brand’s websites, primary first. The first gives the logos, colors and fonts; every one feeds the analysis.
update_brand_kit
write
Update a brand kit: its section content, colors and fonts, brand media, which kit is the DEFAULT, and which tracked accounts it is LINKED to. Only what you pass changes. Get the current kit first with get_brand_kit. SECTION CONTENT is written with sections, naming only the sections you change, all or nothing. An entry with a key edits that section: body (Markdown) replaces the whole section, revertTo restores an earlier version as a new one, sectionName renames it, width sets its card to full or half. An entry without a key adds a section of your own, with sectionName and tab. Pass each edited section’s version as expectedVersion: if any of them changed since you read it, NOTHING is written and the error lists each stale section’s current version and body, so re-read, reapply your change, and retry. To remove a section, archive it with archive (assetType brand_kit_section). A section entry’s afterId, beforeId or position moves that section within its tab; its neighbors are section ids. Requires the brand:write scope. afterId, beforeId or position moves the kit itself among the caller’s kits. extract:true RE-RUNS the import: the visuals from its first website and the analysis of its websites and own accounts, which writes only into sections still empty and returns immediately (poll analysisStatus and extractionStatus via get_brand_kit). logos/assets/brandAccounts/inspirationAccounts are DECLARATIVE: a patch REPLACES the whole list, so pass the full set and use [] to clear. THIS IS ALSO HOW YOU ADD NEW MEDIA TO A KIT: a logo or asset entry names either a url it already has, or outputId to bring in a generation that is not in the kit yet (‘<id>’, or ‘<id>-2’ for variation 2 of a batch), whose bytes get COPIED into the kit so trashing that generation later cannot empty it. To add a logo, read the kit, append one entry, and send the whole list back; sending an outputId twice adds it twice. brandAccounts are the account owner’s OWN profiles (performance), inspirationAccounts are competitors and creators they watch; they are separate lists because they mean opposite things. AN ENTRY IS EITHER a tracked-account id you already have, OR { platform?, handleOrUrl } to ADD a profile that is not tracked yet, which is what STARTS ingesting its posts (a full profile url carries its own platform, so platform is only needed for a bare handle). isDefault only accepts true (passing false would leave the account with no default at all, so to move the default, name the kit that should hold it).
string
required
The brand kit id.
string
Place it immediately after this item of the same list.
object[]
The kit’s brand assets, each { url | outputId, name? }. REPLACES the list; [] clears it.
string
Place it immediately before this item of the same list.
value[]
The account owner’s OWN profiles. Each entry is a tracked-account id, or { platform?, handleOrUrl } to ADD one and start ingesting it. REPLACES the list; [] clears it.
object[]
The kit’s palette, each { hex, name?, role? }. REPLACES the list; [] clears it.
boolean
Re-run this kit’s import from its websites and own accounts. Returns immediately; poll analysisStatus and extractionStatus.
value[]
Competitor/creator profiles they watch. Same entry shape as brandAccounts. REPLACES the list; [] clears it.
boolean
Make this the default kit, un-defaulting every other.
object[]
The kit’s logos, each { url | outputId, name?, isPrimary?, isDisplay?, layout?: horizontal|stacked|icon|wordmark, colorMode?: full_color|light|dark|grayscale }. REPLACES the list; [] clears it. Exactly one ends up primary (the kit’s cover); name none and the first wins.
string
Rename the kit. This is the label in the UI, not the business name.
`top` \| `bottom`
Place it at an end of the list instead of beside an item. Name neighbors or a position, not both.
object[]
Section writes: only the sections you change, all or nothing. { key, body, expectedVersion } edits one; { sectionName, tab, body? } adds one.
object or null
The kit’s fonts, { titleFont?, bodyFont? }. MERGES: a font left out keeps its value; null clears both.
string[]
The brand’s websites, primary first. REPLACES the list; [] clears it. Stored only: pass extract to import from them.
list_brand_knowledge
read
The complete, paginated index of a brand kit’s knowledge items (titles and metadata, no bodies). Use it to browse what exists, or to find an item’s id before get_brand_knowledge or remove_brand_knowledge. For relevance retrieval, use search_brand_knowledge instead. Requires the brand:read scope.
string
required
The brand kit id.
string
The previous page’s cursor.
integer
How many to return.
get_brand_knowledge
read
Get one knowledge item’s stored body by id (the capped anchor text; the full document is embedded for search, not stored verbatim). Use search_brand_knowledge for the deep content. Requires the brand:read scope.
string
required
The brand kit id.
string
required
The knowledge item id (from list_brand_knowledge or search_brand_knowledge).
add_brand_knowledge
write
Add an item to a brand kit’s knowledge base so it can be searched later. This is how the brand’s knowledge grows over time: capture a lesson learned, a brand decision, an asset description, an article, or a video. Source can be text (a note), url (a page to scrape), youtube (a video transcript), or file. For a file, pass either fileData (base64, best for small documents and images) or fileUrl (a hosted URL the server fetches, needed for large files and video/audio). Requires the brand:write scope. SPENDS CREDITS for images, video and audio (text, links, YouTube and documents are free): pass getCost to preview the price first, which runs nothing and charges nothing.
string
required
The brand kit id.
`text` \| `url` \| `youtube` \| `file`
required
How the content is provided.
string
For sourceType “file”: base64-encoded file bytes (small documents and images).
string
For sourceType “file”: the extension without a dot, e.g. “pdf”. Inferred from fileUrl when omitted.
string
For sourceType “file”: a hosted URL the server fetches (large files, video, audio).
boolean
Return the credit cost estimate instead of adding (video and audio are measured, not processed; nothing is charged).
string
For sourceType “text”: the note body.
string
Optional title (otherwise derived from the content).
string
For sourceType “url” or “youtube”: the link.
remove_brand_knowledge
write
Remove a knowledge item and its embedding chunks from a brand kit’s knowledge base. Requires the brand:write scope.
string
required
The brand kit id.
string
required
The knowledge item id to remove.
search_brand_knowledge
read
Semantic search over a brand kit’s knowledge base (everything the owner has uploaded: notes, docs, articles, video transcripts). Returns the most relevant passages, ranked. This is the deep-grounding read: use it to pull what the brand has said about a topic before drafting or deciding. Requires the brand:read scope.
string
required
The brand kit id (from list_brand_kits / get_brand_kit).
string
required
What to search for, in natural language.
integer
Max matches (default 8).
number
Minimum similarity 0-1 (default 0.45).